IaC(Terraform / Terragrunt)を1年以上運用してきた所感
こんにちは、KANNA の SRE チームの okenak です。
インフラの運用を続けていると、
「この設定、どこで入れたんだっけ?」
「本番を触るのが怖い」
といった状況に直面することがあると思います。
本記事では、Terraform / Terragrunt を使った IaC を1年以上運用してきた中で、
そうした状況がどう変わったか、またどんなつらさがあったかを整理しています。
IaC / Terraform / Terragrunt について(補足)
IaC はインフラ構成をコードで管理する考え方で、Terraform はそれを実現する代表的なツールです。
Terragrunt は Terraform をマルチプロダクト構成でも運用しやすくするための補助ツールです。
IaCに慣れると、もう戻れない
IaC を本格的に運用するようになってから、正直なところ IaC なしのインフラ運用には戻れない と感じています。
コードを見れば、現在のインフラ構成や設定内容を把握できる、という状態は、想像以上に心理的な安心感があります。
一方、手動オペレーション中心の運用では、
- どこにどの設定が入っているのか分からなくなる
- 環境ごとに設定が微妙にズレる
- 「触ってはいけない雰囲気」だけが蓄積される
といった状態になりがちで、結果として運用がカオス化し、うかつに手を入れること自体がリスクになっていました。
IaCとAIの相性は思っていたより良かった
IaC はコードである以上、生成AIとの相性はかなり良いと感じています。
Claude Code などの生成AIツールを使って Terraform / Terragrunt のコードを書かせることもありますが、ペアプロ的な使い方であれば十分実用範囲だと感じています。
もちろん、ベストプラクティス的にはツッコミどころのあるコードが出てくることも多いです。ただ、
- 公式ドキュメントや最新仕様をコンテキストとして与える
- 生成されたコードを前提に、人間がレビューする
という前提であれば、設計のたたき台としてはかなり助けられています。
一方で、気を抜くと普通に変なコードやセキュリティ上問題のある設定は生まれるので、IaCにおいてレビューが不要になることはない、という点は強く感じています。
IaCならではのつらさ
IaCを使っていると、ダッシュボード操作では意識しなくてよかった部分まで、きちんと理解する必要が出てきます。
- 各リソースのパラメータ
- リソース同士の依存関係
- 暗黙的に設定されている挙動
正直、最初はつらいです。ただ、長期的に見ると「ちゃんと理解した方がいい領域」でもあるので、必ずしもデメリットとは言い切れないとも感じています。
また、以下のような特殊なケースでは IaC だけで完結できない場面も発生します。
- スナップショットからの DB リストア
- ALB リスナールールの一時的な切り替え
- 緊急対応時の一時的なスケール変更や設定変更
これらは一度手動で操作した後、terraform import などで state を合わせ直す必要があります。
手順を誤ると destroy / create が走ってしまうリスクもあるため、特に prod 環境では慎重に手順を検証してから行う必要があります。
lifecycle ignore_changes の使いどころ
IaC で管理しにくい「動的に変化する値」については、lifecycle.ignore_changes を活用しています。
resource "aws_ecs_service" "this" {
# ... 省略 ...
lifecycle {
ignore_changes = [
desired_count, # AutoScaleで動的に変更されるため
load_balancer, # Blue/Green時にCodeDeploy側で切り替わるため
task_definition, # CodeDeploy側で動的に切り替わるため
]
}
}
このパターンを使わないと、apply のたびに差分が出たり、意図しない変更が入ったりします。
「IaC で管理する領域」と「動的に変化させる領域」を明確に分けることが、運用上かなり重要だと感じています。
複数人運用と認知負荷
複数人で同じ IaC を触る場合、コンテキスト理解のコストはそれなりに高くなります。
誰が、どの範囲を、どういう意図で変更しているのかを把握しないと、安全に手を入れられません。
その点では、Terragrunt で state を分離できているおかげで、作業範囲の衝突はかなり減りました。
完全に楽とは言えませんが、「致命的に怖い」状態からは脱却できた感覚があります。
モジュール分割設計はいまだに難しい
モジュール設計については、正直いまでも明確な正解は分かっていません。
汎用化しすぎると引数や条件分岐が増えて見通しが悪くなり、
特化しすぎると変更に弱くなります。
そのため現在は、モジュールで無理に DRY を担保しない という割り切りをしています。
環境差分や細かな設定値は Terragrunt の階層的な設定に寄せ、
Terraform モジュールは「構造が本当に共通な部分」だけを切り出す方針です。
modules/
ecs/
service/
alb-blue-green/
alb-inplace/
without-alb/
task-definition/
kanna-project/
kanna-payment/
kanna-report/
alb/
kanna-project/
kanna-payment/
ECS Service は構成がほぼ共通なため汎用モジュール化し、
Task Definition は差分が大きいためプロダクト別にしています。
この「汎用 vs 特化」の判断は変更頻度と差分の性質を見ながら行っていますが、
今後も揺れ続けるポイントだと思っています。
Terragruntは「規模が出た瞬間」に効いてくる
環境やプロダクトの数が増えてくると、
「どの設定がどこで定義されているのか」を把握するだけでも大きな負担になります。
Terragrunt では、設定を階層的に分離することで、
環境ごと・プロダクトごとの違いを一箇所に集約し、
各リソースでは必要最小限の設定だけを書くことができます。
階層的な設定ファイルによるDRY
aws/kanna-platform/
root.hcl
environment/
aldagram-prod/
ap-northeast-1/
prod/
environment.hcl
kanna-project/
product.hcl
ecs/
service/
api/
terragrunt.hcl
この構成では、
- root.hcl:全環境共通の設定
- environment.hcl:環境ごとに異なる設定
- product.hcl:プロダクト固有の設定
- terragrunt.hcl:リソース固有の設定
という役割分担になっており、
root.hcl → environment.hcl → product.hcl → terragrunt.hcl の順で設定を積み上げています。
その結果、各 terragrunt.hcl には「そのリソースで本当に必要な設定」だけを書けばよくなり、環境数が増えても管理コストが大きく増えません。
ワークスペース名の自動生成
Terraform Cloud を使った運用では、ワークスペース名の管理が地味に効いてきます。
環境やプロダクトの数が増えると、
「このワークスペースはどの環境・どのリソースに対応しているのか」を
人間が名前で管理するのは現実的ではなくなります。
そこで、Terragrunt ではディレクトリ構造からワークスペース名を自動生成しています。
locals {
workspace_name = join("_", compact(split("/", get_path_from_repo_root())))
}
この仕組みにより、ディレクトリとワークスペースが常に 1 対 1 で対応し、
名前の付け忘れや命名ルールのブレを気にせずに済むようになりました。
環境やリソースが増えても、
「どのワークスペースを触っているのか」を迷いにくくなる点は、
規模が出たときに特に効いています。
依存関係の明示的な管理
dependency "vpc" {
config_path = "${get_parent_terragrunt_dir("product")}/../none-product/vpc"
}
dependency "ecs_cluster" {
config_path = "${get_parent_terragrunt_dir("product")}/ecs/cluster"
}
dependency "alb" {
config_path = "${get_parent_terragrunt_dir("product")}/alb/api"
}
環境やリソース数が増えると、「どの順番で apply すべきか」を人間が意識するのは現実的ではなくなります。
run-all 実行時に依存関係が解決され、正しい順序で apply されます。
環境数が多くても回せている理由
現在は、以下のような環境構成で運用しています。
- prod / pre-prod / loadtest
- dev / qa1〜qa15 / shared
shared には RDS や ElastiCache など、複数 QA 環境で共有するリソースを配置しています。
環境数は多いですが、Terragrunt のおかげで運用コストはそこまで増えていません。
Terraform Cloud(Free Plan)+Terragruntのつらみ
Terraform Cloud(Free Plan)と Terragrunt を組み合わせた運用では、つらい点もありました。
Terragrunt で多数の workspace を一括実行する構成では、
Terraform の実行ごとに provider の初期化処理が走り、
その際に取得される discovery document の回数が短時間に集中します。
結果として rate limit に達し、実行が不安定になるケースがありました。
また、
- 旧 Free Plan を継続利用している
- 既存リソース数が新 Free Plan の上限を超えており、移行できない
- 有料プランは月額が高く、現実的ではない
といった背景もあり、現在は S3 backend への移行を検討しています。
正直なところ、最初から S3 を選んでおいてもよかったかもしれません。
おわりに
IaC(Terraform / Terragrunt)は、学習コストや運用上のつらさは確実にあります。
それでも、1年以上運用してきた今の感覚としては、「それを差し引いても十分に価値がある」と感じています。
20環境・5プロダクトという規模を Terraform 単体で管理しようとすると、かなり厳しかったと思います。
Terragrunt による state 分離と階層的な設定管理があってこそ、現実的な運用ができています。
ただし、今のやり方が最適かどうかは分かりません。
このあたりは、これからも試行錯誤しながらアップデートしていくつもりです。
株式会社アルダグラムのTech Blogです。 世界中のノンデスクワーク業界における現場の生産性アップを実現する現場DXサービス「KANNA」を開発しています。 採用情報はこちら: herp.careers/v1/aldagram0508/
Discussion