Terraformはこうやって使うといいよ集
Terraformを用いてインフラを構築する上で「こういうふうに書くと後で困る」みたいなアンチパターンがいくつか見えてきたので、事前にこういう考えて作っておくといいよを共有する。
リソース名は考えてつけること
terraformはリソース名を変えるとreplace(destroy&create)が走る[1]ので、あとからリファクタリングでも変更しにくい箇所。ちょっと考えれば適切な名前が浮かぶと思うので、リソース名には気を使いたい。
意外とよくあるのがこれ。
不正解の例のようにリソース種別をリソース名に含めてしまうパターン。
Planした時などにaws_instance.instance_web
と出力されて冗長である。
# 不正解
resource "aws_instance" "instance_web" {
...
}
# 正解
resource "aws_instance" "web" {
...
}
配列をもとにしてリソースを作成することはしない
配列でできることは基本的にmapでも実現できるはずなので、配列を使わずmapを使うこと。
設定値として配列を用いる場合は問題にならないが、配列を元にリソースを作ることは止めるべき。
例えば、["web1","web2","web3"]
という配列を元にEC2インスタンスを作るロジックを書いたとする。
web2が不要になって配列から削除すると、web2が消えるだけでなく、web3がreplace(destroy&create)される。
web3に付与された添字(index)が2から1へ変わることからリソース名変更に該当するため。
locals変数(Local Values)を積極的に利用すること
なんでもかんでもvariableで宣言しないこと。
locals変数のほうが他の変数と組み合わせて組み立てたりできてできることが多い。
特に、環境名を元にリソース名を生成する所などではlocals変数を利用すること。
applyコマンドを実行する時やtfversで外部からdefault値を変更したい用途が考えられる変数にのみvariableを用いること。
Local Values の使い方については次の記事が詳しい。
2018年の記事だが2022年の今でもモダンである。
環境切り替えのためにtfversを使わないこと
tfversを使っている理由の多くに「同じコードを用いて検証と本番をデプロイするため」というのがある。
検証と本番が同一構成であることを担保したい場合はモジュールを用いて実現してほしい。
下記のようなディレクトリ構成で、prdおよびdevのmain.tfからmodules/platformを呼び出せば構成は同じであることが担保できる。
root/
├── env/
│ ├── prd/main.tf
│ └── dev/main.tf
└── modules/
├── platform/
└── functionA/
tfvers の用途
tfversはパスワード、秘密鍵などコードの中に埋め込みにくい変数の格納に用いること。
gitignoreにも記載されている。
#すべての.tfvarsファイルを除外します。これらのファイルには、次のような機密データが含まれている可能性があります。
#パスワード、秘密鍵、およびその他の秘密。これらはバージョンの一部であってはなりません
#潜在的に機密性が高く、対象となるデータポイントであるため、制御する
#環境に応じて変更します。
* .tfvars
override.tf についても知っておく
別のデプロイ先にprefixだけ変えて個人的にデプロイ試したいみたいな用途の時にはoverride.tfを使う。
#オーバーライドファイルは通常、リソースをローカルでオーバーライドするために使用されるため、無視します。
#チェックインされていません
override.tf
モジュールは積極的に使っていくこと
terraformのモジュールは大きな規模や複雑な構成になった場合に使うものだと理解している人も多いと思うけど、モジュールはロジックにもデプロイにも柔軟性をもたらすので、小さな規模でも積極的に用いたほうが良い。
terraformはstateの管理が必要だし実行する場合に環境セットアップも必要になる。それでも、モジュールを用いることでロジックを組むことができるメリットを享受できるため、これはデメリットを上回ると考えIaCツールとしてterraformを採用している。
モジュールを使わないのであれば構成そのままコードに落としただけなので、terraformじゃなくてCloudFormation使っといたら?と思う。
モジュールは機能ごとのまとまりにすること
モジュールを使ってくださいとお願いすると、iamとかvpcとかそういう単位でモジュールを作ってしまうことがおおい。
それだとただのフォルダ分割にしかなっていないし、モジュール同士が疎結合にならず、1個の機能をデプロイしたいだけなのに、結局全部のモジュールをデプロイする必要があるみたいな状況になってしまう。
モジュールは機能毎に作成するのが良い。
# 正解
root/
├── env/
│ ├── prd/main.tf
│ └── dev/main.tf
└── modules/
├── platform/vpc.tf
├── functionA/ec2.tf,iam.tf
└── functionB/lambda.tf
# 不正解
root/
├── env/
│ ├── prd/main.tf
│ └── dev/main.tf
└── modules/
├── vpc/vpc.tf
└── ec2/ec2.tf
tfstateは適切な単位で分割すること
terraformを用いたインフラ構築を何度か経験するとわかってくるが、AWSでもAzureでもPlanするだけですごく時間のかかるリソースが存在する。
iamとかは一瞬で応答が変えてくるけど、RDSとかEIPは差分確認だけですごく待たされる等。
最低限、ネットワークや共通IAMをまとめたplatform的なまとまりでひとつのtfstate、その上で稼働するリソースをまとめてひとつのtfstateとすることをおすすめする。
参考リンク
-
v1.1 からmovedブロックが導入されてreplaceは不要になりました ↩︎
Discussion