Ansibleで既存環境に手を加える時、あらかじめどのような変更が加わるかを把握できればいいですよね。
そんな時に使えるのが、check modeです。
check modeは簡単?
使い方はとても簡単、コマンドに--checkをつけるだけです。
$ ansible-playbook -i hosts site.yml --check
既存の本番環境にAnsibleを流す前はcheck modeでまず実行すれば、変更箇所を簡単に把握することができる!
じゃあ作ったPlaybookに何でもかんでも--checkを付ければ完璧!というわけにはいきません。
check modeを使いたい時は、Playbookの作り方に気を付けましょう。
shell/commandモジュールのcheck modeで気を付けたいパターン
1.変更予定箇所がskippedと判定されてしまうパターン
これは基本的なことなのでみんな知っているかもしれませんが、一応。
shell:とcommand:タスクはcheck modeの場合Ansibleの仕様により無条件でskippedとなります。
そのため、以下のPlaybookをcheck modeで実行した場合、changedにはなりません。
- shell: /usr/bin/some-command
register: version
ignore_errors: yes
check modeの場合:
PLAY RECAP ***************************************************************************************************
192.168.1.2 : ok=0 changed=0 unreachable=0 failed=0 skipped=1 rescued=0 ignored=0
通常実行の場合:
PLAY RECAP ***************************************************************************************************
192.168.1.2 : ok=0 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
これは、shell/commandモジュールがcheck modeに対応していないためですね。
skippedになるのは仕様であるため、changed_when等を付けても意味はありません。
そういうものだと思って受け入れましょう。
2.変更予定かskip予定かわからないパターン
前述のパターンの場合は常に実行されると思っておけばいいですが、以下のようにshell:タスクに実行条件を指定するパターンはまた違います。
これは(Ansibleの画面上では)タスクが実際には実行されるのかスキップされるのかわかりません。
- name: バージョンの確認
shell: /usr/bin/some-command --version
register: version
changed_when: no
ignore_errors: yes
check_mode: no
- name: 上書きインストール
shell: /tmp/some-software/install.sh
when: >
version.stdout is defined
and '1.0' not in version.stdout
これを実行した場合、versionに登録されたバージョンが1.0であろうとなかろうと、check modeで実行した場合の「上書きインストール」は必ずskippedとなります。
果たして/tmp/some-software/install.shが実行されるのかどうかは、--checkを外して実際にPlaybookを流すまでわかりません。
対処としては、以下のようにshell:タスクを別ファイルにする書き方が考えられます。
私の我流なので、もっといいやり方があるかもしれません。
- name: バージョンの確認
shell: /usr/bin/some-command --version
register: version
changed_when: no
ignore_errors: yes
check_mode: no
- name: 上書きインストール処理をimport
import_tasks: 2-ok-2.yml
when: >
version.stdout is defined
and '1.0' not in version.stdout
- name: 上書きインストール
shell: /tmp/some-software/install.sh
こうしておけば、versionが1.0だった場合はそもそも処理フローが「上書きインストール」 を通らないので、--checkを外した場合でも「上書きインストール」は実行されないと判断することができます。
3.変更されない箇所がchanged(またはskipped)に判定されてしまうパターン
また別のパターンとして、以下を考えてみましょう。
あるコマンドの実行結果を元にfile:タスクを実行するPlaybookがあったとします。
- name: バージョンの確認
shell: /usr/bin/some-command --version
register: version
changed_when: no
ignore_errors: yes
- name: 既存バージョンの削除
file:
name: /some/dir
state: absent
when: >
version.stdout is defined
and '1.0' not in version.stdout
このPlaybookはcheck modeでは期待通りの動作しません。
なぜならば、check modeではshellはskippedになり、変数versionには何も格納されないからです。
たとえ1.0のソフトがすでにインストールされていたとしても、check modeではインストールされていないとみなされて「既存バージョンの削除」がchangedになります。
え?削除されちゃうの!?と焦ることになります。
この問題を回避するためには、タスクにcheck_modeオプションを設定します。
check_mode: noが設定された場合、そのタスクだけ--checkを無視して強制実行することができます。
先ほどのPlaybookの場合、以下のように修正すればOKです。
- name: バージョンの確認
shell: /usr/bin/some-command --version
register: version
changed_when: no
ignore_errors: yes
check_mode: no
- name: 既存バージョンの削除
file:
name: /some/dir
state: absent
when: >
version.stdout is defined
and '1.0' not in version.stdout
changed_when: noとcheck_mode: noを設定した場合、check modeであっても実行されて、かつchangedにすらならないので実行するコマンドが絶対にシステムに影響を与えないことを担保する必要があります。
長々と書きましたが
Ansibleでは冪等性という考え方があるので、そこをちゃんと考慮したPlaybookを作ってさえいればcheck modeでいくつchangedになろうがいくつskippedになろうが、最終的には同じ状態に収束するはずなのです。
なのでcheck modeはあくまでおまけ程度に考えておいた方がいいのかもしれません。
冪等性を担保するためにはそれはそれで色々考える必要があるのですが、それはまた別の機会に書きます。
参考
https://docs.ansible.com/ansible/latest/user_guide/playbooks_checkmode.html
https://medium.com/opsops/understanding-ansibles-check-mode-299fd8a6a532