インベントリをディレクトリに移したらグループが半分しか出てこなかった
目標
インベントリソースが複数あるとき、何がマージされて何が勝つのかを、1つずつ実測します。最後に、「この変数は結局どの値が勝つのか」に答えてくれるツールを、自分で作成します。
なぜ重要なのか
インベントリがファイル1つのときは、読めばわかります。ところが、ファイル1つで済む組織はまれです。共通のホストはプラットフォームチームが、サービスごとのホストは各チームが管理し、本番とステージングはファイルを分け、クラウドのリソースは動的ソースが生成します。そうなると、事故が2種類起きます。同じグループ名のせいで見当違いのサーバーが対象に入ってくること、そして、古いファイルや新しいgroup_varsのせいで見当違いの値が勝つことです。どちらも、ファイルを読んで推論する方式では防げません。防ぐ方法は1つだけです。マージされた結果を、実行前に機械に尋ねることです。このラボは、その質問を8回投げかけ、最後に、その質問を代わりに投げかけてくれるツールを残します。
ステップ
/root/ans/inv/src/base.iniにwebグループ(web1、web2)と[web:vars] tier=baseを、/root/ans/inv/src/extra.iniにwebグループ(web3)・dbグループ(db1)・[web:vars] tier=extraを書いてください。すべてのホストはansible_host=127.0.0.1で、base.iniの[all:vars]にansible_port=2222とansible_user=rootを置きます。-iを2回指定して(base.iniが先)マージされた結果を/root/ans/inv/out/merged.jsonに、グラフを/root/ans/inv/out/merged-graph.txtに保存し、マージ後のweb1のtierが何かを、/root/ans/inv/out/two-sources.txtにtier=<값>の1行で書いてください(プレースホルダーは値です)。/root/ans/inv/dir/を作成して、ステップ1の2つのファイルと同じ内容を、拡張子なしで10-baseと20-extraとして入れてください。そこに、/root/ans/inv/dir/30-late.iniをもう1つ置きますが、このファイルはwebグループにweb9を入れ、[web:vars] tier=lateを書きます。-i /root/ans/inv/dirでマージされた結果を/root/ans/inv/out/dir-merged.jsonに保存し、/root/ans/inv/out/dir-order.txtに2行を書いてください。1行目はweb1のtierをtier=<값>で、2行目は、ディレクトリを走査するときにスキップされたファイル名をskipped=<파일이름>で書きます(プレースホルダーは値とファイル名です)。/root/ans/inv/dir/group_vars/all.ymlにpool: gv-allとowner: platformを、/root/ans/inv/dir/group_vars/web.ymlにpool: gv-webを、/root/ans/inv/dir/host_vars/web1.ymlにpool: hv-web1を書いてください。そして、/root/ans/inv/out/levels.jsonに、3つのホストが最終的に持つpoolの値を、web1・web2・db1の3つのキーのJSONで書いてください。/root/ans/inv/dir/20-extraの[web:vars]にregion=inline-extraを追加し、/root/ans/inv/dir/group_vars/web.ymlにregion: file-webを追加してください。そして、/root/ans/inv/out/inline-vs-file.txtに2行を書いてください。1行目は、web2が最終的に持つ値をregion=<값>で、2行目は、その値がどこから来たのかを、source=inlineまたはsource=group_varsのどちらかで書きます(プレースホルダーは値です)。/root/ans/inv/prio/hosts.iniを作成してください。ホストapp1がalphaとzuluの2つのグループの両方に属し、[alpha:vars]にstack=from-alpha、[zulu:vars]にstack=from-zuluを書きます。そして、[alpha:vars]にansible_group_priority=10を追加して、alphaが勝つようにしてください。/root/ans/inv/out/priority-before.txtには、その行がないときに勝つ値を、/root/ans/inv/out/priority-after.txtには、その行があるときに勝つ値を、それぞれstack=<값>の1行で書いてください(プレースホルダーは値です)。/root/ans/inv/tree/hosts.iniを作成してください。webグループにweb1、dbグループにdb1を入れて、2つのグループをzprodの子としてまとめます。[zprod:vars]にenv=from-zprodとowner=platformを、[web:vars]にenv=from-webを書いてください。そして、/root/ans/inv/out/children.txtに3行を、辞書順のまま書いてください。db1.env=<값>・web1.env=<값>・web1.owner=<값>です(プレースホルダーは値です)。/root/ans/inv/pat/hosts.iniを作成してください。webグループにweb1・web2・web3、dbグループにdb1を入れ、2つのグループをprodの子としてまとめ、stagingグループにweb3とdb1を入れます。そして、web:&prod:!stagingパターンに合うホスト名だけを、1行に1つずつ辞書順に/root/ans/inv/out/pattern-hosts.txtに保存し、グラフを/root/ans/inv/out/pat-graph.txtに保存してください。/root/ans/inv/whowins.shを書いてください。引数を3つ受け取ります。インベントリのパス・ホスト名・変数名です。そのホストが最終的に持つ値を<변수>=<값>の1行で出力し、その変数が定義されていなければ、標準エラー出力に理由を書いて、0以外の値で終了する必要があります(プレースホルダーは変数と値です)。そのツールで、/root/ans/inv/out/merge-report.txtを4行で作成してください。dirのweb1のtier、dirのweb1のpool、dirのweb2のregion、prio/hosts.iniのapp1のstackの順です。
参考
- 作業ディレクトリは
/root/ans/invです。このラボは、プレイブックを実行しません。インベントリを解釈するコマンドだけを使います。 - 3つの問い:
--graphは構造を、--listは全体をJSONで、--hostは1つのホストの最終的な変数を、答えます。 - よくある間違い: ディレクトリの中に、拡張子が付いたインベントリファイルを置いて、なぜ読み込まれないのかわからず迷うことです。
- よくある間違い: 値をファイルから目で探して書くことです。ソースが2つ以上あると、目では間違えます。
- よくある間違い: パターンの結果が空なのに、エラーではないので、何の対象もないまま緑で終わることです。
- インベントリの作成・ホストパターン・ansible-inventoryコマンド・変数の使用・設定項目の一覧
ソースを2つ指定すると、何がマージされるか
/root/ans/inv/src/base.iniにwebグループ(web1、web2)と[web:vars] tier=baseを、/root/ans/inv/src/extra.iniにwebグループ(web3)・dbグループ(db1)・[web:vars] tier=extraを書いてください。すべてのホストはansible_host=127.0.0.1で、base.iniの[all:vars]にansible_port=2222とansible_user=rootを置きます。-iを2回指定して(base.iniが先)マージされた結果を/root/ans/inv/out/merged.jsonに、グラフを/root/ans/inv/out/merged-graph.txtに保存し、マージ後のweb1のtierが何かを、/root/ans/inv/out/two-sources.txtにtier=<값>の1行で書いてください(プレースホルダーは値です)。
ホストとグループは和集合になり、同じ名前の変数は、あとから読まれたソースが勝ちます。マージされた結果を見るコマンドは、--listと--graphの2つで、1つのホストの最終的な変数だけを見るには、--hostを使います。値を推測して書かず、実際に尋ねて書いてください。採点ツールも、同じ方法で再計算して突き合わせます。
ディレクトリを指定すると、ファイル名が順序を決める
/root/ans/inv/dir/を作成して、ステップ1の2つのファイルと同じ内容を、拡張子なしで10-baseと20-extraとして入れてください。そこに、/root/ans/inv/dir/30-late.iniをもう1つ置きますが、このファイルはwebグループにweb9を入れ、[web:vars] tier=lateを書きます。-i /root/ans/inv/dirでマージされた結果を/root/ans/inv/out/dir-merged.jsonに保存し、/root/ans/inv/out/dir-order.txtに2行を書いてください。1行目はweb1のtierをtier=<값>で、2行目は、ディレクトリを走査するときにスキップされたファイル名をskipped=<파일이름>で書きます(プレースホルダーは値とファイル名です)。
ディレクトリ内のファイルは、名前の順に読まれます。ところが、3つのうち1つは読み込まれません。そのファイルが入れようとしたホストが結果にあるかどうかを見れば、すぐにわかります。設定項目inventory_ignore_extensionsのデフォルトの一覧を、ansible-config listで確認してみてください。ファイル名だけを書きます(パスではありません)。
グループ変数とホスト変数が作る3つの層
/root/ans/inv/dir/group_vars/all.ymlにpool: gv-allとowner: platformを、/root/ans/inv/dir/group_vars/web.ymlにpool: gv-webを、/root/ans/inv/dir/host_vars/web1.ymlにpool: hv-web1を書いてください。そして、/root/ans/inv/out/levels.jsonに、3つのホストが最終的に持つpoolの値を、web1・web2・db1の3つのキーのJSONで書いてください。
これらのディレクトリは、インベントリの隣にあって初めて読み込まれます。広いものから狭いものへの順に上書きされ、狭いものが勝ちます。全員にかかるもの、そのグループだけにかかるもの、そのホストだけにかかるものです。3つの値を手で推論せず、ホストごとに尋ねて書いてください。
インベントリファイル内のグループ変数が最も弱い
/root/ans/inv/dir/20-extraの[web:vars]にregion=inline-extraを追加し、/root/ans/inv/dir/group_vars/web.ymlにregion: file-webを追加してください。そして、/root/ans/inv/out/inline-vs-file.txtに2行を書いてください。1行目は、web2が最終的に持つ値をregion=<값>で、2行目は、その値がどこから来たのかを、source=inlineまたはsource=group_varsのどちらかで書きます(プレースホルダーは値です)。
同じディレクトリにあっても、2つの場所の強さは異なります。公式の優先順位の一覧で、inventory file group varsとinventory group_varsのどちらが下にあるかが、答えです。web1ではなくweb2で尋ねれば、ホスト変数に隠されません。
同じ深さのグループは辞書順、それをひっくり返すつまみ
/root/ans/inv/prio/hosts.iniを作成してください。ホストapp1がalphaとzuluの2つのグループの両方に属し、[alpha:vars]にstack=from-alpha、[zulu:vars]にstack=from-zuluを書きます。そして、[alpha:vars]にansible_group_priority=10を追加して、alphaが勝つようにしてください。/root/ans/inv/out/priority-before.txtには、その行がないときに勝つ値を、/root/ans/inv/out/priority-after.txtには、その行があるときに勝つ値を、それぞれstack=<값>の1行で書いてください(プレースホルダーは値です)。
つまみがないときのデフォルトのルールは、名前です。辞書順にマージされて、後ろが勝ちます。その値を確認するには、その行を除いたコピーを一時的に作成して尋ねればよいです(元のファイルはそのままにしてください)。この変数は、インベントリソースの中に書いて初めて効きます。group_varsファイルに書いても、何も起こりません。
子グループが親グループに勝つ
/root/ans/inv/tree/hosts.iniを作成してください。webグループにweb1、dbグループにdb1を入れて、2つのグループをzprodの子としてまとめます。[zprod:vars]にenv=from-zprodとowner=platformを、[web:vars]にenv=from-webを書いてください。そして、/root/ans/inv/out/children.txtに3行を、辞書順のまま書いてください。db1.env=<값>・web1.env=<값>・web1.owner=<값>です(プレースホルダーは値です)。
親の名前を、わざと辞書順で後ろに来るようにしました。そうすることで、勝った理由が名前ではなく深さであることが明らかになります。親は広く、子は狭く、常に狭いものが勝ちます。子が定義していない変数は、親の値がそのまま下りてきます。3つの値をすべて、尋ねて書いてください。
パターンで対象を絞り込み、目で確認する
/root/ans/inv/pat/hosts.iniを作成してください。webグループにweb1・web2・web3、dbグループにdb1を入れ、2つのグループをprodの子としてまとめ、stagingグループにweb3とdb1を入れます。そして、web:&prod:!stagingパターンに合うホスト名だけを、1行に1つずつ辞書順に/root/ans/inv/out/pattern-hosts.txtに保存し、グラフを/root/ans/inv/out/pat-graph.txtに保存してください。
パターンの記号は、和集合・積集合・差集合の3つです。対象の一覧だけを取り出してくれるオプションがありますが、その出力の1行目は件数で、ホスト名ではありません。空白も一緒に入ってくるので、整えて保存してください。差集合は、結果が空でもエラーではないので、実行前に目で数える習慣が重要です。
この変数は結局どの値が勝つのか: ツールにする
/root/ans/inv/whowins.shを書いてください。引数を3つ受け取ります。インベントリのパス・ホスト名・変数名です。そのホストが最終的に持つ値を<변수>=<값>の1行で出力し、その変数が定義されていなければ、標準エラー出力に理由を書いて、0以外の値で終了する必要があります(プレースホルダーは変数と値です)。そのツールで、/root/ans/inv/out/merge-report.txtを4行で作成してください。dirのweb1のtier、dirのweb1のpool、dirのweb2のregion、prio/hosts.iniのapp1のstackの順です。
ファイルをgrepして答えると、group_varsも、グループの優先順位も、子グループも見えません。マージされた結果を出力するコマンドに、直接尋ねてください。値がないことと、値が空文字列であることを区別するには、まずキーがあるかどうかを確認する必要があります。採点ツールは、自分で作成したインベントリでもこのツールを実行してみて、定義されていない変数を尋ねたときにどう終了するかも見ます。