インベントリ — 自動化の住所録
一言でいうと
Ansibleは、「何をするか」よりも「誰に対して行うか」を先に問います。その答えがインベントリです。
なぜ必要なのか
サーバーが3台のときは、ターミナルを3つ開けば済みます。7台になると、ウィンドウを間違えて選ぶ事故が起きます。ホームラボを3ノードから7ノードに増やした記録を見ると、ノードを追加する手順自体は難しくないのですが、「どのノードに何をしたのか」を人が記憶した瞬間から、ミスが始まります。実際に、新しく追加した2つのノードには、以前のクラスターの残骸(古いバージョンのkubelet、古い証明書)が残っていて、それを片付ける手順を手作業で行っていると、1台は見落とします。
インベントリは、この問題を「リストをファイルにする」ことで解決します。ファイルに書かれていないサーバーは自動化の対象ではなく、ファイルに書かれたサーバーはすべて同じ扱いを受けます。この単純なルールが、「1台だけ設定が違う」状況を構造的になくします。
どう動くのか
インベントリには、3つの要素が含まれます。
| 要素 | 何か | 例 |
|---|---|---|
| ホスト | 接続する対象1つ | web1 |
| グループ | ホストのまとまり | [web]、[db] |
| 変数 | ホスト/グループに付随する値 | ansible_port=2222、app_port=8080 |
核心は、グループが階層をなすという点です。[prod:children]の下にwebとdbを入れると、prodは2つのグループのすべてのホストを含みます。そして、すべてのホストは、自動的にallグループに属します。変数は上から下へ流れますが、より狭い範囲が勝ちます。allの値よりグループの値が、グループの値よりホストの値が優先されます。
形式は、INIとYAMLの2つです。INIは短くて手で書きやすく、YAMLは入れ子構造や複雑な変数を扱いやすいです。どちらを使っても、ansible-inventory --listで出力してみれば、結局同じJSONになります。インベントリを疑うときは、ファイルをにらみつけず、このコマンドで実際の解釈結果を見てください。タイプミス1つでグループがまるごと空になっていることを、3秒でわかります。
現場での姿
1つ目は、「なぜ1台だけうまくいかなかったのか」という状況です。対象を絞り込むときに、--limitを使わず、インベントリからホストを一時的に削除する人がいます。すると、そのファイルがそのままコミットされて、数日後にそのサーバーだけがパッチから漏れます。対象の絞り込みは、インベントリではなく、実行オプションで行います。
2つ目は、接続情報も変数だということです。ansible_host、ansible_port、ansible_userは、特別な変数のように見えますが、ただの変数です。そのため、ジャンプホストや非標準ポートのような環境の違いを、コードを変更せずにインベントリで吸収できます。このラボ環境でも、sshdが127.0.0.1:2222で起動しているので、ansible_portでその違いを扱います。
3つ目は、動的インベントリです。クラウドではサーバーの一覧が毎日変わるので、ファイルの代わりにスクリプトが一覧を作ります。それでも概念は同じです。グループと変数を含むJSONを作ってくれるだけです。静的インベントリをきちんと理解すれば、動的インベントリは、出力形式が違うだけの同じものです。
変数をどこに置くのか
インベントリファイルの中に変数をたくさん書くと、すぐに読みにくくなります。そのため、実務では変数を別のディレクトリに切り出します。インベントリの隣にgroup_vars/とhost_vars/を置くと、group_vars/web.ymlはwebグループ全体に、host_vars/web1.ymlはそのホスト1台に適用されます。ファイル名がそのまま適用範囲なので、どこを直せばよいのか迷うことがありません。
優先順位は、先ほど見たとおり、狭いほうが勝ちます。ただし、実際の順序はそれより長く、よくぶつかるものだけを整理すると、次のとおりです。
- コマンドラインの
-e(最も強い) - タスクやブロックに直接書いた値
host_vars/group_vars/(子グループが親グループに勝つ)group_vars/all- ロールのデフォルト値(
defaults/、最も弱い)
ロールのデフォルト値が最も弱いのは、意図された設計です。ロールは「この値がなければ、こうする」を書く場所であり、それを使う側が、常にオーバーライドできなければなりません。逆に、ロール内のvars/ははるかに強く、オーバーライドしにくいので、本当に変わってはいけないものにだけ使います。
ここに、実務のルールを1つ加えます。シークレットは、インベントリに平文で置きません。リポジトリにコミットされた瞬間に元に戻せず、削除しても履歴に残ります。暗号化された変数ファイルや、外部のシークレットストアから取得する方式を使いますが、どちらの場合でも、シークレットでない値とファイルを分けておくほうがよいです。1つのファイルに混在していると、ごく普通の設定を1つ直すために毎回暗号を解かなければならず、そうしているうちに、結局誰も暗号化を使わなくなります。
次のラボですること
/root/ans/inventory/hosts.iniにweb・dbグループと、prodという上位グループを作成し、同じ構造をYAMLでも表現します。次に、アドホックコマンドで3つのホストに一度に接続し、--limitで対象を絞り込んでみます。最後に、インベントリをJSONで出力して、グループごとのホストのレポートを作成します。