VPoTの岩本 (iwamot) です。
この記事では、Terraformモジュール構成のベストプラクティスをご紹介します。Terraformドキュメントに書かれているものですが、従わずに時間を溶かした失敗談をまじえてお伝えすることで、同じ轍を踏む方が減ることを願っています。
取り上げるのは下記のベストプラクティスです。
- Module Composition(フラットなモジュールツリー)
- Dependency Inversion(依存性の逆転)
Module Composition(フラットなモジュールツリー)
Module Compositionは、モジュールをフラットに並べられるよう構成すべし、という話です。Terraformドキュメントでは下記の例が挙げられています。
module "network" { source = "./modules/aws-network" base_cidr_block = "10.0.0.0/8" } module "consul_cluster" { source = "./modules/aws-consul-cluster" vpc_id = module.network.vpc_id subnet_ids = module.network.subnet_ids }
これに従わず、aws-consul-clusterモジュール内でnetworkモジュールを参照し、ルートモジュールで下記のように記述する設計も可能です。
module "consul_cluster" { source = "./modules/aws-consul-cluster" base_cidr_block = "10.0.0.0/8" }
ただ、後者の設計だと、たとえばnetworkモジュールの変数が増えたときにconsul_clusterモジュールにも追加しなければならず、保守の手間がかかります。モジュールの結合度が強すぎるわけです。
ぼくが設計を失敗したのは、モジュールAとモジュールBの共通部分をモジュールCに切り出し、内部的に参照させたケースでした。開発途中で変数の引き回しが面倒になり、結果的にModule Compositionに従うこととなりました。最初から従っていれば、無駄な工数が省けたはずです。
Dependency Inversion(依存性の逆転)
モジュールがフラットになれば、下記のような記述も可能になります。
data "aws_vpc" "main" { tags = { Environment = "production" } } data "aws_subnet_ids" "main" { vpc_id = data.aws_vpc.main.id } module "consul_cluster" { source = "./modules/aws-consul-cluster" vpc_id = data.aws_vpc.main.id subnet_ids = data.aws_subnet_ids.main.ids }
つまり、networkモジュールを使わずとも、consul_clusterモジュールが使えるわけです。このように、モジュールで必要となるリソースをルートモジュールから渡す設計が「Dependency Inversion」です。
ぼくはこの点も考慮が不足していて、前述のモジュールCにおいて、モジュールA・Bのリソース命名規則に依存した処理を記述してしまいました。Module Compositionに従って構成をフラットに変えたものの、結合度の強さは変わらぬままでした。命名規則が変わるたび、モジュールCにも手を入れなければなりません。
そこで、モジュールCで必要となるリソースを、モジュールA・Bから渡してもらう設計に見直しました。Terraformドキュメントにあるように、object型の変数を定義すると、リソースが渡しやすくなります。
variable "ami" { type = object({ # Declare an object using only the subset of attributes the module # needs. Terraform will allow any object that has at least these # attributes. id = string architecture = string }) }
アンチパターン:リソースがなければ作成する
また、Terraformドキュメントでは「リソースがなければ作成する」(Conditional Creation of Objects)設計も避けるよう勧めています。もし不足しているリソースがあれば、ルートモジュール側で作成するほうが、あとで読む人が理解しやすいためです。
ぼくはこの点も考慮不足で、条件式の結果を count
に代入し、リソースを作ったり作らなかったりする記述をしていました。ただ、当該リソースを参照するのに条件式をいろんな場所で書かなければならず、不便でやめました。こちらも最初から従っていれば、無駄な工数が省けたはずです。
まとめ
以上、Terraformモジュール構成のベストプラクティスを、ぼくの失敗談をまじえてお伝えしました。
結論として、下記の点に気をつけるとよいでしょう。
- 他のモジュールに依存したモジュールは作らない
- 条件式に依存したリソース作成はなるべく避ける(作成後に参照しないならOK)
ぼくも、またTerraformモジュールを作る機会があれば気をつけます。