はじめに
Ansibleを2.3から2.4にバージョンアップしたら既存のPlaybookが動かなくなった!
group_vars/host_varsに定義してある変数が読み込めてないらしい。
"VARIABLE IS NOT DEFINED!"
Ansible2.3までは問題なく読み込めていたのに。。。
原因はgroup_vars
とhost_vars
のディレクトリのパスでした。
個人的にハマったので書いておきます。
Summary
-
Ansible2.3->2.4でgroup_vars,host_varsの参照パスが変わった
- 2.3まで
- 起点となるplaybook(site.yml)からの相対パス
- 2.4以降
- インベントリファイルからの相対パス
- 公式ドキュメント
- 2.3まで
-
group_vars/host_varsにグループ名/ホスト名のサブディレクトリを置くと、配下の任意のファイルに定義した変数を読み込める
本題
問題が発生した環境
ディレクトリ構成
インベントリを環境ごとに分けているので、inventories
配下に環境名
としてインベントリファイルを置いていました。
group_varsやhost_varsなどのファイルの内容はイメージです。Roleは省略。
├── ansible.cfg
├── group_vars
│ ├── web.yml
│ ├── db.yml
│ └── ...
├── host_vars
│ ├── staging-web01.yml
│ ├── staging-db01.yml
│ └── ...
├── inventories
│ ├── staging
│ ├── production
│ └── ...
├── playbooks
│ ├── web.yml
│ ├── db.yml
│ └── ...
└── site.yml
ansible.cfg
[defaults]
hash_behaviour = merge
gathering = false
[privilege_escalation]
become = True
インベントリ
[web]
staging-web01
[db]
staging-db01
group_vars
---
my_vars:
nginx:
server_name: www.example.com
host_vars
---
my_vars:
db:
host: staging-db01
user: dbuser
pass: STAGING_PASS
実行コマンド
$ ansible-playbook -i inventories/staging -l web site.yml
実行するとエラー
"VARIABLE IS NOT DEFINED!"
検証
結論から言うと、公式ドキュメントに書いてありました。
Similar functionality can still be achieved by using vars_files, include_vars, or group_vars and host_vars placed relative to the inventory file.
インベントリファイルからの相対パスになった模様。。。
また、こちらには
As an advanced use case, you can create directories named after your groups or hosts, and Ansible will read all the files in these directories in lexicographical order.
group_vars/host_varsディレクトリ配下にグループ名/ホスト名のサブディレクトリがあれば、その中に置いた複数のファイルの読み込みも可能とあります。これは知らなかった。
せっかくなのであわせて各バージョンでの挙動を調べてみます。
検証用環境
検証用の環境は以下の通り。
- 起点となるplaybook(site.yml)と同じ階層にインベントリファイルと
group_vars
,host_vars
ディレクトリを置く - サブディレクトリにインベントリファイルと
group_vars
,host_vars
ディレクトリを置く
上記2パターンを同居させ、それぞれに書いたvarsがどう読み込まれるか試してみました。
それぞれの変数には読み込まれたファイルがわかるように、自身のパスを書いておきます。
ディレクトリ構成
├── ansible.cfg
├── group_vars
│ ├── group1
│ │ └── any.yml
│ └── group1.yml
├── host_vars
│ ├── host1
│ │ └── any.yml
│ └── host1.yml
├── hosts
├── inventories
│ ├── group_vars
│ │ ├── group1
│ │ │ └── any.yml
│ │ └── group1.yml
│ ├── host_vars
│ │ ├── host1
│ │ │ └── any.yml
│ │ └── host1.yml
│ └── hosts
├── playbooks
│ └── load_vars_test.yml
└── site.yml
ansible.cfg
[defaults]
display_skipped_hosts = True
display_args_to_stdout = True
hash_behaviour = merge
gathering = false
[privilege_escalation]
become = True
インベントリ(hosts, inventories/hosts)
[group1]
host1 ansible_host=localhost
site.yml
---
- include: playbooks/load_vars_test.yml
playbook
debug
モジュールで、実行時に読み込んだgroup_varsとhost_varsを表示させます。
---
- name: load vars test
hosts: all
tasks:
- name: load group_vars
debug: var=groupvar
- name: load host_vars
debug: var=hostvar
site.ymlと同階層のgroup_vars
---
groupvar: group_vars/group1.yml
site.ymlと同階層のgroup_vars(グループ名サブディレクトリ配下のファイル)
---
groupvar: group_vars/group1/any.yml
site.ymlと同階層のhost_vars
---
hostvar: host_vars/host1.yml
site.ymlと同階層のhost_vars(ホスト名サブディレクトリ配下のファイル)
---
hostvar: host_vars/host1/any.yml
インベントリファイルを置いたサブディレクトリのgroup_vars
---
groupvar: inventories/group_vars/group1.yml
インベントリファイルを置いたサブディレクトリのgroup_vars(グループ名サブディレクトリ配下のファイル)
---
groupvar: inventories/group_vars/group1/any.yml
インベントリファイルを置いたサブディレクトリのhost_vars
---
hostvar: inventories/host_vars/host1.yml
インベントリファイルを置いたサブディレクトリのhost_vars(ホスト名サブディレクトリ配下のファイル)
---
hostvar: inventories/host_vars/host1/any.yml
group_vars/host_varsの参照パスについて検証
group_vars/host_vars内のサブディレクトリは一旦リネームして除外しておきます。
1. インベントリファイルをsite.ymlと同じディレクトリに格納している場合
Ansible2.3
$ ansible-playbook -i hosts site.yml
PLAY [load vars test] **********************************************************
TASK [load group_vars var=groupvar] ********************************************
ok: [host1] => {
"groupvar": "group_vars/group1.yml"
}
TASK [load host_vars var=hostvar] **********************************************
ok: [host1] => {
"hostvar": "host_vars/host1.yml"
}
site.ymlと同階層のgroup_vars/host_varsが読み込まれました。
Ansible2.4以降
$ ansible-playbook -i hosts site.yml
PLAY [load vars test] **********************************************************
TASK [load group_vars var=groupvar] ********************************************
ok: [host1] => {
"groupvar": "group_vars/group1.yml"
}
TASK [load host_vars var=hostvar] **********************************************
ok: [host1] => {
"hostvar": "host_vars/host1.yml"
}
こちらもsite.ymlと同階層のgroup_vars/host_varsが読み込まれました。
2.3と2.4以降で結果的に同じ挙動になります。
2. インベントリファイルをサブディレクトリに格納している場合
Ansible2.3
$ ansible-playbook -i inventories/hosts site.yml
PLAY [load vars test] **********************************************************
TASK [load group_vars var=groupvar] ********************************************
ok: [host1] => {
"groupvar": "group_vars/group1.yml"
}
TASK [load host_vars var=hostvar] **********************************************
ok: [host1] => {
"hostvar": "host_vars/host1.yml"
}
先ほどと同じく、site.ymlと同階層のgroup_vars/host_varsが読み込まれました。
つまりインベントリファイルのパスは無関係ということです。
Ansible2.4以降
$ ansible-playbook -i inventories/hosts site.yml
PLAY [load vars test] **********************************************************
TASK [load group_vars var=groupvar] ********************************************
ok: [host1] => {
"groupvar": "inventories/group_vars/group1.yml"
}
TASK [load host_vars var=hostvar] **********************************************
ok: [host1] => {
"hostvar": "inventories/host_vars/host1.yml"
}
たしかにインベントリファイルを置いたサブディレクトリ内のgroup_vars/host_varsが読み込まれています。
group_vars/host_varsにサブディレクトリを作り、group_varsを記載したファイルを置いた場合の検証
サブディレクトリはgroup_vars/GROUP_NAME
,host_vars/HOST_NAME
のように、グループ名/ホスト名
にする必要があります。サブディレクトリ内のファイル名は任意です。
$ ansible-playbook -i hosts site.yml
PLAY [load vars test] **********************************************************
TASK [load group_vars var=groupvar] ********************************************
ok: [host1] => {
"groupvar": "group_vars/group1/any.yml"
}
TASK [load host_vars var=hostvar] **********************************************
ok: [host1] => {
"hostvar": "host_vars/host1/any.yml"
}
サブディレクトリに置いたファイルの変数が読み込まれています。
ちなみに、この挙動はver2.3〜2.7まで共通でした。
ここで、group_vars/host_varsのサブディレクトリをグループ名/ホスト名からリネームすると
$ ansible-playbook -i hosts site.yml
PLAY [load vars test] **********************************************************
TASK [load group_vars var=groupvar] ********************************************
ok: [host1] => {
"groupvar": "group_vars/group1.yml"
}
TASK [load host_vars var=hostvar] **********************************************
ok: [host1] => {
"hostvar": "host_vars/host1.yml"
}
group_vars/host_varsのファイルが読み込まれました。サブディレクトリの方が優先度が高いようです。
まとめ
Ansible2.3
- トップレベルplaybookのディレクトリからの相対パス
- インベントリファイルのパスは関係ない
- group_vars,host_varsディレクトリのサブディレクトリ(グループ名/ホスト名)のYAMLファイル(ファイル名は任意)が優先、なければ直下のYAMLファイル(グループ名/ホスト名)を読む
group_vars
- TOPLEVEL-PLAYBOOK-DIR/group_vars/GROUP_NAME/ANY.yml
- TOPLEVEL-PLAYBOOK-DIR/group_vars/GROUP_NAME.yml
host_vars
- TOPLEVEL-PLAYBOOK-DIR/host_vars/HOST_NAME/ANY.yml
- TOPLEVEL-PLAYBOOK-DIR/host_vars/HOST_NAME.yml
Ansible2.4以降(2.5,2.6,2.7も同様の挙動)
- インベントリファイルのディレクトリからの相対パス
group_vars
- INVENTORYFILE-DIR/group_vars/GROUP_NAME/ANY.yml
- INVENTORYFILE-DIR/group_vars/GROUP_NAME.yml
host_vars
- INVENTORYFILE-DIR/host_vars/HOST_NAME/ANY.yml
- INVENTORYFILE-DIR/host_vars/HOST_NAME.yml