Chapter 08

プレイブックの基本

y_mrok
y_mrok
2021.10.17に更新

プレイブックとは

「手順書」のことです。管理対象ノードに対して行うことを、行う順番に記載します。

構造

モジュール

プレイブックを構成する最小単位は「モジュール (module) 」です。モジュール単体では何もできません。

タスク

モジュールのパラメーターに値を設定してはじめてモジュールは実行可能になります。実行可能になったモジュールを「タスク (task) 」と呼びます。タスクには固有の「タスク名」を設定します。

プレイ

1 つ以上のタスクを行う順番に並べ、対象のグループや管理対象ノード、変数などを加えたものを「プレイ (play) 」と呼びます。プレイには固有の「プレイ名」を設定します。

プレイブック

1 つ以上のプレイを行う順番に並べたものを「プレイブック (playbook)」と呼びます。

プレイ内の 4 つのセクション

プレイは 4 つのセクションで構成します。

  • targets セクション
  • vars セクション
  • tasks セクション
  • handlers セクション

targets セクションと tasks セクションは必須です。 vars セクションと handlers セクションは必要なときに定義します。

targets セクション

プレイの実行対象になるグループや管理対象ノードを指定します。targets セクションで主に使用するディレクティブです。

ディレクティブ 説明
name プレイ名を指定する
hosts
(必須)
プレイの実行対象になるグループや管理対象ノードを指定する
become プレイを管理者権限で実行するか否かを指定する
・yes[1] → 管理者権限で実行する
・no   → 管理者権限で実行しない (デフォルト)
gather_facts 管理対象ノードのファクト変数を収集するか否かを指定する
・yes → ファクト変数を収集する (デフォルト)
・no  → ファクト変数を収集しない
- name: Install Apache.
  hosts: takeyamachi
  become: yes
  gather_facts: no

vars セクション

vars で宣言した後に、プレイ内で使用する変数を定義します。

  vars:
    apache_package_name: httpd
    apache_service_name: httpd.service

tasks セクション

tasks でセクションの開始を宣言します。「手順書」のメインのである管理対象ノードで実行するタスクを実行順に記述します。

ディレクティブ 説明
name タスク名を指定する
実行する手順 (タスク) の内容です。
  1. タスク Install the httpd package.
    • Apache のパッケージ (httpd) をインストールする
    • インストールしたときは tasks セクションの実行後に handlers セクションのタスクを実行する
  2. タスク Install the firewalld package.
    • ファイアウォールのパッケージ (firewalld) をインストールする
  3. タスク Start "firewalld.service".
    • firewall のサービスを開始する

タスクの実行時に「手順の作業は実行済みか?」を自動的に補完します。

  tasks:
    - name: Install the httpd package.
      ansible.builtin.yum:
        name: "{{ apache_package_name }}"
        state: present
      notify:
        - start apache service

    - name: Install the firewalld package.
      ansible.builtin.yum:
        name: firewalld
        state: present

    - name: Start "firewalld.service".
      ansible.builtin.systemd:
        name: firewalld.service
        enabled: yes
        state: started

handlers セクション

handlers でセクションの開始を宣言します。このセクションのタスクは tasks セクション内のタスクの実行が終了した後、tasks セクションのタスクの実行状況により実行したり、実行しなかったします。

ディレクティブ 説明
name タスク名を指定する
実行する手順 (タスク) の内容です。今回は tasks セクションで Apache のパッケージ (httpd) がインストールされたときに限って実行します。
  • タスク Change the listening port.
    • Apache の設定ファイルを変更し、待ち受け port を 8080 にする
  • タスク Start "httpd.service".
    • Apache のサービスを開始する
  • タスク Drill a hole for the http port.
    • ファイアウォールに待ち受け port : 8080 の穴を開ける

タスクの実行時に「手順の作業は実行済みか?」を自動的に補完します。

  handlers:
    - name: Change the listening port.
      ansible.builtin.lineinfile:
        path: /etc/httpd/conf/httpd.conf
        regexp: "^Listen "
        line: "Listen 8080"
        validate: httpd -t -f %s 
      listen:
        - start apache service

    - name: Start "httpd.service".
      ansible.builtin.systemd:
        name: "{{ apache_service_name }}"
        enabled: yes
        state: started
      listen:
        - start apache service

    - name: Drill a hole for the http port.
      ansible.posix.firewalld:
        port: "{{ http_port }}/tcp"
        permanent: yes
        immediate: yes
        state: enabled
      listen:
        - start apache service

