記事

チームで使うTerraform:ステート分割、影響範囲、レビュー済みのプラン

Terraformのステートを分割し、CIでplanを実行し、レビューしたものだけを適用して、インフラ変更をリスクではなく日常業務にする方法。

例は汎用的なもので、説明を目的としています。どの顧客環境から取ったものでもありません。しきい値や設定はご自身のシステムに合わせて調整してください。

本当の境界はステートファイル

terraform applyは、対象とするステート内のあらゆるものを変更できます。つまりリスクの単位はモジュールではなくステートです。環境全体で1つのステートしかないと、DNSレコードのタイプミスも本番データベースの変更も同じplanに乗り、そのplanの計算には何分もかかります。

ライフサイクルと影響範囲で分割する

一緒に変わり、一緒に壊れるリソースをまとめます。よくある構成では、環境ごとに次のように分けます:

  • 基盤:アカウント、ネットワーク、DNSゾーン——めったに変更しない。
  • データ:データベース、バケット、キュー——慎重に変更し、削除から保護する。
  • コンピュートとサービス:クラスタ、関数、ロードバランサー——頻繁に変更する。

各レイヤーは下位レイヤーの出力を読み取るだけで、そのリソースを管理することはありません。環境間では同じモジュールを共有しつつ、ステートと認証情報は分けます。そうすればstagingでのapplyが本番に触れることはありません。

リモートステート、ロック、バージョン

  • ステートはロックとバージョニングを有効にしたリモートバックエンドに置きます。Gitにも、ノートPCにも置きません。
  • 依存関係のロックファイル(.terraform.lock.hcl)をコミットし、モジュールのバージョンを固定します。今日のplanと来月のplanが同じコードを実行するようにするためです。
  • ステートを持つリソースは、prevent_destroyとプロバイダー側の削除保護で守ります。

プルリクエストごとにplan、レビューしたものをapply

変更のたびにCIでterraform planを実行し、結果をプルリクエストに投稿します。承認後は再度planせずに保存済みのプランを適用し、レビュアーが読んだ内容そのものを本番に反映します。途中でステートが変わっていれば、Terraformは古いプランを拒否します。パイプラインの認証には、シークレットとして保存した長期キーではなく、OIDCフェデレーションによる短期の認証情報を使います。

terraform init -input=false
terraform plan -input=false -lock-timeout=5m -out=tfplan
terraform show -no-color tfplan > plan.txt   # attach to the pull request

# after approval, applying the same saved plan:
terraform apply -input=false tfplan

定期的にドリフトを検出する

コンソールでの変更は、たいていインシデント中に、今でも起こります。terraform plan -detailed-exitcodeを定期実行するジョブがそれを見つけます。終了コード0は変更なし、2は実際のインフラがコードと異なる、1はエラーを意味します。2はチケットにし、その変更をコードに取り込むか元に戻します。

applyの前にポリシー

「公開バケット禁止」「すべてのリソースにオーナータグ」といったルールは、長い1日の終わりのレビュアーより機械のほうが確実にチェックできます。Conftest経由のOpen Policy Agentのようなポリシー・アズ・コードのツールは、CIでplanのJSON形式(terraform show -json)を評価し、マージをブロックできます。

お問い合わせ

現在の構成についてセカンドオピニオンが必要ですか?

システムの概要を短くお送りください。シニアエンジニアが1営業日以内に書面でお返しします。

インフラについて相談する