LoginSignup
12
3

More than 1 year has passed since last update.

No More 変数汚染

Last updated at Posted at 2021-12-05

概要

長年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な実装になっています。

サンプルコード

pearents.yml
---
- 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 }}"
child1.yml
---
- ansible.builtin.debug:
    msg: "子"

- ansible.builtin.debug:
    msg: "{{ my_var }}"

- ansible.builtin.import_tasks: ./grandchild.yml

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 変数名

上記二つの方法である程度変数汚染を抑えることができますが、変数名が指し示すものがわかりづらかったり、抽象すぎたりすると効果がなかったりします。
直感的でわかりやすい名前付け、具体的な名前付けを心がけるようにしましょう。
プログラマーのみならず、様々なところで有用なスキルだと思いますので、書籍「リーダブルコード」を是非一読されることをお勧めします!

12
3
3

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
12
3