「AnsibleとTerraformの連携が壊れる理由はライフサイクルにあった」第1部まとめブログ:環境構築・連携編で直面する9つのトラブル
🗺️ シリーズ全体の位置づけを知りたい方はこちら
本記事は「AnsibleとTerraformの連携が壊れる理由はライフサイクルにあった」シリーズ(全50回・5部構成)のうち、第1部(第1〜10回)のまとめ記事です。
シリーズ全体(第1〜5部)の関係については、以下の統合ブログで整理しています。→ 「AnsibleとTerraformの連携が壊れる理由はライフサイクルにあった」シリーズ統合ブログ ※近日公開予定
目次
0. このブログの使い方
1. はじめに
2. Ansible×Terraform×Docker環境の処理フロー
3. 処理フローの中で第1〜9回の事象が発生するタイミング
4. 第2部(運用・ライフサイクル編)への接続
5. 初めて読む方へ
6. 第1部 各回リンク
0. このブログの使い方
このブログは、「AnsibleとTerraformの連携が壊れる理由はライフサイクルにあった」シリーズの第1部(環境構築・連携編)の全体像を把握するための記事です。
- 第1部(環境構築・連携編)の全体像を先に把握してから各回を読みたい方、または各回を読んだ後に全体を俯瞰したい方は、このまま読み進めてください
- シリーズ全体(第1〜5部)の位置づけを把握したい方は、「AnsibleとTerraformの連携が壊れる理由はライフサイクルにあった」シリーズ統合ブログ (※近日公開予定) をご覧ください
- 各回の詳細なトラブル内容・検証の手順を確認したい方は、第6節 の各回リンクから該当回に進んでください
1. はじめに
「TerraformでリソースをつくってAnsibleで中身を整えれば、それで自動化は完成する」と思っていないでしょうか。
実際、多くの入門記事やチュートリアルはこの通りに進みます。terraform applyでリソースを作り、続けてansible-playbookを流す。この2つを組み合わせれば、インフラの構築から中身の設定まで一気通貫で終わるように見えます。
しかし実際に運用してみると、そうとは限らない場面にぶつかります。
- Terraformの実行が終わった直後にAnsibleを動かすと、なぜか接続できない
- コードを少し直して
applyし直しただけなのに、Ansibleで設定したはずの内容が消えている - エラーが出たとき、原因がTerraform側にあるのかAnsible側にあるのか切り分けられない
こうした場面に共通しているのは、AnsibleとTerraformがそれぞれ管理しているレイヤー・タイミング・設定が独立しており、その境界を意識しないまま連携させると、見えないところでズレが生じるという構造です。AnsibleとTerraformの連携は、複数のツールと複数のレイヤーにまたがる処理の連鎖によって成り立っています。Terraformがインフラリソースを生成し、そのリソースに対してAnsibleが構成管理を行うという流れは一見単純ですが、実際にはリソースの生成完了、ネットワークの初期化、SSH接続の確立、Pythonの実行環境、権限昇格といった複数の段階を経て初めて成立します。この構造は、コンテナ・VM・AWSやGCPなどのクラウドを問わず共通です。本シリーズでは、その中から再現のための検証環境としてDockerコンテナを使っています。
このブログは、「AnsibleとTerraformの連携が壊れる理由はライフサイクルにあった」という全50回、5部構成のシリーズのうち、第1部(第1〜10回、環境構築・連携編)を整理するまとめ記事です。第1部では、この連携の中で起きる9つのトラブル(第1〜9回)と、それらを踏まえたコードテンプレート化(第10回)を、それぞれ検証や構造の整理を交えながら個別に扱っています。このブログでは、個々のトラブルの詳細には踏み込まず、それらが処理フローの中のどのタイミングで発生するのかを俯瞰し、第1部全体の位置づけを整理します。
- これから第1部を読み始める方にとっては、全体像を先につかむための地図として
- すでに各回を読まれた方にとっては、読んだ内容を俯瞰して整理し直すための記事として
どちらの立場でも読み進めていただける内容になっています。
次のセクションでは、Ansible×Terraform×Docker環境の連携で実行される処理フローを図で整理します。
2. Ansible×Terraform×Docker環境の処理フロー
Ansible×Terraform×Docker環境の連携で実行される処理の流れを、第1部の範囲に絞って整理します。
以下の流れは、各回の検証で使っているDockerコンテナを例にしたものです。コンテナ・VM・クラウドのどれでも、リソース生成先(③④)とその中身(⑦⑧の接続先・実行先)が変わるだけで、①〜⑨の流れそのものは共通です。環境ごとの読み替えは、図の後の対応表で整理します。
① 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のタスクを順に実行する
↓
⑨ 実行結果をコントロールノードに返す
各ステップがどのツール・どのレイヤーで実行されるかを、以下の対応表で整理します。
| ステップ | 実行主体 | レイヤー |
|---|---|---|
| ①② | Terraform | IaCツール |
| ③④ | Docker Engine | コンテナ基盤 |
| ⑤ | Terraform | IaCツール |
| ⑥ | Terraform(local-exec) | IaCツール→構成管理ツールの橋渡し |
| ⑦⑧⑨ | Ansible | 構成管理ツール |
この図で示した通り、Terraformが担うのは①〜⑥までの、リソースを生成し構成管理ツールを呼び出すところまでです。⑦〜⑨は、その先でAnsibleが実際にOS内部の構成管理を行う領域になります。この境界は、TerraformとAnsibleの管理レイヤーの違い(第1回)にそのまま対応しています。
図はコンテナを例にしているため、VM・クラウドでは次のように読み替えます。
| ステップ | Docker(図の記載) | VM・クラウド(AWS・GCPなど) |
|---|---|---|
| ③④ リソース生成 | Docker Engineがコンテナを生成する | クラウドAPIやハイパーバイザーがVM・インスタンスを生成する |
| ④ IPアドレスの払い出し | Dockerネットワークが払い出す | DHCPやサブネットの設定に従って払い出される |
| ④ 内部プロセスの起動 | コンテナ内でSSHDが起動する | OSがブートし、その後SSHDが起動する |
| ⑦ 接続先 | コンテナ | VM・インスタンス |
| ⑧ モジュールの実行先 | コンテナOS(イメージ由来のPython・ユーザー) | VMのOS(AMIやイメージ由来のPython・ユーザー) |
環境によって変わるのは、各ステップにかかる時間の長さです。VMのブートやクラウドのネットワーク伝播は、コンテナより長くなりやすい傾向があります。ただし、「リソースの生成完了」と「実際に使える状態」が別のイベントとして存在する構造は、どの環境でも共通です。
なお、⑨以降の継続的な運用(構成ドリフトの発生、tfstateと実態の不整合、Ansibleが投入した設定の消失など)は、この処理フローには含めていません。これらは 第2部 で扱う範囲であり、第4節 で改めて接続を整理します。
次のセクションでは、この処理フローの中で第1〜9回の事象がどのタイミングで発生するかを対応づけます。
3. 処理フローの中で第1〜9回の事象が発生するタイミング
第2節で整理した処理フローの中で、第1〜9回の事象がそれぞれどのタイミングで発生するかを対応づけます。各回の詳細な検証内容には踏み込まず、各回へのリンク (第6節) で示すにとどめます。ステップ番号は第2節のDockerコンテナの流れに基づきますが、VM・クラウドでも読み替えれば同じ位置づけになります。
| 回 | 事象 | 発生するステップ |
|---|---|---|
| 第1回 | 役割の境界が不明確な設計によるアンチパターン | ①②(設計段階) |
| 第2回 | TerraformのAPIレスポンス後にSSHDがまだ起動していない | ④→⑥の間 |
| 第3回 | TerraformのJSON出力をAnsibleがパースできない | ⑤→⑦の間 |
| 第4回 | 秘密鍵のパーミッション不備でSSH接続が拒否される | ⑤→⑦の間 |
| 第5回 | リソース再生成によるIPアドレス変動でインベントリと不一致になる | ④→⑦の間 |
| 第6回 | ネットワーク初期化完了前にAnsibleが接続を試みてタイムアウトする | ④→⑦の間 |
| 第7回 | PythonバージョンのズレでAnsibleモジュールが失敗する | ⑧ |
| 第8回 | TerraformのparallelismとAnsibleのforksがズレて遅延する | ③→⑧の間 |
| 第9回 | コンテナイメージのデフォルトユーザーの違いでbecome設定がずれる | ⑦⑧ |
この対応関係から、いくつかの傾向が見えてきます。
第1回 は、他の回のように処理フローの特定の1ステップに紐づくものではなく、①②の設計段階そのものに位置づけられます。役割の境界をどう設計するかという問題であるため、フローの実行時ではなく、フローを組む前の段階の話です。
第2回、第6回 は、いずれも④→⑥の間、または④→⑦の間という「ステップとステップの間」に位置づけられます。これは、リソースが生成されてから実際に使える状態になるまでのタイムラグの中で発生する事象であるためです。この2回は、単一のステップの内部で起きる問題ではなく、ステップ間の見えないラグそのものが問題の所在です。ラグの長さは環境で異なり、VMのブートやクラウドのネットワーク伝播ではコンテナより長くなりやすい一方、構造そのものは共通です。
第3回、第4回、第5回 は、いずれも⑤→⑦の間、または④→⑦の間に位置づけられます。Terraformがリソースの情報を記録・出力してから、Ansibleがその情報を使って接続を試みるまでの区間で発生する事象です。この区間では、TerraformとAnsibleの間で受け渡される情報(JSON出力、秘密鍵、IPアドレス)そのものの形式や状態が問題の起点になっています。これらはプロバイダーを問わず発生し、IPアドレスの変動もAWS EC2の再作成やDHCP環境で同じ構造で起こります。
第7回、第9回 は、⑦⑧、または⑧そのものに位置づけられます。SSH接続が確立した後、AnsibleがコンテナOS上でモジュールを実行する段階で発生する事象です。この段階では、Terraformが生成したリソースの中身(Pythonバージョン、デフォルトユーザー)が、Ansible側の前提と食い違うことが問題の起点になっています。イメージやAMIの中身をTerraformが管理していないという構造は、コンテナ・VM・クラウドを問わず共通です。
第8回 は、③→⑧の間という他のどの回よりも広い範囲にまたがっています。これは、TerraformのリソースAPI呼び出しの並列度と、Ansibleのモジュール実行の並列度という、フロー全体を横断する2つの独立した設定が噛み合わないという性質のトラブルであるためです。リソース数が並列度に対して相対的に多いときに顕在化しやすく、小規模な検証環境では表面化しにくい一方、構造はコンテナ・VM・クラウドを問わず共通です。
次のセクションでは、第1部を通じて見えてきたこの構造を踏まえ、第2部 が何を扱うのかを整理します。
4. 第2部(運用・ライフサイクル編)への接続
第2部(第11〜20回)では、視点が「構築時」から「構築後の継続運用」に移ります。
第1部(第1〜10回):環境構築・連携編
→ 初回構築時に直面するトラブルと解決策
↓
第2部(第11〜20回):運用・ライフサイクル編
→ 継続運用の中で発生する構成ズレと状態管理の問題
第1部で整理した処理フロー (第2節) は、⑨「実行結果をコントロールノードに返す」で区切っています。第2部 が扱うのは、この⑨より先、つまり環境が一度構築された後の話です。手動での変更や、terraform applyの再実行によって、構築時に整えたはずの状態がどのようにずれていくのか、そのずれをどう検知し、どう同期させるのかが、第2部 のテーマになります。
第1部を読み終えた方は、続けて 第2部(第11回〜)へ進んでいただくと、環境構築から継続運用までの流れを一通り把握できます。
次回:「AnsibleとTerraformの連携が壊れる理由はライフサイクルにあった」 第11回:手動変更による構成ドリフトの検知と同期手法
第2部:運用・ライフサイクル編
| 回数 | テーマ・記事タイトル | 概要・備考 |
|---|---|---|
| 第11回 | 手動変更による構成ドリフトの検知と同期手法 | 構築後に手動やAnsibleで変更したOS内部の状態を、Terraformのplanが検知できず、インフラの管理状態に不整合が出る問題。ドリフトシリーズとの接続を示す。 |
| 第12回 |
terraform apply実行時における初期化処理とOS設定の上書き問題 |
Terraform側で初期化スクリプトや起動定義を書き換えて再実行した際、Ansibleによって設定済みのOS内部状態が初期化される課題。 |
| 第13回 | コード修正に伴うリソースの強制再生成(リビルド)リスク | Terraformの定義変更によって、リソースが「更新」ではなく「破棄・再生成」され、Ansibleが投入した内部データが消失する課題。 |
| 第14回 | 複数回実行時におけるAnsible Playbookの冪等性の確保 | 1回目の実行は成功するものの、2回目(運用フェーズ)の実行時に「ファイル重複」や「サービス重複起動」等でAnsibleが停止する問題。冪等性シリーズ・レガシーPlaybook引き継ぎの知識を実践に適用する回として位置づける。 |
| 第15回 | チーム運用における状態管理ファイル(tfstate)の整合性維持 | Ansibleを実行するオペレーターと、Terraformを管理するエンジニア間で、Terraformの状態管理ファイルに競合が発生するリスク。チーム運用での役割分担設計も整理する。 |
| 第16回 | パッケージアップデートに伴う環境の非互換性への対応 | 運用中にapt update等を実行した結果、OSのライブラリやミドルウェアのバージョンが上がり、Terraformの定義と矛盾が生じるケース。 |
| 第17回 | リソース再生成時におけるIPアドレス変動と接続情報の更新遅延 | リソースの再生成に伴ってIPアドレスが変更された際、Ansible用のインベントリや各種設定ファイルの書き換えが追いつかない問題。 |
| 第18回 | TerraformとAnsible Vaultにおける機密情報の役割分担 | データベースのパスワードやAPIキーなどの機密情報を、両ツールのどちらで暗号化し、どのように管理すべきかの運用設計。Vault誤用パターンと回避策も整理する。 |
| 第19回 | OSのメジャーバージョンアップ時におけるPlaybookの互換性検証 | Terraform側でベースイメージ(例:Ubuntu 22.04→24.04)を変更した際、Ansibleの古い独自モジュールやシェルコマンドが機能しなくなる課題。 |
| 第20回 | 運用編まとめ:ミュータブル運用とイミュータブル運用の折衷案 | 状態(データ)を維持し続ける運用(ミュータブル)と、使い捨てる運用(イミュータブル)を、両ツールの組み合わせでどのように着地させるかの最適解。 |
5. 初めて読む方へ
ここまで、第1部の処理フローと、そこで発生する9つのトラブルの全体像を見てきました。実際にどの回から読み進めるか迷っている方は、関心に応じて以下を参考にしてください。
- TerraformとAnsibleそれぞれの管理範囲、連携させる際の設計の基本を知りたい → 第1回
-
terraform apply直後にAnsibleを実行すると、SSH接続に失敗する → 第2回、第6回 - TerraformのJSON出力をAnsibleのインベントリとして渡すとホストが認識されない、自動生成された秘密鍵でSSH接続が拒否される → 第3回、第4回
- Terraformがリソースを再生成した後、Ansibleが古いIPアドレス・意図しないホストに接続してしまう → 第5回
- Terraformが生成したコンテナ・VMの中身(Pythonバージョン、デフォルトユーザー)がAnsible側の前提と食い違い、実行時エラーになる → 第7回、第9回
- Terraformで複数リソースを同時構築すると、Ansible側の処理が詰まって遅延する → 第8回
- 第1〜9回の解決策を統合したTerraform・Ansibleのコードをまとめて見たい → 第10回
6. 第1部 各回リンク
第1部(第1〜10回)の各回タイトルと概要の一覧です。各回はDockerコンテナを例に解説していますが、いずれもプロバイダーに依存しない構造の問題です。各回の詳細なトラブル内容・検証の手順は、リンク先の本文をご覧ください。
第1部:環境構築・連携編
| 回数 | テーマ・記事タイトル | 概要 |
|---|---|---|
| 第1回 | AnsibleとTerraformの連携目的と設計思想の違い | リソース生成(Terraform)と構成管理(Ansible)の役割分担と、連携時における設計のアンチパターンを俯瞰。冪等性シリーズ・ドリフトシリーズとの接続を示す。 |
| 第2回 | Terraform完了直後のプロビジョニング失敗を防ぐSSH待機制御 | TerraformのAPIレスポンスとターゲット側のSSHDが実際にリクエストを受け付けられる状態になるまでのタイムラグによる接続失敗と解決策。VM環境(EC2・GCP Compute Engine)でのOSブート待ち・Docker環境でのSSHD初期化待ちなど環境を問わず発生する構造として示す。 |
| 第3回 | 動的インベントリ生成時における出力データのパースエラー | TerraformのJSON出力とAnsibleが期待する動的インベントリのJSONスキーマとの構造的な差異を整理し、生のTerraform出力をそのまま渡した場合に発生する「静かな失敗」を実機で再現したうえで、変換スクリプトによる解決方法を解説する。 |
| 第4回 | 自動生成されたSSH鍵のパーミッション設定エラー | Terraformで自動生成した秘密鍵ファイルの権限設定が不適切なため、AnsibleのSSH実行時に接続を拒否されるトラブルへの対応。 |
| 第5回 | 仮想環境におけるIPアドレス変動対策 | Terraformがリソースを再生成する際に発生するIPアドレス変動を実機で検証する。terraform apply単体ではtfstateとインベントリが自動的に追従する一方、applyとAnsibleの実行タイミングが分離すると、SSH接続自体は成功するのに意図しないホストへ接続してしまう危険があることを示す。解決策として、TerraformリソースレベルでのIP固定と動的インベントリという2つのアプローチを実機で比較する。 |
| 第6回 | ネットワーク初期化完了前に発生する接続タイムアウト | Terraformでのネットワーク構築完了直後にAnsibleから接続しようとすると、ネットワークの反映待ちによってSSH接続がタイムアウトする問題を扱う。この問題は、DockerネットワークのブリッジやAWSのSecurity Group・VPCルーターの伝播ラグなど、実装方式が異なっていても「ネットワークの構築完了」と「実際に通信可能な状態」が別のタイミングであるという共通の構造から生じることを整理し、対策設計を解説する。 |
| 第7回 | 実行環境とターゲットOS間におけるPythonバージョンの不一致 | ターゲットOS内のPythonバージョンと、Ansibleを実行するコントロールノード側のPythonの乖離による実行時エラーへの対応。冪等性シリーズ第7回との接続を示す。 |
| 第8回 | 複数インスタンス同時構築時における並列処理の競合 | Terraformで複数リソースを同時生成する際、Ansible側の並列実行数(forks)の制限によって処理が遅延・競合する可能性がある問題を扱う。同時生成するリソース数が実行環境のキャパシティに対して相対的に多い場合に起こりうる構造的な問題であることを整理し、forks調整やバッチ分割といった対策設計を解説する。 |
| 第9回 | OS固有の初期ユーザーと管理者権限(sudo)への昇格エラー | コンテナイメージごとに異なるデフォルトユーザーに対し、Ansibleからbecomeを用いて権限昇格する際の設定ミスと対策。Docker・VM・クラウドを問わず同じ構造で発生することを示す。 |
| 第10回 | 環境構築編まとめ:自動連携のためのコードテンプレート化 | 第1回〜9回の課題を踏まえ、TerraformからAnsibleへ一貫して安全に処理を移譲するための実行順序と、統合したコードテンプレートを解説する。第1〜9回それぞれの解決策を集約したもので、統合した状態そのものを実機で動作確認したものではなく、実際の環境に適用する際は個別の調整が必要になる場合がある。 |
📑 連載の移動 前の記事:【Molecule編】第4回 | 次の記事:【Ansible×Terraform編】第1回
🗺️ シリーズ全体の位置づけを知りたい方はこちら
本記事は「AnsibleとTerraformの連携が壊れる理由はライフサイクルにあった」シリーズ(全50回・5部構成)のうち、第1部(第1〜10回)のまとめ記事です。
シリーズ全体(第1〜5部)の関係については、以下の統合ブログで整理しています。→ 「AnsibleとTerraformの連携が壊れる理由はライフサイクルにあった」シリーズ統合ブログ ※近日公開予定