プレイブックを実行

ansible-playbook コマンド

プレイブックを実行します。

構文

ansible-playbook [-h] [--version] [-v] [-k]
                 [--private-key PRIVATE_KEY_FILE] [-u REMOTE_USER]
                 [-c CONNECTION] [-T TIMEOUT]
                 [--ssh-common-args SSH_COMMON_ARGS]
                 [--sftp-extra-args SFTP_EXTRA_ARGS]
                 [--scp-extra-args SCP_EXTRA_ARGS]
                 [--ssh-extra-args SSH_EXTRA_ARGS] [--force-handlers]
                 [--flush-cache] [-b] [--become-method BECOME_METHOD]
                 [--become-user BECOME_USER] [-K] [-t TAGS]
                 [--skip-tags SKIP_TAGS] [-C] [--syntax-check] [-D]
                 [-i INVENTORY] [--list-hosts] [-l SUBSET]
                 [-e EXTRA_VARS] [--vault-id VAULT_IDS]
                 [--ask-vault-password | --vault-password-file VAULT_PASSWORD_FILES]
                 [-f FORKS] [-M MODULE_PATH] [--list-tasks]
                 [--list-tags] [--step] [--start-at-task START_AT_TASK]
                 playbook [playbook ...]

主なパラメーター

パラメーター 説明
playbook 実行するプレイブックファイルを指定する
-i インベントリーファイルを指定する
--list-tasks プレイブックを実行せず、実行対象のタスクの一覧 ( タスク名、設定したタグ 等 ) を表示する
-t
--tags
指定したタグを設定したタスクを実行する
--skip-tags 指定したタグを設定したタスクを除いて実行する

実行例

次のプレイブックファイルを実行します。

hosts.yml
---
all:
  hosts:
    marutamachi:
  children:
    web:
      hosts:
        takeyamachi:
        ebisugawa:
        nijyo:
    mail:
      hosts:
        nijyo:
    database:
      hosts:
        oshikoji:
        oike:
group_vars/all.yml
---
ansible_user: vagrant
ansible_password: vagrant
group_vars/web.yml
---
http_port: 8080
host_vars/takeyamachi.yml
---
ansible_host: 192.168.111.102
install_apache.yml
- name: Install Apache.
  hosts: takeyamachi
  become: yes
  gather_facts: no

  vars:
    apache_package_name: httpd
    apache_service_name: httpd.service

  tasks:
    - name: Install the httpd package.
      ansible.builtin.yum:
        name: "{{ apache_package_name }}"
        state: present
      notify:
        - start apache service

    - name: Install the firewalld package.
      ansible.builtin.yum:
        name: firewalld
        state: present

    - name: Start "firewalld.service".
      ansible.builtin.systemd:
        name: firewalld.service
        enabled: yes
        state: started

  handlers:
    - name: Change the listening port.
      ansible.builtin.lineinfile:
        path: /etc/httpd/conf/httpd.conf
        regexp: "^Listen "
        line: "Listen 8080"
        validate: httpd -t -f %s 
      listen:
        - start apache service

    - name: Start "httpd.service".
      ansible.builtin.systemd:
        name: "{{ apache_service_name }}"
        enabled: yes
        state: started
      listen:
        - start apache service

    - name: Drill a hole for the http port.
      ansible.posix.firewalld:
        port: "{{ http_port }}/tcp"
        permanent: yes
        immediate: yes
        state: enabled
      listen:
        - start apache service

実行結果です。

y_mrok@ctrl:~/code/exam4$ ansible-playbook -i hosts.yml install_apache.yml 

PLAY [Install Apache.] *********************************************************

TASK [Install the httpd package.] **********************************************
changed: [takeyamachi]

TASK [Install the firewalld package.] ******************************************
ok: [takeyamachi]

TASK [Start "firewalld.service".] **********************************************
changed: [takeyamachi]

