レッドハットでコンサルタントをしている id:nanodayo です。 赤帽エンジニア Advent Calendar 2019の8日目のエントリーです。
ご存知、Ansible のplaybookはYAML形式で記述しますが、YAMLのAnchor と Aliasも適用できるのでご紹介します。 なお、多用すると複雑なplaybookになり保守しにくくなるので強くはおすすめはしません。
YAMLのAnchorとAlias
同じような記述をたくさん書くときに便利な記法です。 以下のような仮想マシンの変数定義(vars)があり、殆ど同じパラメータを設定するので、共通設定として一箇所で書きたいとします。 (説明用に作った物なのでデータ構造は適当です。)
vars: vms: - hostname: "test0.example.com" image: "cirros" flavor: "m1.tiny" az: "az1" nics: - name: "net0" - name: "net1" - hostname: "test1.example.com" image: "cirros" flavor: "m1.tiny" az: "az1" nics: - name: "net0" - name: "net1" - hostname: "test2.example.com" image: "cirros" flavor: "m1.tiny" az: "az1" nics: - name: "net3"
このような定義の場合、例えばimage の定義を全台違うものにするには3箇所に変更が必要なってしまうので 雛形を決めて、1箇所だけで管理できるようにしたいケースでAnchorとAliasを使います。
--- - name: yaml anchor and alias test hosts: all gather_facts: no vars: vms: # Anchorに登録 - &test hostname: "test0.example.com" image: "cirros" flavor: "m1.tiny" az: "az1" nics: - name: "net0" - name: "net1" # 全く同じ内容を参照 - *test # 参照先で部分的に上書き - hostname: "test1.example.com" <<: *test # 参照先で部分的に上書き2 - hostname: "test2.example.com" # list はlistごと上書き nics: - name: "net3" <<: *test tasks: - name: display vms data debug: var: vms
実行すると以下のようなデータとして扱われていることがわかります。
TASK [display vms data] ********************************************************************************************************************** ok: [localhost] => { "vms": [ { "az": "az1", "flavor": "m1.tiny", "hostname": "test0.example.com", "image": "cirros", "nics": [ { "name": "net0" }, { "name": "net1" } ] }, { "az": "az1", "flavor": "m1.tiny", "hostname": "test0.example.com", "image": "cirros", "nics": [ { "name": "net0" }, { "name": "net1" } ] }, { "az": "az1", "flavor": "m1.tiny", "hostname": "test1.example.com", "image": "cirros", "nics": [ { "name": "net0" }, { "name": "net1" } ] }, { "az": "az1", "flavor": "m1.tiny", "hostname": "test2.example.com", "image": "cirros", "nics": [ { "name": "net3" } ] } ] }
test0.example.comの内容をひな形にして参照し、test1.example.com, test2.example.comのように参照先で定義している値で上書きされてる事がわかります。
データ構造全部まるごとAnchorに登録ではなく、部分的に使うこともできます。 部分的なAnchorを追加したサンプルは以下。
vars: vms: # Anchorに登録。部分的なものも追加 - &test hostname: "test0.example.com" image: &image "cirros" flavor: "m1.tiny" az: &az "az1" nics: &nics - name: "net0" - name: "net1" # 全く同じ内容を持ってくる - *test # 参照先で部分的に上書き - hostname: "test1.example.com" <<: *test # 参照先で部分的に上書き2 - hostname: "test2.example.com" # list はlistごと上書き nics: - name: "net3" <<: *test # 部分的なAnchor を参照 - hostname: "test3.example.com" image: *image flavor: "m1.tiny" az: *az nics: *nics
vars に限らず task の記述に対しても適用ができます。
--- - name: yaml anchor and alias test hosts: all gather_facts: no tasks: - name: sample task block block: &task_block - &task name: sample task in block 1 debug: &module msg: "sample message 1" ignore_errors: yes - name: sample task in block 2 debug: msg: "sample message 2" <<: *task - name: sample task out of block debug: <<: *module - name: sample task block(alias) block: *task_block
とまぁ一見便利な記法なのですが、Ansible playbookで使う場合に関しては
- 同じ値を一箇所で定義するだけであれば、varsの値に変数を定義すれば可能
- 特定のホスト群でのデフォルト値を設定したい場合は group vars や role default 等が登録できる。
- 複数task をまとめたものを再利用したいならファイルを分けて include_playbook などで読み出せる
と、Ansible側の機能で事足りる場合もあります。
GitLab CIでAnchor Aliasを使う
Ansible以外ですと、GitLab CIのYAMLファイルを記載する際、パイプラインの処理で共通の内容が多く定義するのにお勧めできます。 以下、GitLab CIのパイプラインの定義例です。 細かな説明はしませんが、パイプラインに必要な前処理(before_script)等が、どのパイプラインでも同じというケースが多いです。
.Unit_test_template: &unit_test_template stage: unit_test only: - staging - master image: name: unittest_container before_script: - eval $(ssh-agent -s) - echo "${CICD_SSH_PRIVATE_KEY}" | tr -d '\r' | ssh-add - > /dev/null - echo -e "Host *\n\tPort 22\n\tStrictHostKeyChecking no\n\n" > ~/.ssh/config - ansible -c local tests/unit_test_presetup.yml after_script: - ansible -c local tests/unit_test_cleanup.yml tags: - container-server Unit_test1: <<: *unit_test_template script: - bash unit_test1.sh Unit_test2: <<: *unit_test_template script: - bash unit_test2.sh
なお、GitLab側のドキュメントにもっと良いサンプルが載っています。
https://docs.gitlab.com/ce/ci/yaml/#special-yaml-features
おまけ
Anchor / Alias以外のYAMLの特徴で、Ansibleで使えるか試してみたもの紹介します。
変数の値の型を指定する
通常、シングルクォートやダブルクォート でくくっていれば文字列、そうでなければ数値として解釈されるので、あまり明示的に指定するケースはないと思われますが、以下のように値の前に !!str
等を書けば指定ができるようです。
--- - name: yaml test hosts: all gather_facts: no vars: string_test: !!str 1234 int_test: !!int 1234 tasks: - name: step1 string_test debug: msg: "{{ string_test }} is {{ string_test | type_debug }}" - name: step2 int_test debug: msg: "{{ int_test }} is {{int_test | type_debug}}" ...
実行結果
# ansible-playbook -i hosts yaml-test.yml PLAY [yaml test] ***************************************************************************************************************************** TASK [step1 string_test] ********************************************************************************************************************* ok: [localhost] => { "msg": "1234 is AnsibleUnicode" } TASK [step2 int_test] ************************************************************************************************************************ ok: [localhost] => { "msg": "1234 is int" } PLAY RECAP *********************************************************************************************************************************** localhost : ok=2 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
... を使って好きな場所で終了(はできない)
YAMLの文法上、---
が開始 ...
が終了となっています。
省略も可能なので、特にplaybookでは ...
を書かないケースが多いです。
「playbookのデバッグ時に、止めたい箇所に ...
を入れれば exit命令的に使えるのでは」と思って試しましたが、...
より後にコメントではないものを書くとsyntax errorになります。
# これは動きません --- - name: yaml test hosts: all gather_facts: no vars: string_test: !!str 1234 int_test: !!int 1234 tasks: - name: step1 string_test debug: msg: "{{ string_test }} is {{ string_test | type_debug }}" ... - name: step2 int_test debug: msg: "{{ int_test }} is {{int_test | type_debug}}"
yamllintでも同様にNGだったので、YAML的はそういう使い方を想定してないのでしょう。