ロールに分割した処理を呼び出すときに、ホストグループ等に依存しないロール内で使用する変数を呼び出し元で定義することで、プログラミングにおける関数コールの引数みたいなことをやってみました。
唯一無二のやり方というわけでなく、最初に思いついたのがこの方法だった、という内容です。
- ベースの内容
- playbookで変数指定
- ロール毎に変数指定
- デフォルト値の定義
- で、これで何するの?
- 入力チェックについて(5/19追記)
- 「role (and include_role) params」について(5/19追記)
ベースの内容
プレイブック
playbook.ymlは以下の通り。
--- - hosts: localhost gather_facts: false roles: - sample_role
sample_roleというロールを呼び出すプレイブックです。
ロール
roles/sample_role/tasks/main.ymlは以下の通り。
--- - name: hello curry world debug: msg: "hello {{ args }}"
hello {{ args }}をprintします。
ただし、このプレイブックトロールだけだとargsが未定義なのでエラーになります。
(実行時に-e指定するという手もあるけど)
playbookで変数指定
ここで、ベースのプレイブックを次のように変更。
--- - hosts: localhost gather_facts: false vars: args: curry roles: - sample_role
すると実行結果は
TASK [sample_role : hello curry world] ***************************************** ok: [localhost] => msg: hello curry PLAY RECAP ********************************************************************* localhost : ok=1 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0 [zaki@manager-dev sample-ansible]$
この通り、呼び出し元のplaybookでセットしたcurryという文字列が参照できている。
呼び出し元のplaybookで定義忘れに気を付ければ、ひとまずこれで実現はできました。
ロール毎に変数指定
プレイでなくロールに変数指定するには
- hosts: localhost gather_facts: false roles: - role: sample_role vars: args: curry - role: sample_role vars: args: carbonara
これでsample_roleは2回実行されるけど、引数はそれぞれcurryとcarbonaraになる。
TASK [sample_role : hello curry world] ***************************************** ok: [localhost] => msg: hello curry TASK [sample_role : hello curry world] ***************************************** ok: [localhost] => msg: hello carbonara PLAY RECAP ********************************************************************* localhost : ok=2 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
デフォルト値の定義
といってもロール使用時に変数定義し忘れることもあるし、未設定の場合はデフォルトの値を指定を使用するようにしてみるには。
roles/sample_role/defaults/main.ymlというパスでファイルを作成。
--- args: ramen
これで、argsがどこにも設定されてなかった場合に上記ファイルからramenという文字列が参照されるようになる。
プレイブックの
vars: args: curry
を削除してansible-playbookを実行すると
TASK [sample_role : hello curry world] ***************************************** ok: [localhost] => msg: hello ramen PLAY RECAP ********************************************************************* localhost : ok=1 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
このとおり。
ここで重要なのは、defaultsというディレクトリに定義するということ。
ロール用の変数を定義する場合は、普通はvarsというディレクトリ(roles/sample_role/vars/main.yml)に定義しますが、ロール変数に定義してしまうと、呼び出し元のプレイブックでvarsディレクティブで変数定義しても上書きができず、ロールroles/sample_role/vars/main.ymlの値が使われるため、上の例であれば必ず出力はhello ramenになります。
これはAnsibleの変数の優先順位が規定されており、抜粋すると以下の優先度(数字が大きいほど強い)になっているため。
| 優先度 | 変数種別 |
|---|---|
| 2 | ロールのデフォルト変数 (defaults/main.yml) |
| 12 | プレイのvars変数 |
| 15 | ロール変数 (vars/main.yml) |
(※優先度20の「role (and include_role) params」が実は何者かよくわからなかった… → 後述)
ちなみにリストを見ればわかるけど最強優先度の変数は、コマンドラインで-eを使って指定するエクストラ変数。
$ ansible-playbook -i inventory.ini playbook.yml -e args=gohan
のように実行すれば、実行中に出現する{{ args }}はgohanとなる。
で、これで何するの?
Kubernetes環境だと、実際の処理対象(kubectl実行とか)のターゲットノードはmasterノード1台で、入出力の引数がmaster全ノードだったりworker全ノードだったり、「処理は一緒だけど対象ノードが異なり、実行するノードはまたそれと別の1台」みたいなことがあって、色々考えた結果こんな処理を編み出したという話。
なんだかんだでYAMLはマークアップで普通のプログラミングとは異なり、クラスの定義だとかCのヘッダファイルとかが無いので、ロールの実装を隠ぺいするのであればコメントやドキュメントなどで引数指定の仕様をどこかで明確にしておかないと、分かりづらくなる可能性が高いので注意。
入力チェックについて(5/19追記)
入力のチェックもあるほうが良いとコメントいただきました。ありがとうございます。
関数コールっぽくするなら確かに入力チェックも行う方が普通ですね。
本記事のプレイブック/ロールでは行ってませんが、assertを使えばよさそう。
一般的かどうかはよくわかりませんが王道の方法では。変数の仕様等説明は defaults/main.yml に既定値 and/or コメントで書いておいて role 側は assertive な感じで型と値のチェックは最低限入れておくと良いのではないかと (チェックないと変な値の場合に非常にまずい場合もありうるので)
— ssato (@satoru_satoh) 2020年5月18日
「role (and include_role) params」について(5/19追記)
記事内で「よくわからん」と書いてたところ、よこちさん( id:akira6592 )がまた調べてくださいました。いつもありがとうございます!
前述のプレイブック例に使ってみると
roles: - role: sample_role vars: # この"vars:"以下に書いたら args: curry # 優先度12のplay vars - role: sample_role args: carbonara # ここが優先度20のrole params
とのことでした。
ロール内でロール変数を定義している状態で実行するとこの通り。
TASK [sample_role : hello curry world] ***************************************** ok: [localhost] => msg: hello ramen TASK [sample_role : hello curry world] ***************************************** ok: [localhost] => msg: hello carbonara PLAY RECAP ********************************************************************* localhost : ok=2 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
確かにvarsを使ってると上書きはできず(ramenのまま)、varsを使わずroleと同じレベルで変数名: 値をセットすると上書きできている。
そーこーかーー!!!
roleに関するドキュメントは目を通したつもりだったけど見当たらない…どこに載ってるんだろう。