「AnsibleとTerraformの連携が壊れる理由はライフサイクルにあった」第2部まとめブログ:TerraformとAnsibleの運用で直面する構成ズレと状態管理の問題
🗺️ シリーズ全体の位置づけを知りたい方はこちら
本記事は「AnsibleとTerraformの連携が壊れる理由はライフサイクルにあった」シリーズ(全50回、5部構成)のうち、第2部(第11〜20回)のまとめ記事です。
シリーズ全体(第1〜5部)の関係については、以下の統合ブログで整理しています。→ 「AnsibleとTerraformの連携が壊れる理由はライフサイクルにあった」シリーズ統合ブログ ※近日公開予定
目次
0. このブログの使い方
1. はじめに
2. 第2部の処理フロー図解
3. 第11〜19回の事象と処理フローの対応関係
4. ミュータブル、イミュータブルの軸での再整理
5. 第3部への接続
6. 初めて読む方へ
7. 第2部 各回リンク
0. このブログの使い方
このブログは、「AnsibleとTerraformの連携が壊れる理由はライフサイクルにあった」シリーズの第2部(第11〜20回)の全体像を把握するための記事です。
- 第2部(運用、ライフサイクル編)の全体像を先に把握してから各回を読みたい方、または各回を読んだ後に全体を俯瞰したい方は、このまま読み進めてください
- シリーズ全体(第1〜5部)の位置づけを把握したい方は、「AnsibleとTerraformの連携が壊れる理由はライフサイクルにあった」シリーズ統合ブログ (※近日公開予定) をご覧ください
- 各回の詳細なトラブル内容、実機検証の手順を確認したい方は、第7節 の各回リンクから該当回に進んでください
1. はじめに
「Terraformでインフラを作って、Ansibleで中身を設定すれば、あとはそのまま運用していける」と考えていないでしょうか。
第1部(第1〜10回)で扱ったのは、この「作るところまで」でした。Terraformがリソースを生成し、Ansibleがその上に構成を投入するという一連の流れが、SSH接続のタイミングやIPアドレスの変動、Pythonバージョンの不一致といった個別のトラブルとしてどこでつまずくかを、実機検証を交えて整理しています。
しかし、インフラは一度構築して終わりではありません。運用が始まると、
- 手動での変更
-
terraform applyの再実行 - 時間の経過によるパッケージの更新
- チームでの分業
といった、構築時には想定しきれない場面が次々に発生します。こうした変化に共通しているのは、TerraformとAnsibleがそれぞれ把握している状態と、実際の環境との間に、少しずつズレが生じていくという構造です。TerraformとAnsibleは、自分が管理する範囲の状態しか把握しておらず、もう一方の管理範囲で起きた変化を自動的に検知する仕組みを持っていません。この構造は、構築が完了した後も稼働を続けるインフラである以上、避けられない前提です。
このブログは、「AnsibleとTerraformの連携が壊れる理由はライフサイクルにあった」という全50回、5部構成のシリーズのうち、第2部(第11〜20回、運用、ライフサイクル編)を整理するまとめ記事です。第2部では、この構築後の継続運用の中で発生する構成のズレと状態管理の問題を、第11回から第20回まで一回ずつ扱っています。このブログでは、第11〜19回で扱っている個別の事象を振り返るのではなく、それらが運用フェーズの処理フローのどのタイミングで発生していたかを整理し直し、対応関係として示します。あわせて、第1部と第2部で扱ってきた問題の性質がどう異なるか、そして第20回で整理しているミュータブル運用とイミュータブル運用という軸で見たときに第2部全体がどう見えるかを、それぞれ振り返ります。各回の詳細なトラブル内容、実機検証の手順には踏み込みません。詳細を確認したい方は、第7節 の各回リンクから該当回に進んでください。
- これから第2部を読み始める方にとっては全体像を先につかむための地図として
- すでに各回を読まれた方にとっては読んだ内容を俯瞰して整理し直すための記事として
どちらの立場でも読み進めていただける内容になっています。
次のセクションでは、第2部の範囲で実行される運用フェーズの処理フローを図で整理します。
2. 第2部の処理フロー図解
第1部では、Terraformがリソースを生成してからAnsibleが構成管理を完了するまでの、①〜⑨という単一方向の処理フロー(以下、構築フロー)を整理しました。
【構築フロー】
① TerraformがインフラリソースをHCLコードで定義する
↓
② terraform applyを実行する
↓
③ Docker EngineにコンテナリソースのAPIリクエストを送る
↓
④ Docker Engineがコンテナを生成する
├─ DockerネットワークがIPアドレスを払い出す
├─ ネットワークルールを適用する
└─ コンテナ内のプロセスが起動する
↓
⑤ TerraformがコンテナのAPIレスポンスを受け取る
├─ 秘密鍵をローカルに出力する
└─ tfstateにリソース情報を記録する
↓
⑥ Terraformがlocal-execでAnsibleを呼び出す
↓
⑦ AnsibleがSSH経由でコンテナに接続する
├─ SSHDの起動確認
└─ 秘密鍵、接続ユーザーの確認
↓
⑧ AnsibleがコンテナOS上でモジュールを実行する
├─ Pythonバージョンの確認
├─ become権限昇格
└─ Playbookのタスクを順に実行する
↓
⑨ 実行結果をコントロールノードに返す
第2部が扱うのは、この⑨より先です。環境が一度構築された後、稼働状態に入ってからも、インフラは動き続けます。この稼働の中で繰り返し発生する処理の流れを、ここでは運用フローと呼び、図解します。構築フロー①〜⑨とは、⑨の直後を⑩とすることで番号を連続させています。
【運用フロー】
①〜⑨(構築フローと同一、⑨で完了)
↓
⑩ 稼働状態
↓
⑪ 手動変更、パッケージ更新、コード修正、チーム操作(変化の発生)
↓
⑫ 構成ズレ、状態変化が発生する
↓
⑬ terraform plan、ansible-playbook --checkで差分検知
↓
⑭ terraform apply、ansible-playbookで同期、再設定
↓
⑮ 稼働状態に戻る(またはリソース再生成を経由して⑩に戻る)
↓
(⑪に戻る)
構築フローと運用フローの最も大きな違いは、運用フローが「一度きりの実行」ではなく「繰り返し発生する処理サイクル」を前提にしている点です。構築フローの①〜⑨は、構築が完了すれば一度で終わる直線的な流れでした。これに対し運用フローは、⑩の稼働状態を起点に、⑪〜⑮という一連の流れを何度も繰り返しながら運用が続いていきます。
この繰り返しの中で、⑭の同期の結果が「⑮稼働状態への復帰」で終わる場合と、「リソース再生成」を経由する場合の2通りがある点にも触れておきます。手動変更のようにリソース内部の状態だけが変化するケースでは、⑭の同期は⑮稼働状態への復帰で完結します。一方、リソース定義そのものの変更が更新ではなく破棄、再生成として扱われるケースでは、⑭の過程でリソースが一度作り直され、その後にAnsibleによる再設定を経て⑮稼働状態に戻ります。この分岐は、次節で第11〜19回の各回を運用フロー上に対応づける際の軸のひとつになります。
なお、リソース再生成によって生じる変化の具体例として、ポート設定、イメージ、タグといった属性の変更を挙げます。これらはいずれも環境非依存の一般的な例であり、本検証環境(Docker)に固有のものではありません。VM、クラウド環境であっても、同様の属性変更が更新ではなく再生成として扱われる場面は共通して存在します。
次のセクションでは、この運用フローの中で第11〜19回の事象がそれぞれどのタイミングで発生していたかを対応づけます。
3. 第11〜19回の事象と処理フローの対応関係
第2節 で番号を振った運用フローの中で、第11〜19回の事象がそれぞれどのタイミングで発生していたかを対応づけます。各回の詳細な検証内容には踏み込まず、各回へのリンク(第7節)で示すにとどめます。
| 回 | 事象 | 運用フロー上の発生タイミング |
|---|---|---|
| 第11回 | 手動変更による構成ドリフトの検知 | ⑩→⑬ |
| 第12回 | 初期化処理変更によるOS設定消失 | ⑪→⑭(リソース再生成を経由) |
| 第13回 | リソース定義変更による強制再生成 | ⑪→⑭(リソース再生成を経由) |
| 第14回 | Playbook複数回実行時の冪等性崩壊 | ⑭ |
| 第15回 | tfstateの競合リスク | ⑪→⑭ |
| 第16回 | パッケージアップデートによる非互換 | ⑩→⑪→⑭ |
| 第17回 | IPアドレス変動と接続情報の更新遅延 | ⑭(リソース再生成後)→⑮ |
| 第18回 | 機密情報の役割分担 | ①〜⑮全体(設計段階の問題) |
| 第19回 | OSメジャーバージョンアップの非互換 | ⑪→⑭(リソース再生成を経由)→⑮ |
この対応関係から、いくつかの傾向が見えてきます。
第11回、第16回 は、いずれも⑩稼働状態を起点にしています。リソース定義そのものは変更されず、稼働し続けている間にOS内部の状態やパッケージバージョンが静かにずれていくという点が共通しています。この2回は、運用フローの中でも変化の主体がAnsible側の管理範囲、つまりOS層にあるという特徴を持ちます。
第12回、第13回、第19回 は、いずれも⑪のコード修正を起点に、⑭のterraform applyを経てリソース再生成へと至ります。この3回に共通するのは、変化の主体がTerraform側にあり、意図した操作の結果としてリソースが作り直されるという構造です。第19回 は同じ構造を持ちながら、変化の範囲がベースイメージ全体という、より大きな単位に及ぶ点が 第12回、第13回 との違いです。
第17回 は、⑭のリソース再生成そのものではなく、その後の⑮稼働状態への復帰フェーズに位置づけられます。再生成によって変わったIPアドレスという識別子が、インベントリや関連設定にどこまで追従するかという、再生成の後始末にあたる問題だからです。
第14回 は、⑭の同期、再設定フェーズそのもの、つまりAnsibleが再実行される場面に位置づけられます。この再実行は、第11回 のような手動変更への対応としても、第12回、第13回 のようなリソース再生成後の復旧としても発生するため、運用フローの中でも複数の経路から合流する地点にあたります。
第15回 は、⑪の「チーム操作」という起点を持ちます。変化の主体が単独の操作ではなく複数人の同時操作にある点が、他の回との違いです。
第18回 は、運用フロー上の特定の1点に位置づけることができません。機密情報の管理という問題は、①の構築フロー開始時点から⑮の運用フロー全体を通じて存在し続けるため、フローの実行時ではなく、フロー全体を支える設計の話にあたります。この位置づけは、第1回 が構築フローの①②という設計段階に位置づけられていたことと、性質としてよく似ています。
次のセクションでは、この対応関係を踏まえ、第2部で扱った問題をミュータブル、イミュータブルの軸で整理し直します。
4. ミュータブル、イミュータブルの軸での再整理
第2部で扱った問題は、運用フロー上の発生タイミングだけでなく、もうひとつ別の軸でも整理し直すことができます。第20回 で整理した、「リソースを直し続けて使うか、使い捨てて入れ替えるか」というミュータブル運用とイミュータブル運用の軸です。
| 運用思想 | 該当する回 | 特性 |
|---|---|---|
| ミュータブル型の問題 | 第11回、第15回、第16回 | 直し続けることが前提。ドリフト、非互換が蓄積しやすい |
| イミュータブル型の問題 | 第12回、第13回、第17回 | 作り直しが起点。データ消失、追従コストが課題 |
| 両方に関わる問題 | 第14回、第18回、第19回 | どちらの運用思想でも発生しうる |
この分類を 第3節 の整理と重ねると、興味深い対応関係が見えてきます。ミュータブル型、イミュータブル型に分類された回は、いずれも第3節での位置づけ(⑩起点か、⑪→⑭のリソース再生成かという分岐)とそのまま対応しており、ここに新しい発見はありません。注目すべきは「両方に関わる問題」です。第3節 では、第14回、第18回、第19回 の3回を、運用フロー上でそれぞれ異なる位置づけとして整理していました。しかしミュータブル、イミュータブルの軸では、この3回はいずれも「両方に関わる問題」として揃います。第14回 の冪等性は、直し続ける運用でも作り直す運用でもAnsibleの再実行という形で発生し、第18回 の機密情報管理は運用思想を問わず必要な設計であり、第19回 のOSメジャーバージョンアップは、イミュータブルな操作でありながら段階的な検証というミュータブル運用に近い進め方を要する、という性質を持っています。運用フロー上の位置づけが一様でなかったこれら3回が、運用思想の軸ではむしろ共通してどちらのタイプにも当てはまるという結果になっている点は、2つの軸が異なる角度から同じ第2部の問題群を照らしていることを示しています。
この運用思想の違いによって、TerraformとAnsibleの役割分担の重心も変わります。ミュータブル運用ではAnsibleが継続的な構成管理を担う主役になり、イミュータブル運用ではTerraformがリソースの作成、破棄を頻繁に担う主役になります。ミュータブル運用とイミュータブル運用は、それぞれに利点と欠点があり、片方が一方的に優れているという結論にはなりません。第20回 で示した結論は、すべてのリソースを一律にどちらかへ寄せるのではなく、ステートフルなリソースはミュータブル寄り、ステートレスなリソースはイミュータブル寄りという、リソースの性質による使い分けが現実的な折衷案になる、というものです。
第2部全体を通じて見えてきたのは、運用フローという時間軸の繰り返しと、ミュータブル、イミュータブルという運用思想の軸が、互いに独立しながらも重なり合いながら、構築後の継続運用というテーマを形作っているという構造です。詳細な議論は 第20回 に譲り、ここでは対応表と、この一言の整理にとどめます。
次のセクションでは、この第2部の整理を踏まえ、第3部が何を扱うのかを整理します。
5. 第3部への接続
第21回 からの第3部(トラブルシューティング、デバッグ編)では、視点が変わります。第2部が「設計、運用で防ぐ」ことを扱っていたのに対し、第3部は「起きてしまった問題をどう特定するか」という、障害発生時の原因特定とデバッグに焦点を移します。
第2部(第11〜20回):運用、ライフサイクル編
→ 問題が発生する構造と、設計、運用による対処
↓
第3部(第21〜30回):トラブルシューティング、デバッグ編
→ 起きてしまった問題の、原因特定とデバッグ
第2部までの設計、運用による予防と、第3部からの原因特定、デバッグは、連続する2つのフェーズとして位置づけられます。
次回:「AnsibleとTerraformの連携が壊れる理由はライフサイクルにあった」第21回:統合実行時におけるネストされたエラーログの解析手法
第3部:トラブルシューティング、デバッグ編
| 回数 | テーマ、記事タイトル | 概要 |
|---|---|---|
| 第21回 | 統合実行時におけるネストされたエラーログの解析手法 | Terraformのlocal-exec経由で実行されたAnsibleのエラーログから、原因がTerraform側(HCL、State)かAnsible側(Playbook、タスク)かを特定する手法。 |
| 第22回 | マルチネットワーク環境におけるインターフェース競合 | 複数ネットワーク(Dockerネットワーク等)をアタッチした際、Ansibleのansible_default_ipv4や接続IPの自動検出が意図しないインターフェースに引きずられる問題。 |
| 第23回 | 大容量ファイル転送、重いタスク実行時におけるSSHタイムアウト | Ansibleで大きなアセットや大量のパッケージを転送、適用する際、Terraform側のプロビジョナータイムアウトに引っかかる可能性がある問題を扱う。 |
| 第24回 | 並列実行時における実行ホストのシステムリソース枯渇対策 |
parallelismやforks設定により、大量のリソース構築とAnsibleプロビジョニングが同時に走った場合、ホストのCPU、メモリ、ファイルディスクリプタが枯渇しうる問題を扱う。 |
| 第25回 | 構文チェックツール(HCL構文、ansible-lint)の競合緩和 | 両ツールの静的解析ツール(terraform fmt、ansible-lint)を導入した際、コード記述ルールや命名規則の不一致でCIが通らなくなる問題。 |
| 第26回 | 管理者権限(sudo)実行時におけるパスワード入力のプロンプト停止 | Terraformのlocal-execや非対話シェルからAnsibleを実行した際、sudo: a terminal is required to read the passwordで停止する問題。 |
| 第27回 | 再起動、再生成に伴うプロビジョニング断絶 | Ansibleでカーネル更新等のためにOS再起動(またはコンテナ再起動、再生成)を要求した際、接続が途切れTerraformがエラー扱いする問題。 |
| 第28回 | 異種OS(Windowsターゲット)混在環境における接続プロトコルの制約 | SSHではなくWinRM等を用いる特殊プロトコル環境での接続、権限エラー。実務でWindows ServerをAnsible×Terraformで管理する際の構造的注意点を扱う概念解説回。 |
| 第29回 | プロキシ環境等における外部コレクション(Ansible Galaxy)の取得失敗 | インフラ構築処理の途中で外部ネットワーク(Ansible Galaxy等)への依存が切れ、Terraformの処理全体が失敗する問題。 |
| 第30回 | デバッグフラグの組み合わせによるログ解析の高度化 | TerraformのTF_LOGとAnsibleの-vvvvを組み合わせ、接続遅延や処理遅延のボトルネックを特定、解消する手法。 |
6. 初めて読む方へ
ここまで、第2部の運用フローと、そこで発生する事象の全体像を見てきました。実際にどの回から読み進めるか迷っている方は、関心に応じて以下を参考にしてください。
-
手動で変更した内容を、
terraform planが検知してくれない → 第11回 -
terraform applyを再実行しただけなのに、Ansibleで設定したはずの内容が消えている → 第12回、第13回 - 2回目以降のAnsible Playbook実行でエラーになる、terraform apply自体が失敗する → 第14回
- 複数人でTerraformを操作していて、tfstateの競合が心配 → 第15回
-
apt update等のパッケージ更新後、以前は動いていたPlaybookが失敗するようになった → 第16回 - リソースを再生成した後、Ansibleが古いIPアドレスを参照して接続に失敗する → 第17回
- データベースのパスワードやAPIキーを、TerraformとAnsibleのどちらで管理すべきか迷っている → 第18回
- ベースイメージのOSバージョンを上げたら、Playbookが動かなくなった → 第19回
- ミュータブル運用とイミュータブル運用、どちらを選べばよいか知りたい → 第20回
7. 第2部 各回リンク
第2部(第11〜20回)の各回タイトルと概要の一覧です。各回の詳細なトラブル内容、実機検証の手順は、リンク先の本文をご覧ください。
第2部:運用、ライフサイクル編
| 回数 | テーマ、記事タイトル | 概要 |
|---|---|---|
| 第11回 | 手動変更による構成ドリフトの検知と同期手法 | 構築後に手動やAnsibleで変更したOS内部の状態を、Terraformのplanが検知できず、インフラの管理状態に不整合が出る問題。ドリフトシリーズとの接続を示す。 |
| 第12回 |
terraform apply実行時における初期化処理とOS設定の上書き問題 |
Terraform側で初期化スクリプトやコンテナ起動定義を書き換えて再実行した際、Ansibleによって設定済みのOS内部状態が初期化される課題。 |
| 第13回 | コード修正に伴うリソースの強制再生成(リビルド)リスク | TerraformのHCL定義変更によって、リソースが「更新」ではなく「破棄、再生成」され、Ansibleが投入した内部データが消失する課題。 |
| 第14回 | 複数回実行時におけるAnsible Playbookの冪等性の確保 |
terraform applyのlocal-exec経由でAnsibleが複数回実行される構成において、冪等性が確保されていないPlaybookがterraform apply自体の失敗を引き起こす問題。 |
| 第15回 | チーム運用における状態管理ファイル(tfstate)の整合性維持 | Ansibleを実行するオペレーターと、Terraformを管理するエンジニア間で、Terraformの状態管理ファイルに競合が発生するリスク。チーム運用での役割分担設計も整理する。 |
| 第16回 | パッケージアップデートに伴う環境の非互換性への対応 | 運用中にapt update等を実行した結果、OSのライブラリやミドルウェアのバージョンが上がり、Terraformの定義と矛盾が生じるケース。 |
| 第17回 | リソース再生成時におけるIPアドレス変動と接続情報の更新遅延 | リソース(コンテナ、VM)の再生成に伴ってIPアドレスが変更された際、Ansible用のインベントリや各種設定ファイルの書き換えが追いつかない問題。 |
| 第18回 | TerraformとAnsible Vaultにおける機密情報の役割分担 | データベースのパスワードやAPIキーなどの機密情報を、両ツールのどちらで暗号化し、どのように管理すべきかの運用設計。Vault誤用パターンと回避策も整理する。 |
| 第19回 | OSのメジャーバージョンアップ時におけるPlaybookの互換性検証 | Terraform側でベースイメージ(例:Ubuntu 22.04→24.04)を変更した際、Ansibleの古い独自モジュールやシェルコマンドが機能しなくなる課題。 |
| 第20回 | 運用編まとめ:ミュータブル運用とイミュータブル運用の折衷案 | 状態(データ)を維持し続ける運用(ミュータブル)と、使い捨てる運用(イミュータブル)を、両ツールの組み合わせでどのように着地させるかの最適解。 |