tech.guitarrapc.cóm

Technical updates

Ansibleプレイブックをcheckモードで実行できるようにする

Ansibleのプレイブックを実行していると、プレイブックを適用することなくおおむね動作するか確認したくなります。 Ansibleのcheckモードがそれですが、Ansibleのモジュールによってはcheckモードに対応していないものがあります。 今回はそのメモ。

checkモードとは

Ansibleのcheckモードは、プレイブックを実行することなく検証できます。 利用方法は、ansible-playbookコマンドに--checkオプションをつけるだけです。

$ ansible-playbook foo.yml --check

checkモードの理想と現実

checkモードは理想と現実にギャップがあるため利用するには注意しなくてはいけません。

  • 理想: モジュールによる副作用を対象マシンに及ぼすことなく動作確認できる
  • 現実: モジュールによってはcheckモードでも容赦なく実行される

またcheckモードでスキップされるタスクにて変数を登録している場合、後続でその変数を参照しているものは明示的にスキップする必要があります。 あるいはcheckモードでも必ず実行させることで制御するのもいいでしょう。

この3点に気をつければ割と便利に使えます。

モジュールがcheckモードに対応しているかしていないか判別する

Ansibleには多くのモジュールがありますが、そのすべてがcheckモードに対応しているわけではありません。 幸いモジュールがcheckモードに対応しているかどうかは、モジュールのウェブページに記載されています。

確認方法

Attributes表のcheck_modeを確認します。1

ansible.builtin.fileは、Attributesにcheck_mode: fullの表記があるのでcheckモードに対応しています。

ansible.builtin.fileはcheck_mode:fullなので対応している

ansible.builtin.get_urlは、Attributesにcheck_mode: partialの表記があるのでcheckモードに一部対応しています。

ansible.builtin.get_urlはcheck_mode:partialなので一部対応

checkモードでスキップされる変数定義を使う後続ステップはスキップする

変数tem_dirを定義するステップを用意したとしましょう。次のステップでtemp_dirを参照している場合、checkモードで実行するとtemp_dirは定義されていないためエラーになります。

- name: "go - Create temporary directory for download"
  ansible.builtin.tempfile:
    state: directory
    prefix: golang_install
  register: temp_dir

- name: "go - Download Go tarball"
  ansible.builtin.get_url:
    url: "https://go.dev/dl/go{{ go.version }}.linux-amd64.tar.gz"
    dest: "{{ temp_dir.path }}/go.tar.gz"
    mode: "0644"
  register: download_result
  until: download_result is not failed
  retries: 3
  delay: 2
TASK [tools : go - Download Go tarball] ****************************************
fatal: [localhost]: FAILED! => {"msg": "The task includes an option with an undefined variable.. 'dict object' has no attribute 'path'\n\nThe error appears to be in '/home/runner/work/local-provisioner/local-provisioner/envs/include_role/ubuntu/install_go.yaml': line 15, column 3, but may\nbe elsewhere in the file depending on the exact syntax problem.\n\nThe offending line appears to be:\n\n\n- name: \"go - Download Go tarball\"\n  ^ here\n"}

モジュールがcheckモードに対応していてもしていなくても変数を参照してエラーになります。 checkモードの時だけ実行しないようにするには、when: ansible_check_mode使ってcheckモード時だけスキップします。

- name: "go - Download Go tarball"
  ansible.builtin.get_url:
    url: "https://go.dev/dl/go{{ go.version }}.linux-amd64.tar.gz"
    dest: "{{ temp_dir.path }}/go.tar.gz"
    mode: "0644"
  register: download_result
  until: download_result is not failed
  retries: 3
  delay: 2
  when: not ansible_check_mode # <- checkモードのときはスキップする

checkモードでタスクを強制的に実行する

私はansible.builtin.shellansible.builtin.commandで取得した結果から変数を作ることがあります。 副作用のない処理なので、checkモードでも実行したいのですがこれらはcheckモードではスキップされてしまいます。

- name: "Get os"
  ansible.builtin.shell: set -o pipefail && uname | tr '[:upper:]' '[:lower:]'
  args:
    executable: /bin/bash
  register: os_value
  changed_when: false

checkモードでも強制実行されるようにしたいときは、check_mode: falseを指定します。

- name: "Get os"
  ansible.builtin.shell: set -o pipefail && uname | tr '[:upper:]' '[:lower:]'
  args:
    executable: /bin/bash
  register: os_value
  changed_when: false
  check_mode: false

まとめ

Ansibleはいろいろなことができるだけに、checkモードをモジュールがいい感じにやってくれるのではなくユーザーがいい感じにいじる必要があります。 柔軟だからこそのジレンマを感じるところですが、checkモードをうまく使うことでAnsibleのプレイブックをより便利に使えるようになります。

参考


  1. 同じ表からdiffモードに対応しているかも確認できます。