hana_shinのLinux技術ブログ

Linuxの技術情報を掲載しています。特にネットワークをメインに掲載していきます。

Ansibleの使い方(デバッグ編)



1 はじめに

本記事では、Ansibleのデバッグ方法について説明します。

2 検証環境

動作検証をするための環境は以下のとおりです。

                 192.168.122.0/24
control ------------------------------------- node1
      .220                               .87
ホスト名 役割
control コントロールノードとして動作します。Ansibleをインストールします。
node1 ターゲットノードとして動作します。モジュールが実行されるホストです

コントロールノード、ターゲットノードのAlmaLinux版数は以下のとおりです。

[root@server ~]# cat /etc/redhat-release
AlmaLinux release 9.2 (Turquoise Kodkod)

コントロールノード、ターゲットノードのカーネル版数は以下のとおりです。

[root@server ~]# uname -r
5.14.0-284.11.1.el9_2.x86_64

3 事前準備

ansibleのインストールや公開鍵認証の設定方法などは、Ansibleの使い方(モジュール編) - hana_shinのLinux技術ブログを参照してください。

4 playbookの構文をチェックする方法(–syntax-check)

–syntax-checkは、playbookの構文をチェックするオプションです。構文をチェックするだけで、playbookの実行はしません。

テスト用のplaybookを作成します。このplaybookに誤りはありません。

[root@control ~]# vi test.yml
[root@control ~]# cat test.yml
- name: Sample Playbook
  hosts: node1
  tasks:
    - name: The output of magic variables
      ansible.builtin.debug:
        msg: The name of playbook is {{ ansible_play_name }}

--syntax-checkオプションを付けて、playbookを実行します。

[root@control ~]# ansible-playbook -i hosts.ini test.yml --syntax-check

playbook: test.ym

意図的にmsgの開始位置を左にずらして、debugモジュールと同じ位置にしてみます。

[root@control ~]# vi test.yml
[root@control ~]# cat test.yml
- name: Sample Playbook
  hosts: node1
  tasks:
    - name: The output of magic variables
      ansible.builtin.debug:
      msg: The name of playbook is {{ ansible_play_name }}

--syntax-checkオプションを付けて、playbookを実行すると、構文エラーが発生していることがわかります。

[root@control ~]# ansible-playbook -i hosts.ini test.yml --syntax-check
ERROR! conflicting action statements: debug, msg

The error appears to be in '/root/test.yml': line 4, column 7, but may
be elsewhere in the file depending on the exact syntax problem.

The offending line appears to be:

  tasks:
    - name: The output of magic variables
      ^ here

5 デバッグメッセージを表示する方法(-v,-vv...)

-vはデバッグメッセージを出力するオプションです。vの個数は最大6個(-vvvvvv)まで指定できます。-vがデバックメッセージを最も簡潔に出力します。vの数が多くなると、デバッグメッセージをより詳細に出力します。-vvvからネットーワークに関するデバッグメッセージが出力されます。

-vを指定してplaybookを実行すると、デバッグメッセージが出力されていることがわかります。

[root@control ~]# ansible-playbook -i hosts.ini test.yml -v
Using /etc/ansible/ansible.cfg as config file

PLAY [Sample Playbook] ******************************************************************************************************************

TASK [Gathering Facts] ******************************************************************************************************************
ok: [192.168.122.220]

TASK [Execute the hostname command] *****************************************************************************************************
changed: [192.168.122.220] => {"changed": true, "cmd": ["/usr/bin/hostname"], "delta": "0:00:00.007782", "end": "2024-02-24 22:11:47.767717", "msg": "", "rc": 0, "start": "2024-02-24 22:11:47.759935", "stderr": "", "stderr_lines": [], "stdout": "node1", "stdout_lines": ["node1"]}

PLAY RECAP ******************************************************************************************************************************
192.168.122.220            : ok=2    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0

[root@control ~]#

6 タスク毎に実行を確認する方法(--step)

--stepは、Ansible Playbookを実行する際に、各タスクごとにユーザーに対して処理を続けるか中断するかを確認するオプションです。このオプションを使用すると、Playbookの実行中に各タスクが開始される前に、ユーザーに確認メッセージが表示されます。

テスト用のplaybookを作成します。

[root@control ~]# vi test.yml
[root@control ~]# cat test.yml
- name: Sample Playbook
  hosts: node1
  tasks:
    - name: Display nodename
      ansible.builtin.debug:
        msg: "The name of the remote host is {{ ansible_facts.nodename }}"
    - name: Display kernel version
      ansible.builtin.debug:
        msg: "The kernel version of the remote host is {{ ansible_facts.kernel }}"

