TT Lab
はじめる
学ぶ 学習パス コース

Ansible実戦

変数の優先順位 — 表を覚える前に測ってみる

TT Labで続きを見る

一言でいうと

Ansibleで同じ名前の変数を定義できる場所は22か所あり、その順序を覚えるよりも、場所を減らす設計のほうが、事故を確実に防ぎます。

なぜ必要なのか

「確かにgroup_varsにポートを8080と書いたのに、9090と表示される」。こうした報告は、ほとんどいつも同じ形をしています。誰かが急いでいるときに、コマンドラインに-eを付けて実行し、その事実がどこにも残らなかったのです。あるいは、ロールの中にvars/main.ymlがあり、それがプレイのvars_filesに勝っていました。

この問題が難しい理由は、間違った値がエラーとして現れないからです。プレイブックは成功し、ファイルは作られ、ただ内容が違っています。構文チェックもリントも、これを捕まえられません。捕まえられるのは、「今この場所で、この名前が何に解決されるか」を実際に測ることだけです。

そのため、このモジュールは、公式ドキュメントの一覧を書き写す代わりに、同じ名前を1か所ずつ増やして仕込みながら、勝者がどう変わるかを測定します。一度測ったことのある人は、表を覚えなくても、次に同じ状況になったとき、何を最初に疑うべきかがわかります。

どう動くのか

公式ドキュメントは、場所を低いものから高いものまで22個並べています。ラボで測定する12か所を、低いものから書くと次のとおりです。

順位 場所 性格
1 ロールのdefaults/main.yml 「外から上書きしてください」と差し出す値
2 インベントリのgroup_vars/all すべてのホストのベースの値
3 インベントリのgroup_vars/<그룹> グループごとの値
4 インベントリのhost_vars/<호스트> ホストごとの値
5 プレイのvars: このプレイだけ
6 プレイのvars_files: このプレイが読み込んだファイル
7 ロールのvars/main.yml 「外から触らないでください」というロール内部の値
8 ブロックのvars: ブロックの中だけ
9 タスクのvars: タスク1つだけ
10 set_fact 実行中に立てた値
11 ロール呼び出しの引数 ロールを呼び出すときに渡した値
12 コマンドラインの-e 何にでも勝つ

表の山括弧の中の韓国語はプレースホルダーで、順にグループ名とホスト名です。

ここで驚く場所が3つあります。

1つ目は、vars_filesがプレイのvars:より高いことです。同じプレイの中で、すぐ上に書いたvars:が、下で読み込んだファイルに負けます。順序を目で読む感覚とは逆です。

2つ目は、ロールのdefaultsとvarsが正反対だということです。同じロールのディレクトリの中に並んでいますが、defaultsは一番下、varsはずっと上です。この違いが、そのままロール作者の意図です。外から変更してよい値はdefaultsに、変更してはいけない値はvarsに置きます。他人のロールを使っていて、「いくら上書きしても変わらない」なら、その値はvars/main.ymlにあります。

3つ目は、範囲が狭いほど高いということです。プレイ < ブロック < タスクの順序は、覚えるものではなくルールです。狭い場所に書いた人のほうが、より具体的な意図を持っていると見るのです。

測定に使うツールも知っておく価値があります。インベントリの中での勝負は、ansible-inventory --listが、すでにマージされた結果をJSONで見せてくれます。プレイの中での勝負は、debugタスクを1つ挟んで実行してみるのが最も速いです。

ansible-inventory -i inventory --list   # 인벤토리 세 자리가 합쳐진 결과
ansible-inventory -i inventory --graph  # 그룹 구조

インベントリをディレクトリとして渡すときにはまる落とし穴が1つあります。ディレクトリの中の.ini拡張子のファイルは、デフォルトの設定では無視されます(INVENTORY_IGNORE_EXTS)。ファイル1つを-iで渡したときにはうまく動いたhosts.iniが、ディレクトリの中に入った瞬間に消えるのです。ホストが1つも見えなければ、まずこれを疑います。

現場での姿

1つ目は、ディクショナリはマージされないことです。svc_limits: {cpu: "1", memory: 1Gi}を低い場所に置き、高い場所でsvc_limits: {memory: 2Gi}を渡すと、結果は{memory: 2Gi}です。cpuは消えます。マージしたければ、combineフィルターで明示する必要があります。全体設定のhash_behaviour = mergeに変える方法もありますが、その設定はリポジトリ全体の動作を変えるので、公式ドキュメントも推奨していません。

2つ目は、-eは元に戻せないことです。コマンドラインの値はどの場所よりも高く、プレイブックの中で上書きする方法がありません。便利で使い始めると、結局「誰がいつ何を渡したのか」が記録から消えます。インシデント対応用にだけ使い、使った事実を残すチームのルールが必要です。

3つ目は、場所を減らす設計です。表を知っていることよりも強い防御は、同じ名前を仕込む場所を減らすことです。よくあるルールは3つです。環境ごとの値は、インベントリのgroup_varsの1か所だけに置く、ロールは、外から変更してもよい値だけをdefaultsに置く、-eは例外的な状況にだけ使う、というものです。この3つを守れば、優先度の表を知らなくても、事故はほとんど起きません。

4つ目は、vars_promptです。実行するときに人に尋ねて受け取る場所もあります(プレイのvars:とvars_files:の間)。自動化パイプラインでは、実行が止まってしまうので、CIに入るプレイブックには使いません。このラボも自動採点なので、扱いません。

次のラボですること

svc_tierという名前1つを12か所に1か所ずつ増やして仕込みながら、勝者がどう変わるかを自分で測ります。インベントリの3か所はansible-inventory --listで、残りはプレイブックを実行して出力物で確認します。最後に、測定した順序を人が読める表にして残し、ディクショナリがマージされないことと、combineでマージすることを並べて確認します。

参考ドキュメント