はじめに
Ansibleで、あるRole内から別のRoleを呼び出したいことがあります。
例えば、処理A→処理B→処理Cというplaybookを実行したいとき、処理Bについては既存Roleが利用できる場合です。
処理AもCも単独のRoleにして、playbookから
Roles:
- RoleA
- RoleB
- RoleC
のように実行してもいいのですが、できれば分割したくない。
そんな時はRoleA+Cの中でRoleBをinclude_role
モジュールで実行すればOKです。
しかし、
[defaults]
roles_path = ./roles
で指定したroles_path以外に置いたRoleはinclude_role
では呼び出せません。
その場合、Playbookから他のPlaybookを呼び出すモジュールinclude
を使うことになります。
しかし、includeしたplaybookにtemplateモジュールなどが含まれていると少々面倒です。
実際に試してみましょう。
sample
ディレクトリ構成
ansible.cfg
site.yml
hosts/local
roles/
|--include_other_playbook/
| |--tasks/
| | |--main.yml
other_roles/
|--template_role/
| |--tasks/
| | |--main.yml
| |--templates/
| | |--template.j2
ansible.cfg
[defaults]
roles_path = ./roles
site.yml
---
- name: include_other_playbook
hosts: targethost
roles:
- role: include_other_playbook
include_other_playbook
---
- debug: msg="1st"
- name: include template_role
include: ../other_roles/template_role/tasks/main.yml
- debug: msg="2nd"
template_role
---
- name: template
template:
src: template.j2
dest: /tmp/test
owner: root
group: root
mode: '0644'
実行してみる
$ ansible-playbook -i hosts/local -l targethost site.yml
TASK [include_other_playbook : template dest=/tmp/test, src=template.j2, group=root, mode=0644, owner=root] ***
fatal: [targethost]: FAILED! => {"changed": false, "failed": true, "msg": "Unable to find 'template.j2' in expected paths."}
エラーになりました。テンプレートファイルが見つからないようです。
このtemplate_roleを単独実行すると成功するのに。。。
対処法
Ansibleでは、Role内でのcopyモジュールやtemplateモジュールのsourceディレクトリの起点は、Roleディレクトリ配下のfiles,templatesに限定されてしまいます。
しかも、includeした場合でも、実行中のRoleディレクトリ配下が参照されます。
つまり、今回の場合、include_other_playbook/templatesにもtemplate.j2を配置しないと見つけてくれません。
ansible.cfg
site.yml
hosts/local
roles/
|--include_other_playbook/
| |--tasks/
| | |--main.yml
| |--templates/
| | |--template.j2
other_roles/
|--template_role/
| |--tasks/
| | |--main.yml
| |--templates/
| | |--template.j2
しかし、これだと何のためにRoleにしているのか分からなくなってしまいます。
そこで、単独実行に影響を与えず、includeした際にパスを通す方法をご紹介します。
修正後
include_other_playbook
includeする際に、template_pathという変数を定義します。
ここにも書きましたが、相対パスを定義するためには一工夫必要です。
---
- debug: msg="1st"
- name: include template_role
include: ../template_role/tasks/main.yml
vars:
template_path: "{{ inventory_dir }}/../roles/template_role/templates/template.j2"
- debug: msg="2nd"
template_role
srcにはinclude側で指定した変数を適用します。
ただし、単独実行に影響を与えないよう、defaultフィルタでデフォルト値を設定しておきます。
---
- name: template
template:
src: "{{ template_path | default('template.j2') }}"
dest: /tmp/test
owner: root
group: root
mode: '0644'
再度実行してみる
TASK [include_other_playbook : debug msg=1st] **********************************
ok: [targethost] => {
"msg": "1st"
}
TASK [include_other_playbook : template dest=/tmp/test, src={{ template_path | default('template.j2') }}, group=root, mode=0644, owner=root] ***
ok: [targethost]
TASK [include_other_playbook : debug msg=2nd] **********************************
ok: [targethost] => {
"msg": "2nd"
}
成功しました。
まとめ
Ansibleはpath周りで融通が利かないことが多いのが難点だと思いますが、今回の方法を使えばincludeがより柔軟にできると思います。