--stepオプションを指定してplaybookを実行します。各タスクで以下のいずれかを押下します。
・n:タスクを実行しません。
・y:タスクを実行します。
・c:最後のタスクまで自動で実行します。
以下の例では、最初にyを押下して、リモートホストからファクトを収集します。その後、2回ともyを押下してタスクを実行しています。ちなみに、ファクトの収集に対してNを押下すると、リモートホストからホスト名等のファクトを入手できないので、その後のタスクの実行は失敗します。

[root@control ~]# ansible-playbook -i hosts.ini test.yml --step

PLAY [Sample Playbook] ***********************************************************************************
Perform task: TASK: Gathering Facts (N)o/(y)es/(c)ontinue: y

Perform task: TASK: Gathering Facts (N)o/(y)es/(c)ontinue: ***********************************************

TASK [Gathering Facts] ***********************************************************************************
ok: [192.168.122.220]
Perform task: TASK: Display nodename (N)o/(y)es/(c)ontinue: y

Perform task: TASK: Display nodename (N)o/(y)es/(c)ontinue: **********************************************

TASK [Display nodename] **********************************************************************************
ok: [192.168.122.220] => {
    "msg": "The name of the remote host is node1"
}
Perform task: TASK: Display kernel version (N)o/(y)es/(c)ontinue: y

Perform task: TASK: Display kernel version (N)o/(y)es/(c)ontinue: ****************************************

TASK [Display kernel version] ****************************************************************************
ok: [192.168.122.220] => {
    "msg": "The kernel version of the remote host is 5.14.0-284.11.1.el9_2.x86_64"
}

PLAY RECAP ***********************************************************************************************
192.168.122.220            : ok=3    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0

[root@control ~]#

7 debugモジュールを使う方法

debugは、playbookの実行中にメッセージを表示し、変数や式のデバッグするためのモジュールです。詳細は、ansible.builtin.debug module -- Print statements during execution — Ansible Core Documentationを参照してください。

7.1 msgパラメータを使う方法

マジック変数を標準出力に出力するplaybookファイルを作成します。マジック変数とは、Ansible がシステム内の状態を反映する変数です。ユーザーはマジック変数に値を設定することはできません。ここでは、playbookの名前を保持するマジック変数(ansible_play_name)の値を出力してみます。マジック変数は、特別な変数 — Ansible Documentationを参照してください。

[root@control ~]# vi test.yml
[root@control ~]# cat test.yml
- name: Sample Playbook
  hosts: node1
  tasks:
    - name: The output of magic variables
      ansible.builtin.debug:
        msg: The name of playbook is {{ ansible_play_name }}

playbookを実行すると、プレイブックの名前(Sample Playbook)が表示れていることがわかります。

[root@control ~]# ansible-playbook -i hosts.ini test.yml

PLAY [Sample Playbook] *****************************************************************************************************

TASK [Gathering Facts] *****************************************************************************************************
ok: [192.168.122.220]

TASK [The output of magic variables] ***************************************************************************************
ok: [192.168.122.220] => {
    "msg": "The name of playbook is Sample Playbook"
}

PLAY RECAP *****************************************************************************************************************
192.168.122.220            : ok=2    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0

[root@control ~]#

7.2 varパラメータを使う方法

次の内容のplaybookファイルを作成します。commandモジュールに対してhostnameコマンドをパラメータとして渡し、registerディレクティブを使ってhostnameコマンドの実行結果をresult変数に格納します。そして、その後のタスクでdebugモジュールを実行してhostnameコマンドの実行結果を表示します。

[root@control ~]# vi test.yml
[root@control ~]# cat test.yml
- name: Sample Playbook
  hosts: node1
  tasks:
    - name: Execute the hostname command
      shell: /usr/bin/hostname
      register: result
    - name: Print return information from the previous task
      ansible.builtin.debug:
        var: result

playbookを実行すると、hostnameコマンドの実行結果が表示れていることがわかります。

[root@control ~]# ansible-playbook -i hosts.ini test.yml

PLAY [Sample Playbook] ******************************************************************************************************************

TASK [Gathering Facts] ******************************************************************************************************************
ok: [192.168.122.220]

TASK [Execute the hostname command] *****************************************************************************************************
changed: [192.168.122.220]

