Terraform state の構成の提案

Toshiki Shimomura

2022.8.16

動機

単一の Terraform state でリソースを構築していると、徐々にリソース数が増加し、コードの見通しが悪くなったり plan 時間が長くなったりといった問題が発生します。

  • 単一 state で運用していたが、肥大化してきた。
    ・リソース量が多くて見通しが悪い。
    ・plan にかなり時間がかかる。
    ・多重度の違うリソース(例えば EKS クラスタ)をコピペして複製したくない。

そこで、モノリスに伴う問題を解消すべく state を適切な単位に分割する方法を考えます。

Terraform 構成に関する事例

Terraform state の構成は参考になる先行事例がいくつか存在します。

「それ、どこに出しても恥ずかしくないTerraformコードになってるか?」

「それ、どこに出しても恥ずかしくないTerraformコードになってるか?」 / Terraform AWS Best Practices

まとまりごとにモジュール化し、各環境でインポートして単一 state を構成する考え方です。

  • 結局はモノリスなので、plan 時間の増大は避けられない。
  • リソースにモジュールを使っていると、モジュール化することでネストが深くなる。

以下のようなディレクトリ構成で、環境ごとに state を作り、共通のモジュールを参照します。

.
├── envs
│     ├── dev
│     │     ├── backend.tf
│     │     ├── local.tf
│     │     ├── main.tf
│     │     ├── provider.tf
│     │     └── variables.tf
│     └── prd
│            └── (省略)
└── modules
       └── sample-app
              ├── main.tf
              ├── outputs.tf
              └── variable.tf

『実践 Terraform』

実践Terraform AWSにおけるシステム設計とベストプラクティス

『実践 Terraform』の第21章に構造化について書かれているが、具体的な実装例は書かれていません。
これを実現したいと思います。

  • コンポーネント分割
    ・安定度
    ・ステートフル
    ・影響範囲
    ・組織のライフサイクル
  • 依存関係の制御

本事例

無造作にコンポーネント分割していくと、state が増えた場合の運用が煩雑になるので、本事例では依存関係を一方向に制限することで見通しを良くし、運用をなるべくシンプルにすることを考えます。

アプローチ

ソフトウェアの SOLID 原則のように、コンポーネントを一方向の依存関係にする。

  • ただし、SOLID 原則と違って、隣接コンポーネントを越えて依存してもよい。
  • 上位コンポーネントで output として値を出力し、下位コンポーネントで terraform_remote_state を使って値を参照する。

02-vpc/outputs.tf

output "private_subnets" {
  description = "List of IDs of private subnets"
  value       = module.vpc.private_subnets
}

03-db/data.tf

data "terraform_remote_state" "vpc" {
  backend = "s3"

  config = {
    bucket = "${local.name}-terraform-state"
    key    = "02-vpc/terraform.tfstate"
    region = local.region
  }
}

data "aws_subnet" "private" {
  for_each = toset(data.terraform_remote_state.vpc.outputs.private_subnets)
  id       = each.value
}
  • トポロジカルソートして順番をディレクトリ名の接頭辞にする。

環境の出し分けは変数差し替えで実現する。

cd $ENV
terraform -chdir=.. init -backend-config=$ENV/backend.tfvars
terraform -chdir=.. apply -var-file=$ENV/terraform.tfvars

ディレクトリ構成

コンポーネントごとに state を分けます。

この際、上位(接頭辞の値が小さい)コンポーネントの output のみ参照できます。

.
├── 01-kms
│     ├── dev
│     │     ├── backend.tfvars
│     │     └── terraform.tfvars
│     ├── prd
│     │     └── (省略)
│     ├── backend.tf
│     ├── main.tf
│     ├── outputs.tf
│     ├── provider.tf
│     └── variables.tf
├── 02-vpc
│     └── (省略)
├── 03-db
├── 04-route53
├── 05-alb
├── 06-eks-log
├── 07-eks-cluster
├── 08-apps
└── 09-system
分割した state の依存関係

利点

  • state を適切なサイズに分割できる。
  • 接頭辞を見ればコンポーネントの依存方向がわかる。
  • 全体をモジュールでラップしなくて済む。

課題点

  • 管理する state の数が多くなる。
  • 依存する state での apply も必要になる。
    ・忘れがち。
    ・モノリスなら Terraform が担っていた処理を人力でやらないといけない。
  • 間に state を追加すると、backend と terraform_remote_state を一斉に書き換えなければならない。
  • リソースを綺麗に一方向の依存関係で分割できるかどうかは怪しい。

まとめ

肥大化した Terraform state への対処として、依存関係を明示しながらコンポーネント分割する方法を紹介しました。
この方法では、state を見通しのよい単位に分割できるメリットがあります。

その反面、管理対象の state の数が増えることによるデメリットもあります。
Terraform state 構成は正解がないですが、運用していてデメリットが顕在化してきた場合は、構成の改善を検討してみてはいかがでしょうか。

社内共有会で発表したときの質問・反応

  • 削除はどうなるか?
    構築の逆順(接頭辞の降順)に destroy する。
  • apply 漏れが発生しそうだがどうか?
    依存 state だけ apply すればよい。
    ただ、作業者が毎回ちゃんとやってくれるかどうかは保証できないため、注意が必要。
  • 運用でつらいところはないか?
    編集で依存方向ミスは発生しうるため、レビューで弾くなど必要。
    依存 state の自動 apply などできれば望ましい。
  • plan 時間は短縮できたか?
    初期構築はオーバーヘッド分時間が増加した。その後の更新は数分から数十秒になった。
  • どのような方針でその分割単位にしたか?
    log は eks-cluster で共通にしたかったので分けた。
    alb はバックエンドの切り替えで細かく制御したいので分けた。
    要件によって柔軟に変えてよい。
  • モジュールを外部リポジトリに分けるかどうかの基準は?
    モジュールを多数のリポジトリで共通化したい。
    大規模になって,共通モジュールを別チームが管理するようになった。
    インポート元リポジトリとは別のライフサイクルでバージョニングが必要。
  • 移行はどうしたか?
    必要なリソースだけ先に適切なコンポーネントに移動した。
    新しいリソースを分割したコンポーネント内に作成した。

ブログ一覧へ戻る

お気軽にお問い合わせください

SREの設計・技術支援から、
SRE運用内で使用する
ツールの導入など、
SRE全般についてご支援しています。

資料請求・お問い合わせ