RUNNING HANDLER [Change the listening port.] ***********************************
changed: [takeyamachi]

RUNNING HANDLER [Start "httpd.service".] ***************************************
changed: [takeyamachi]

RUNNING HANDLER [Drill a hole for the http port.] ******************************
changed: [takeyamachi]

PLAY RECAP *********************************************************************
takeyamachi                : ok=6    changed=5    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   

y_mrok@ctrl:~/code/exam4$ 

このプレイブックは Apache (httpd) のインストールと、http port を 8080 に変更します。プレイブックの実行後、ブラウザーで次の URL をたたくと、Apache のテストページを表示します。

http://192.168.111.102:8080/

実行結果の見方

targets セクション

プレイ名を表示します。

PLAY [Install Apache.] *********************************************************

上記では含まれていませんが、ファクト変数の収集もここで表示されます。

tasks セクション

各タスクの実行結果です。タスク名とタスクを実行した結果とタスクの実行後の管理対象ノードの状態を表示します。

実行結果 タスクの実行結果 実行後の管理対象ノードの状態
ok 成功した 変更なし
changed 成功した 何らかの変更があった
skipping 実行しなかった (skip した) 変更なし
fatal 失敗した 変更なし
"[" と "]" で括られているのはタスクを実行した管理対象ノード名です。
TASK [Install the httpd package.] **********************************************
changed: [takeyamachi]

TASK [Install the firewalld package.] ******************************************
ok: [takeyamachi]

TASK [Start "firewalld.service".] **********************************************
changed: [takeyamachi]

handlers セクション

"RUNNING HANDLER" で始まります。表示の意味は tasks セクションと同じです。

RUNNING HANDLER [Change the listening port.] ***********************************
changed: [takeyamachi]

RUNNING HANDLER [Start "httpd.service".] ***************************************
changed: [takeyamachi]

RUNNING HANDLER [Drill a hole for the http port.] ******************************
changed: [takeyamachi]

実行結果のサマリー

最後に管理対象ノードごとにタスクの実行結果のサマリーを表示します。

項目 意味
ok 実行が成功したタスク数
changed 管理対象ノードの状態を変更したタスク数
unreachable 管理対象ノードに到達できなかったタスク数
failed 実行に失敗したタスク数
skipped 実行をスキップしたタスク数
rescued rescue ディレクティブを実行した数
ignored 実行時に発生したエラーを無視したタスク数
"ok" , "changed" , "skipped" だけがカウントされていたら、プレイは正常に実行したと判断できます。
PLAY RECAP *********************************************************************
takeyamachi                : ok=6    changed=5    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   

冪等性を確認

Ansible の特徴の 1 つに冪等性があります。冪等性が保たれるよう記述したプレイブックファイルを再実行すると、結果は "ok" と "skipping" / "skipped" だけになります。上記のプレイブックファイルを再実行した結果です。結果はすべて "ok" であり、冪等性が保たれています。

y_mrok@ctrl:~/code/exam4$ ansible-playbook -i hosts.yml install_apache.yml 

PLAY [Install Apache.] *********************************************************

TASK [Install the httpd package.] **********************************************
ok: [takeyamachi]

TASK [Install the firewalld package.] ******************************************
ok: [takeyamachi]

TASK [Start "firewalld.service".] **********************************************
ok: [takeyamachi]

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

y_mrok@ctrl:~/code/exam4$ 

Ansible の各モジュールは冪等性を保たれるよう動作します。しかし、 ansible.builtin.commandansible.builtin.shell など一部モジュールは冪等性が担保されていません。

タスクの実行と管理対象ノード

次のプレイブックを実行します。

practice.yml
---
- name: Task execution and managed nodes
  hosts: all
  gather_facts: no

  tasks:
    - name: This task shall be performed by all managed nodes.
      ansible.builtin.debug:
    
    - name: Task execution fails on managed node marutamachi.
      ansible.builtin.fail:
      when: inventory_hostname == "marutamachi"

    - name: This task will be performed by all remaining managed nodes.
      ansible.builtin.debug:

    - name: Task execution fails on managed node oike.
      ansible.builtin.fail:
      when: inventory_hostname == "oike"

    - name: This task will be further performed by all remaining managed nodes.
      ansible.builtin.debug:

