もうすぐ Ansible 2.5 がリリースされますね。僕もそろそろ Ansible 2.5 Porting Guide とか読み始めました。
ところで、僕は Ansible のバージョンが上がった最初のリリースでつまづくことが多くて、どうしてもしばらく様子見してしまいます。例えば、Ansible 2.4.0 では aws_s3 モジュールに不具合があって、既存の playbook が動かなくなったりしました(下記)。
しかし、いくら自分が注意していても、そういう経験の無い人が気軽に Ansible のバージョンを上げてしまい、あとから「動かなくなったんだけど」と言われることもあります。そんなわけで、playbook 側から実行環境の Ansible のバージョンを指定できないか?と考えてみたら、うまく動いたので紹介します。
対象読者
- Ansible 2.x.0 を基本的に信用していない
- 同じ Ansible playbook を操作・編集する人が自分以外にもいる
- ある程度動作確認が終わってから、Ansible のバージョンを上げたい
実現方法
Ansible では、always
という特別なタグを付けたタスクは「毎回必ず呼ばれるタスク」として扱われます。そこで、Ansible のバージョンをチェックするタスクを、この「毎回必ず呼ばれるタスク」として playbook に登録すれば、今回やりたいことを実現できます。
There is a special
always
tag that will always run a task, unless specifically skipped (--skip-tags always
) Tags — Ansible Documentation
実行環境の Ansible のバージョン番号は、ansible_version
という変数から参照できます。例えば、debug モジュールでこの変数を参照すると、以下のように出力されます。
- debug: msg="{{ ansible_version }}"
TASK [common : debug] ************************************************************************************************ ok: [localhost] => { "msg": { "full": "2.4.3.0", "major": 2, "minor": 4, "revision": 3, "string": "2.4.3.0" } }
バージョン番号の4番目は個別に取得できませんが、そこまで確認する機会は考えにくいので大丈夫でしょう。
具体例
どの playbook からも常に呼ばれるロールを作ります。この例では "common" という名前にします。
まず、固定したいバージョンを表す変数 expected_ansible_version を作ります。今回は、Ansible 2.4.0 だけは使われたくないので、2.4.1 以降なら許すということにします。
roles/common/vars/main.yml
--- expected_ansible_version: major: 2 minor: 4 revision: 1
そして、この変数と ansible_version を比較するタスクを作ります。
roles/common/tasks/main.yml
--- - name: Ansible major & minor version check fail: msg: - Expected Ansible version is {{ expected_ansible_version.major }}.{{ expected_ansible_version.minor }}, but actual version is {{ ansible_version.major }}.{{ ansible_version.minor }} when: not (expected_ansible_version.major == ansible_version.major and expected_ansible_version.minor == ansible_version.minor) run_once: True tags: always - name: Ansible revision check fail: msg: - Expected Ansible version is {{ expected_ansible_version.major }}.{{ expected_ansible_version.minor }}.{{ expected_ansible_version.revision }}+, but actual version is {{ ansible_version.full }} when: not (expected_ansible_version.major == ansible_version.major and expected_ansible_version.minor == ansible_version.minor and expected_ansible_version.revision <= ansible_version.revision) run_once: True tags: always
この例では、revision(バージョン番号の3番目)が想定より大きい場合は許しています。また、エラーメッセージをわかりやすくするために、タスクを2個に分けていますが、Ansible revision check
の方だけでも十分です。
あとは、その環境のすべての playbook で、ロールの先頭にこの common を追加すれば OK です。
playbook1.yml
--- - hosts: all roles: - common
こうすると、ansible-playbook コマンドでタグを指定してもしなくても、必ずバージョンチェックが実行されます。when でチェックを実行しているので、チェックに成功すると "skipping" と表示されます(この表示は若干わかりにくいので、他にいい方法があったら教えてください)。
$ ansible-playbook -i inventory -c local playbook1.yml PLAY [all] *********************************************************************************************************** TASK [Gathering Facts] *********************************************************************************************** ok: [localhost] TASK [common : Ansible major & minor version check] ****************************************************************** skipping: [localhost] TASK [common : Ansible revision check] ******************************************************************************* skipping: [localhost] TASK [common : Example task 1] *************************************************************************************** ok: [localhost] => { "msg": "Example task 1 is executed." } TASK [common : Example task 2] *************************************************************************************** ok: [localhost] => { "msg": "Example task 2 is executed." } PLAY RECAP *********************************************************************************************************** localhost : ok=3 changed=0 unreachable=0 failed=0 $ ansible-playbook -i inventory -c local playbook1.yml --tags=tag1 PLAY [all] *********************************************************************************************************** TASK [Gathering Facts] *********************************************************************************************** ok: [localhost] TASK [common : Ansible major & minor version check] ****************************************************************** skipping: [localhost] TASK [common : Ansible revision check] ******************************************************************************* skipping: [localhost] TASK [common : Example task 1] *************************************************************************************** ok: [localhost] => { "msg": "Example task 1 is executed." } PLAY RECAP *********************************************************************************************************** localhost : ok=2 changed=0 unreachable=0 failed=0
メジャーバージョンかマイナーバージョンが合わないと処理が止まります。例えば、Ansible 2.4.3 以上を指定したのに Ansible 2.5 で実行されると、以下のように表示されます。
$ ansible-playbook -i inventory -c local playbook1.yml --tags=tag1 PLAY [all] *********************************************************************************************************** TASK [Gathering Facts] *********************************************************************************************** ok: [localhost] TASK [common : Ansible major & minor version check] ****************************************************************** fatal: [localhost]: FAILED! => {"changed": false, "msg": ["Expected Ansible version is 2.4, but actual version is 2.5"]} to retry, use: --limit @/Users/myoshiz/devel/ansible_version/playbook1.retry PLAY RECAP *********************************************************************************************************** localhost : ok=1 changed=0 unreachable=0 failed=1
同じく、リビジョンが小さすぎても止まります。例えば、Ansible 2.4.3 以上を指定したのに Ansible 2.4.0 で実行されると、以下のように表示されます。
$ ansible-playbook -i inventory -c local playbook1.yml --tags=tag1 PLAY [all] *********************************************************************************************************** TASK [Gathering Facts] *********************************************************************************************** ok: [localhost] TASK [common : Ansible major & minor version check] ****************************************************************** skipping: [localhost] TASK [common : Ansible revision check] ******************************************************************************* fatal: [localhost]: FAILED! => {"changed": false, "msg": ["Expected Ansible version is 2.4.3+, but actual version is 2.4.0.0"]} to retry, use: --limit @/Users/myoshiz/devel/ansible_version/playbook1.retry
まとめ
Ansible は頻繁にバージョンアップされるので、特に問題がないなら、最新版に追従したほうがいいのは確かです。Ansible 2.5.0 を安心して使う気になれるまでの一時しのぎとして、よかったら試してみてください。
余談:Ansible 2.5 での仕様変更
Ansible 2.5 Porting Guide の冒頭に書いてありますが、include_tasks に付けられた属性(タグなど)の扱いが変わるようです。以下の記事で紹介した事象は、Ansible 2.4 でのみ発生する一時的な問題だったみたいですね。
2018-03-13追記
Ansible のバージョンチェックは1回で十分なので、run_once: True
を付けました(参考)。