コピペしたコードをモジュールに畳む
一言でいうと
同じコードを3回コピーしたなら、それはモジュールにすべきだというサインです。ただし、早すぎる段階でモジュールにすると、コピペよりも悪くなります。
なぜ必要なのか
環境が3つなら、ディレクトリも3つになります。
dev/main.tf (300줄)
stage/main.tf (300줄, dev 와 거의 같음)
prod/main.tf (300줄, 인스턴스 크기만 다름)
セキュリティグループのルールを1つ直すには、3か所を直す必要があり、必ず1か所を直し忘れます。そして、その抜けた1か所が、たいていprodです。
モジュールの役割
モジュールは、入力を受け取ってリソースのまとまりを作る関数です。
module "web" {
source = "../modules/web-service"
name = "api"
instance_size = "large" # 환경마다 다른 것만 인자로
replicas = 6
}
3つの環境が同じモジュールを使い、異なる値だけを引数として渡します。ルールを直すときは、モジュール1か所だけを直します。
良いモジュールの条件
1. 境界が自然である 「ウェブサービス1つに必要なものすべて」は、よい境界です。「自社のすべてのインフラ」は、悪い境界です。モジュールは再利用の単位であって、梱包の単位ではありません。
2. 入力が少ない 引数が30個あるモジュールは、モジュールではなく設定ファイルです。引数が増えていくのは、境界が間違っているというサインです。
3. 出力が明確である 他のモジュールが参照する値(ID、エンドポイント)を、出力として公開します。内部の実装を外から知る必要があるなら、カプセル化に失敗しています。
4. バージョンがある モジュールを直すと、それを使うすべての場所が影響を受けます。バージョンを付けて、環境ごとに異なる時点のモジュールを使えるようにします。devで先に新しいバージョンを試し、prodはあとで上げます。
早すぎる抽象化
3か所で使うコードをモジュールにしたのに、すぐに4つ目の使用先が現れて、「この場合だけ違う」が始まります。引数が1つずつ増え、条件文が入り、結局、誰も理解できないモジュールになります。
経験則は次のとおりです。
- 2回の繰り返し: そのままにします。まだパターンがわかりません。
- 3回の繰り返し: 共通点がはっきりしていれば、モジュールにします。
- 条件が増える: モジュールを分割するか、その使用先ではモジュールを使わないようにします。
重複は目に見えて直せますが、誤った抽象化は目に見えず、直すのも難しいです。自信がなければ、重複したままにしておくほうがよいです。
現場での姿
- モジュールの引数が40個 → 境界が間違っています。分割する必要があります。
- モジュールの中に
count = var.is_prod ? 3 : 1のような環境の分岐がたまっている → 引数として受け取るべき値を、モジュールが自分で判断しています。 - モジュールを直したら、無関係な環境が壊れた → バージョンの固定がありません。
次のラボですること
続く3つのラボで、ドリフトとモジュールをOpenTofuで直接扱います。手で直したファイルをplan -refresh-onlyで読んで、吸収・元に戻す・除外を選び、2つの状態が1つのリソースを巡って争う状況をremovedブロックで片付けたあと、importで既存の値を取り込み、コピペした2つの環境を、movedで置き換えなしにモジュール呼び出しに変えて、gitタグでバージョンを固定します。最後のクイズでは、安定した境界とバージョンの固定、呼び出し側の責任を判断します。