実行ログです。

y_mrok@ctrl:~/code/exam4$ ansible-playbook -i hosts.yml practice.yml 

PLAY [Task execution and managed nodes] ****************************************

TASK [This task shall be performed by all managed nodes.] **********************
ok: [marutamachi] => {
    "msg": "Hello world!"
}
ok: [takeyamachi] => {
    "msg": "Hello world!"
}
ok: [ebisugawa] => {
    "msg": "Hello world!"
}
ok: [nijyo] => {
    "msg": "Hello world!"
}
ok: [oshikoji] => {
    "msg": "Hello world!"
}
ok: [oike] => {
    "msg": "Hello world!"
}

TASK [Task execution fails on managed node marutamachi.] ***********************
fatal: [marutamachi]: FAILED! => {"changed": false, "msg": "Failed as requested from task"}
skipping: [takeyamachi]
skipping: [ebisugawa]
skipping: [nijyo]
skipping: [oshikoji]
skipping: [oike]

TASK [This task will be performed by all remaining managed nodes.] *************
ok: [takeyamachi] => {
    "msg": "Hello world!"
}
ok: [ebisugawa] => {
    "msg": "Hello world!"
}
ok: [nijyo] => {
    "msg": "Hello world!"
}
ok: [oshikoji] => {
    "msg": "Hello world!"
}
ok: [oike] => {
    "msg": "Hello world!"
}

TASK [Task execution fails on managed node oike.] ******************************
skipping: [takeyamachi]
skipping: [ebisugawa]
skipping: [nijyo]
skipping: [oshikoji]
fatal: [oike]: FAILED! => {"changed": false, "msg": "Failed as requested from task"}

TASK [This task will be further performed by all remaining managed nodes.] *****
ok: [takeyamachi] => {
    "msg": "Hello world!"
}
ok: [ebisugawa] => {
    "msg": "Hello world!"
}
ok: [nijyo] => {
    "msg": "Hello world!"
}
ok: [oshikoji] => {
    "msg": "Hello world!"
}

PLAY RECAP *********************************************************************
ebisugawa                  : ok=3    changed=0    unreachable=0    failed=0    skipped=2    rescued=0    ignored=0   
marutamachi                : ok=1    changed=0    unreachable=0    failed=1    skipped=0    rescued=0    ignored=0   
nijyo                      : ok=3    changed=0    unreachable=0    failed=0    skipped=2    rescued=0    ignored=0   
oike                       : ok=2    changed=0    unreachable=0    failed=1    skipped=1    rescued=0    ignored=0   
oshikoji                   : ok=3    changed=0    unreachable=0    failed=0    skipped=2    rescued=0    ignored=0   
takeyamachi                : ok=3    changed=0    unreachable=0    failed=0    skipped=2    rescued=0    ignored=0   

y_mrok@ctrl:~/code/exam4$ 

必ず 1 つのタスクをすべての管理対象ノードが実行した後に、次のタスクの実行に移動します。タスクの実行に失敗した管理対象ノードは、そのタスクで実行対象から除外します。
具体的に見ていきます。各タスクの管理対象ノードの数に着目してください。

タスク 実行に成功した数 実行に失敗した数 メモ
This task shall be performed by all managed nodes. 6 0 -
Task execution fails on managed node marutamachi. 5 1 管理対象ノード marutamachi の実行が失敗した
This task will be performed by all remaining managed nodes. 5 0 -
Task execution fails on managed node oike. 4 1 管理対象ノード oike の実行が失敗した
This task will be further performed by all remaining managed nodes. 4 0 -

実行に失敗したタスクの次のタスクで実行した管理対象ノードの数が減少しました。これは、タスクの実行に失敗した管理対象ノードがその時点でプレイブックの実行を終了したからです。このように、タスクの実行に失敗した管理対象ノードはその時点で次のタスクの実行から除外されます。

演習問題

演習問題はこのリンクをクリックしてください。

脚注
  1. yes / no は true / false と書き直しても同じ動作をします。本書では yes / no を使用します。 ↩︎