概要
長年Ansibleのコードをメンテしていると変数汚染の被害に合うことありませんか?
Ansibleに限らず一般的なプログラミングでも同様な問題が発生します。
変数汚染とは新規作成したタイミングに整合性が保たれている変数が運用していくうちにうっかり同じような変数名をつけ、思わぬバグに見舞われたりすること。
基本にして大事なことなので改めてまとめさせて頂きます。
今すぐにできるカイゼンもあるので、是非お試しください。
その1 命名規則
Ansibleに限らず、通常のプログラミングにおいても命名規則は非常に大事です。
Ansibleには変数の優先順位があるので、変数を上書きしながら柔軟に運用することができます。
しかし、その反面変数管理が煩雑になり、事故を招くことがあります。
なので、変数がどこで宣言されたのかをパッと見わかるように命名規則を設けています。
そうすることでどの優先順位の変数を扱っているのかを意識しやすくなり、同じ変数名をつけるリスクも下がる。
例えば、変数がgroup_vars/all.yml
で定義されているのか、set_fact
で定義されたのか、はたまたregister
の変数なのかがわかるようにしています。
-
group_vars/all.yml
の場合・・・トップレベルはxxx_all
group_vars/all.yml--- # 例 qiita_all: item: 12345
-
set_fact
の場合・・・タスク内で_xxx
(アンダーバー始まりの変数名)hoge_task.yml# 例 - ansible.builtin.set_fact: _qiita_item: 12345
-
register
の場合・・・_result_xxx
hoge_task.yml# 例 - ansible.builtin.command: "xxx" register: _result_item
その2 変数のスコープを限定する
先日の「Ansibleタスクの再利用性を高めるためのタスクの粒度について」のように共通処理をタスク化し、変数で振舞を変えるような機構で実装している場合はimport_tasks(include_tasks)
にvars
で変数を引き渡すことで、その変数の認識範囲がimport_tasks
内に留めることができます。
共通処理の振舞を変えるために定義した変数が想定外の場所で悪さすることを防ぐことができます。
ただし、import_tasks
内でさらにimport_tasks
している場合はそれらにも変数が適用されるのでご注意ください。
ちなみに上記の過去記事の実装では、変数汚染が発生しますので、今となれば自プロジェクト内ではNGな実装になっています。
サンプルコード
---
- name: Test
hosts: localhost
connection: local
tasks:
- ansible.builtin.import_tasks: ./child1.yml
vars:
my_var: "hogehoge"
# my_varが未定義のため、エラーになる
- ansible.builtin.debug:
msg: "{{ my_var }}"
---
- ansible.builtin.debug:
msg: "子"
- ansible.builtin.debug:
msg: "{{ my_var }}"
- ansible.builtin.import_tasks: ./grandchild.yml
---
- ansible.builtin.debug:
msg: "孫"
- ansible.builtin.debug:
msg: "{{ my_var }}"
実行結果
bash-4.2# ansible-playbook playbooks/pearents.yml
PLAY [Test] *******************************************************************************************************************
TASK [Gathering Facts] *********************************************************************************************************************
Sunday 21 November 2021 16:29:36 +0900 (0:00:00.015) 0:00:00.015 *******
ok: [localhost]
TASK [ansible.builtin.debug] ***************************************************************************************************************
Sunday 21 November 2021 16:29:36 +0900 (0:00:00.485) 0:00:00.501 *******
ok: [localhost] => {
"msg": "子"
}
TASK [ansible.builtin.debug] ***************************************************************************************************************
Sunday 21 November 2021 16:29:36 +0900 (0:00:00.018) 0:00:00.520 *******
ok: [localhost] => {
"msg": "hogehoge"
}
TASK [ansible.builtin.debug] ***************************************************************************************************************
Sunday 21 November 2021 16:29:36 +0900 (0:00:00.019) 0:00:00.539 *******
ok: [localhost] => {
"msg": "孫"
}
TASK [ansible.builtin.debug] ***************************************************************************************************************
Sunday 21 November 2021 16:29:36 +0900 (0:00:00.018) 0:00:00.558 *******
ok: [localhost] => {
"msg": "hogehoge"
}
TASK [ansible.builtin.debug] ***************************************************************************************************************
Sunday 21 November 2021 16:29:36 +0900 (0:00:00.019) 0:00:00.577 *******
fatal: [localhost]: FAILED! => {"msg": "The task includes an option with an undefined variable.
The error was: 'my_var' is undefined\n\nThe error appears to be in '/ansible/ansible/playbooks/pearents.yml': line 10,
column 7, but may\nbe elsewhere in the file depending on the exact syntax problem.\n\nThe offending line appears to be:\n\n\n - ansible.builtin.debug:\n ^ here\n"}
PLAY RECAP ***************************************************************************************************************
localhost: ok=5 changed=0 unreachable=0 failed=1 skipped=0 rescued=0 ignored=0
Sunday 21 November 2021 16:29:36 +0900 (0:00:00.008) 0:00:00.586 *******
===============================================================================
Gathering Facts --------------------------------------------------------------------------------------------- 0.49s
ansible.builtin.debug --------------------------------------------------------------------------------------- 0.02s
ansible.builtin.debug --------------------------------------------------------------------------------------- 0.02s
ansible.builtin.debug --------------------------------------------------------------------------------------- 0.02s
ansible.builtin.debug --------------------------------------------------------------------------------------- 0.02s
ansible.builtin.debug --------------------------------------------------------------------------------------- 0.01s
その3 変数名
上記二つの方法である程度変数汚染を抑えることができますが、変数名が指し示すものがわかりづらかったり、抽象すぎたりすると効果がなかったりします。
直感的でわかりやすい名前付け、具体的な名前付けを心がけるようにしましょう。
プログラマーのみならず、様々なところで有用なスキルだと思いますので、書籍「リーダブルコード」を是非一読されることをお勧めします!