TASK [Print return information from the previous task] **********************************************************************************
ok: [192.168.122.220] => {
    "result": {
        "changed": true,
        "cmd": "/usr/bin/hostname",
        "delta": "0:00:00.011434",
        "end": "2024-02-24 20:57:19.969301",
        "failed": false,
        "msg": "",
        "rc": 0,
        "start": "2024-02-24 20:57:19.957867",
        "stderr": "",
        "stderr_lines": [],
        "stdout": "node1",
        "stdout_lines": [
            "node1"
        ]
    }
}

PLAY RECAP ******************************************************************************************************************************
192.168.122.220            : ok=3    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0

[root@control ~]#

実行結果のうち、ホスト名だけを表示する場合、以下のようにvar変数にresult.stdoutと指定します。

[root@control ~]# vi test.yml
[root@control ~]# cat test.yml
- name: Sample Playbook
  hosts: node1
  tasks:
    - name: Execute the hostname command
      shell: /usr/bin/hostname
      register: result
    - name: Print return information from the previous task
      debug:
        var: result.stdout

playbookを実行すると、ターゲットノードのホスト名(node1)が表示れていることがわかります。

[root@control ~]# ansible-playbook -i hosts.ini test.yml

PLAY [Sample Playbook] ******************************************************************************************************************

TASK [Gathering Facts] ******************************************************************************************************************
ok: [192.168.122.220]

TASK [Execute the hostname command] *****************************************************************************************************
changed: [192.168.122.220]

TASK [Print return information from the previous task] **********************************************************************************
ok: [192.168.122.220] => {
    "result.stdout": "node1"
}

PLAY RECAP ******************************************************************************************************************************
192.168.122.220            : ok=3    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0

[root@control ~]#

7.3 verbosityパラメータを使う方法

verbosityは、出力メッセージを詳細に表示するパラメータです。しかし、以下のようなplaybookを作成して実行してみましたが、私の環境では、デバッグメッセージが表示されませんでした。

[root@control ~]# vi test.yml
[root@control ~]# cat test.yml
- name: Sample Playbook
  hosts: node1
  tasks:
    - name: Execute the hostname command
      ansible.builtin.command:
        cmd: /usr/bin/hostname
      register: var_hostname

    - name: Display hostname command result
      ansible.builtin.debug:
        var: var_hostname
        verbosity: 1

playbookを実行します。デバッグメッセージは表示されませんでした。

[root@control ~]# ansible-playbook -i hosts.ini test.yml

PLAY [Sample Playbook] ******************************************************************************************************************

TASK [Gathering Facts] ******************************************************************************************************************
ok: [192.168.122.220]

TASK [Execute the hostname command] *****************************************************************************************************
changed: [192.168.122.220]

TASK [Display hostname command result] **************************************************************************************************
skipping: [192.168.122.220]

PLAY RECAP ******************************************************************************************************************************
192.168.122.220            : ok=2    changed=1    unreachable=0    failed=0    skipped=1    rescued=0    ignored=0

[root@control ~]#

8 failモジュールを使う方法

failは、特定の条件が満たされた場合に、Playbookの実行を中止し、エラーメッセージを表示するモジュールです。

8.1 ファイルの状態を使った例

以下の処理を実行するplaybookを作成します。
・/tmp/test.txtが存在する場合:エラーメッセージ(File does't exist)を表示しない
・/tmp/test.txtが存在しない場合:エラーメッセージ(File does't exist)を表示する
なお、statモジュールについては、Ansibleの使い方(モジュール編) - hana_shinのLinux技術ブログを参照してください。

(1) その1(条件に一致しない場合)

[root@control ~]# vi test.yml
[root@control ~]# cat test.yml
- name: Sample Playbook
  hosts: node1
  tasks:
    - name: Check the status of /tmp/test.txt
      ansible.builtin.stat:
        path: /tmp/test.txt
      register: result

    - name: Fail if /tmp/test.txt does not exist
      ansible.builtin.fail:
        msg: "File doesn't exist"
      when: not result.stat.exists

ターゲットノードでテスト用のファイルを作成します。

[root@node1 ~]# touch /tmp/test.txt

playbookを実行します。/tmp/test.txtが存在するので、エラーメッセージが表示されないことがわかります。

[root@control ~]# ansible-playbook -i hosts.ini test.yml

PLAY [Sample Playbook] *************************************************************************************************************

TASK [Gathering Facts] *************************************************************************************************************
ok: [192.168.122.220]

TASK [Check the status of /tmp/test.txt] *******************************************************************************************
ok: [192.168.122.220]

TASK [if /tmp/test.txt does not exist] *********************************************************************************************
skipping: [192.168.122.220]

PLAY RECAP *************************************************************************************************************************
192.168.122.220            : ok=2    changed=0    unreachable=0    failed=0    skipped=1    rescued=0    ignored=0

[root@control ~]#

(2) その2(条件に一致する場合)
ターゲットノードでテスト用ファイルを削除します。

[root@node1 ~]# rm /tmp/test.txt
rm: 通常の空ファイル '/tmp/test.txt' を削除しますか? y

playbookを実行します。/tmp/test.txtが存在しないので、エラーメッセージが表示されていることがわかります。

[root@control ~]# ansible-playbook -i hosts.ini test.yml

PLAY [Sample Playbook] *************************************************************************************************************

TASK [Gathering Facts] *************************************************************************************************************
ok: [192.168.122.220]

TASK [Check the status of /tmp/test.txt] *******************************************************************************************
ok: [192.168.122.220]

TASK [if /tmp/test.txt does not exist] *********************************************************************************************
fatal: [192.168.122.220]: FAILED! => {"changed": false, "msg": "File does't exist"}

PLAY RECAP *************************************************************************************************************************
192.168.122.220            : ok=2    changed=0    unreachable=0    failed=1    skipped=0    rescued=0    ignored=0

[root@control ~]#

8.2 ファイルサイズを使った例

以下の処理を実行するplaybookを作成します。
・ファイルサイズが3バイト未満の場合:ファイルサイズを表示してplaybookを中断する
・ファイルサイズが3バイト以上の場合:正常終了する

(1) その1(条件に一致する場合)
以下の処理を実行するplaybookを作成します。
・ファイルサイズが3バイト未満の場合:ファイルサイズを表示してplaybookを中断する

[root@control ~]# vi test.yml
[root@control ~]# cat test.yml
- name: Sample Playbook
  hosts: node1
  tasks:
    - name: Check the status of /tmp/test.txt
      ansible.builtin.stat:
        path: /tmp/test.txt
      register: result

    - name: Fail if /tmp/test.txt size is less than 3 bytes
      ansible.builtin.fail:
        msg: "File size is {{ result.stat.size }} bytes"
      when: result.stat.size < 3

ターゲットノードでテスト用のファイル(2byte)を作成します。

[root@node1 ~]# echo 0 > /tmp/test.txt
[root@node1 ~]# ls -l /tmp/test.txt
-rw-r--r--. 1 root root 2  2月 25 22:22 /tmp/test.txt

playbookを実行します。ファイルサイズが2バイトである旨を表示して、playbookを終了していることがわかります。

[root@control ~]# ansible-playbook -i hosts.ini test.yml

PLAY [Sample Playbook] *************************************************************************************************************

TASK [Gathering Facts] *************************************************************************************************************
ok: [192.168.122.220]

TASK [Check the status of /tmp/test.txt] *******************************************************************************************
ok: [192.168.122.220]

TASK [if /tmp/test.txt does not exist] *********************************************************************************************
fatal: [192.168.122.220]: FAILED! => {"changed": false, "msg": "File size is 2 byte "}

PLAY RECAP *************************************************************************************************************************
192.168.122.220            : ok=2    changed=0    unreachable=0    failed=1    skipped=0    rescued=0    ignored=0

[root@control ~]#

(2) その2(条件に一致しない場合)
ターゲットノードでテスト用のファイル(4byte)を作成します。

[root@node1 ~]# echo 012 > /tmp/test.txt
[root@node1 ~]# ls -l /tmp/test.txt
-rw-r--r--. 1 root root 4  2月 25 22:25 /tmp/test.txt

playbookを実行します。playbookが正常終了していることがわかります。

[root@control ~]# ansible-playbook -i hosts.ini test.yml

PLAY [Sample Playbook] *************************************************************************************************************

TASK [Gathering Facts] *************************************************************************************************************
ok: [192.168.122.220]

TASK [Check the status of /tmp/test.txt] *******************************************************************************************
ok: [192.168.122.220]

TASK [if /tmp/test.txt does not exist] *********************************************************************************************
skipping: [192.168.122.220]

PLAY RECAP *************************************************************************************************************************
192.168.122.220            : ok=2    changed=0    unreachable=0    failed=0    skipped=1    rescued=0    ignored=0

[root@control ~]#

9 assertモジュールを使う方法

assertは、条件に一致しない場合、Playbookの実行を中止しエラーメッセージを表示するモジュールです。詳細は、ansible.builtin.assert module -- Asserts given expressions are true — Ansible Core Documentationを参照してください。

以下の処理を実行するplaybookを作成します。
・ファイルサイズが3バイト未満の場合:ファイルサイズを表示してplaybookを中断する
・ファイルサイズが3バイト以上の場合:正常終了する

(1) その1(条件に一致する場合)

[root@control ~]# vi test.yml
[root@control ~]# cat test.yml
- name: Sample Playbook
  hosts: node1
  tasks:
    - name: Check the status of /tmp/test.txt
      ansible.builtin.stat:
        path: /tmp/test.txt
      register: result

    - name: Fail if /tmp/test.txt size is less than 3 bytes
      ansible.builtin.assert:
        that:
          - result.stat.size >= 3
        fail_msg: "The file size is less than 3 bytes. The process will be aborted"

ターゲットノードでテスト用のファイル(4byte)を作成します。

[root@node1 ~]# echo 012 > /tmp/test.txt
[root@node1 ~]# ls -l /tmp/test.txt
-rw-r--r--. 1 root root 4  2月 25 22:25 /tmp/test.txt

playbookを実行します。

[root@control ~]# ansible-playbook -i hosts.ini test.yml

PLAY [Sample Playbook] *************************************************************************************************************

TASK [Gathering Facts] *************************************************************************************************************
ok: [192.168.122.220]

TASK [Check the status of /tmp/test.txt] *******************************************************************************************
ok: [192.168.122.220]

TASK [if /tmp/test.txt does not exist] *********************************************************************************************
ok: [192.168.122.220] => {
    "changed": false,
    "msg": "All assertions passed"
}

PLAY RECAP *************************************************************************************************************************
192.168.122.220            : ok=3    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0

[root@control ~]#

(2) その2(条件に一致しない場合)
ターゲットノードでテスト用のファイル(2byte)を作成します。

[root@node1 ~]# echo 0 > /tmp/test.txt
[root@node1 ~]# ls -l /tmp/test.txt
-rw-r--r--. 1 root root 2  2月 25 23:14 /tmp/test.txt

playbookを実行します。

[root@control ~]#  ansible-playbook -i hosts.ini test.yml

PLAY [Sample Playbook] **************************************************************************************************

TASK [Gathering Facts] **************************************************************************************************
ok: [192.168.122.220]

TASK [Check the status of /tmp/test.txt] ********************************************************************************
ok: [192.168.122.220]

TASK [Fail if /tmp/test.txt size is less than 3 bytes] ******************************************************************
fatal: [192.168.122.220]: FAILED! => {
    "assertion": "result.stat.size >= 3",
    "changed": false,
    "evaluated_to": false,
    "msg": "The file size is less than 3 bytes. The process will be aborted"
}

PLAY RECAP **************************************************************************************************************
192.168.122.220            : ok=2    changed=0    unreachable=0    failed=1    skipped=0    rescued=0    ignored=0

[root@control ~]#

10 debuggerオプションを使う方法

debuggerオプションは、playbook実行中、デバッガを起動するオプションです。gdbやcrash等のデバッガと同じように、playbookのステップ実行や変数の値を確認したりすることができます。

playbookを作成します。このとき、"debugger: always"という1行を記載します。

[root@control ~]# vi test.yml
[root@control ~]# cat test.yml
- name: Sample Playbook
  hosts: node1
  debugger: always
  tasks:
    - name: Install the latest version of Apache
      ansible.builtin.dnf:
        name: httpd
        state: latest

playbookを実行します。以下は、hコマンドを押下して、デバッガのヘルプを表示してるところです。

[root@control ~]# ansible-playbook -i hosts.ini test.yml

PLAY [Sample Playbook] *****************************************************************************************************************

TASK [Gathering Facts] *****************************************************************************************************************
ok: [192.168.122.220]
[192.168.122.220] TASK: Gathering Facts (debug)> h

Documented commands (type help <topic>):
========================================
EOF  c  continue  h  help  p  pprint  q  quit  r  redo  u  update_task

[192.168.122.220] TASK: Gathering Facts (debug)>

ここでは、cを押下してplaybookの処理を先に進めてみます。最初のc押下でファクトの収集、2つ目のc押下でhttpdパッケージをインストールしています。

[192.168.122.220] TASK: Gathering Facts (debug)> c

TASK [Install the latest version of Apache] ********************************************************************************************
ok: [192.168.122.220]
[192.168.122.220] TASK: Install the latest version of Apache (debug)> c

PLAY RECAP *****************************************************************************************************************************
192.168.122.220            : ok=2    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0

[root@control ~]#