Red Hat Certified Engineer (RHCE) #11 Role の作成と使用

読了 8分

#10 Ansible Vault で秘密を暗号化して安全に扱う方法を押さえたなら、今回はプレイブックを再利用可能な単位にまとめる role を扱います。role は task・handler・変数・テンプレート・ファイルを決まったディレクトリ構造にまとめ、一度書いた構成を複数のプレイブックから呼び出せるようにする単位です。RHCE 試験では「role を作成して playbook から呼び出せ」という形式がほぼ毎回出題されるので、今回の記事で構造と使い方を手に覚えさせます。

role が必要な理由 #

これまで書いてきたプレイブックは、task と handler、変数、テンプレートが 1 つのファイルやいくつかのファイルに散らばっていました。小さな構成では問題ありませんが、同じパッケージインストール・設定配布・サービス起動のまとまりを別のプレイブックでも使うには、毎回コピーしなければなりません。コピーしたコードが増えるほど、1 か所を直すときにすべてのコピーを探して直さなければならず、ミスが積み重なります。

role はこのまとまりを 約束されたディレクトリ構造 に一度整理しておき、プレイブックからは名前だけで呼んで使えるようにします。次のような利点が生まれます。

  • 再利用。一度書いた role を複数のプレイブックから呼び出します。
  • 構造化。task・handler・変数・ファイルが決まった位置に入るので見つけやすいです。
  • 共有。ansible-galaxy でダウンロードしたり、collection に入れて配布したりできます。

role のディレクトリ構造 #

role は決まったサブディレクトリ名に従います。各ディレクトリの main.yml が自動的にロードされる入口です。標準構造は次のとおりです。

roleの標準ディレクトリ構成
roles/
└── webserver/
    ├── tasks/
    │   └── main.yml        # role が実行する task
    ├── handlers/
    │   └── main.yml        # notify で呼び出される handler
    ├── defaults/
    │   └── main.yml        # 既定の変数 (優先順位は最下位)
    ├── vars/
    │   └── main.yml        # role 変数 (優先順位は高い)
    ├── templates/
    │   └── httpd.conf.j2   # template モジュールの src 基準パス
    ├── files/
    │   └── index.html      # copy モジュールの src 基準パス
    ├── meta/
    │   └── main.yml        # 依存関係とメタデータ
    └── README.md

ここで重要なのは パスの自動探索 です。role の task の中で template モジュールに src: httpd.conf.j2 とだけ書けば、Ansible が自動的にその role の templates/ 以下で探します。copy モジュールの src も同じく files/ 以下を見ます。したがって role の中では全体パスを書かなくても済みます。

ansible-galaxy で role の骨組みを作る #

空のディレクトリを手で作る必要はありません。ansible-galaxy role init コマンドが標準構造を一度に生成します。

roleスケルトンの生成
# roles ディレクトリの中で実行
$ cd roles
$ ansible-galaxy role init webserver
- Role webserver was created successfully

$ tree webserver
webserver/
├── defaults
│   └── main.yml
├── files
├── handlers
│   └── main.yml
├── meta
│   └── main.yml
├── README.md
├── tasks
│   └── main.yml
├── templates
├── tests
│   ├── inventory
│   └── test.yml
└── vars
    └── main.yml

生成された各 main.yml は空の骨組みなので、必要なディレクトリの内容だけ埋めれば済みます。試験ではこのコマンドで骨組みを作ったあと、tasks/main.yml と必要な変数・テンプレートだけ書く流れが速いです。

role の内容を書く #

webserver role を例に、task と handler、defaults、template を埋めていきます。

tasks/main.yml #

roles/webserver/tasks/main.yml
---
# roles/webserver/tasks/main.yml
- name: httpd パッケージをインストールする
  ansible.builtin.dnf:
    name: httpd
    state: present

- name: index.html を配布する
  ansible.builtin.copy:
    src: index.html          # files/index.html を自動的に探します
    dest: /var/www/html/index.html

- name: httpd の設定を配布する
  ansible.builtin.template:
    src: httpd.conf.j2       # templates/httpd.conf.j2 を自動的に探します
    dest: /etc/httpd/conf/httpd.conf
  notify: restart httpd

- name: httpd サービスを起動する
  ansible.builtin.service:
    name: httpd
    state: started
    enabled: true

handlers/main.yml #

roles/webserver/handlers/main.yml
---
# roles/webserver/handlers/main.yml
- name: restart httpd
  ansible.builtin.service:
    name: httpd
    state: restarted

defaults/main.yml #

roles/webserver/defaults/main.yml
---
# roles/webserver/defaults/main.yml
http_port: 80
server_admin: admin@example.com

defaults に置いた変数は優先順位が最も低いので、プレイブックから role を呼び出すときに同じ名前の値を渡せば手軽に上書きできます。role を使う側が調整する余地がある値は、vars ではなく defaults に置くのが慣例です。

role を使う #

書いた role をプレイブックから呼び出す方法は大きく 3 つあります。roles キー、include_roleimport_role で、動作のしかたがそれぞれ異なります。

roles キー #

最も単純な方法は、play レベルの roles キーに並べることです。role の task が play の通常 task より先に実行されます。

site.yml
---
# site.yml
- name: ウェブサーバーを構成する
  hosts: web
  become: true
  roles:
    - webserver

変数を渡すには次のように書きます。

roleへの変数渡し
  roles:
    - role: webserver
      vars:
        http_port: 8080

include_role と import_role #

roles キーの代わりに task 一覧の中で role を呼び出すと、実行位置を直接制御できます。このとき 2 つのモジュールの違いが試験のポイントです。

項目import_roleinclude_role
処理時点静的 (static)。プレイブックのパース段階で前もって取り込む動的 (dynamic)。実行時点で取り込む
when の適用内部のすべての task に条件が個別に適用されるrole 呼び出し 1 回に条件が適用される
loop の使用不可可能。role を繰り返し呼び出す
tag の継承内部 task が tag を継承する呼び出し task にのみ tag が適用される
include_roleで動的呼び出し
---
- name: 条件に応じて role を呼び出す
  hosts: web
  become: true
  tasks:
    - name: 通常の task を先に実行する
      ansible.builtin.debug:
        msg: "role 呼び出し前"

    - name: 動的に webserver role を呼び出す
      ansible.builtin.include_role:
        name: webserver
      when: enable_web | default(false)

まとめると、前もって静的に取り込んでよい場合は import_role を、条件や繰り返しで実行の有無・回数を実行時点で決めなければならない場合は include_role を使います。

role 変数の優先順位 #

role は変数を defaults と vars の 2 か所に置けて、両者の優先順位が異なります。#6 変数と fact で扱った全体の優先順位の中で、role 関連の位置だけ抜き出すと次のとおりです。

  • defaults/main.yml。最も低い優先順位。ほぼすべての他の変数がこれを上書きします。
  • role 呼び出し時に vars: で渡した値。defaults を上書きします。
  • vars/main.yml。defaults より高いです。
  • play と呼び出し側の vars、inventory 変数、extra vars (-e)。より高い位置から上書きします。

核心は defaults が最も低い という点です。role を使う人が変えられるべき値は defaults に置き、role 内部で固定して使う値は vars に置きます。同じ変数を 2 か所に同時に置くと vars が勝つので、外部から上書きしにくくなります。注意します。

role の依存関係 #

ある role が別の role を先に実行しなければならないなら、meta/main.ymldependencies に宣言します。宣言された依存 role は現在の role より先に実行されます。

roles/webserver/meta/main.yml
---
# roles/webserver/meta/main.yml
dependencies:
  - role: common              # webserver より先に実行されます
  - role: firewall
    vars:
      firewall_ports:
        - 80/tcp

commonfirewall role が webserver より先に実行されるので、共通パッケージのインストールやファイアウォールの開放といった先行作業を依存関係にまとめておけます。依存関係にも上記のように vars で値を渡せます。

roles_path: role を探す場所 #

Ansible は role を次の順で探します。

  1. プレイブックと同じ位置の roles/ ディレクトリ
  2. ansible.cfgroles_path に指定したパス

したがってプレイブックの隣に roles/ ディレクトリを置けば、別途の設定なしで role を認識します。別の位置に role を置くには ansible.cfg にパスを書きます。

ansible.cfg
# ansible.cfg
[defaults]
roles_path = ./roles:/etc/ansible/roles

試験では通常、作業ディレクトリの roles/ 以下に role を作るのが最も安全です。ansible-galaxy でダウンロードした role もこのパス規則に従います。

全体の例: role を作成して呼び出す #

ここまで作ってきた断片を 1 つの流れにまとめます。作業ディレクトリの構造は次のとおりです。

プロジェクトのディレクトリ構成
project/
├── ansible.cfg
├── inventory
├── site.yml
└── roles/
    └── webserver/
        ├── tasks/main.yml
        ├── handlers/main.yml
        ├── defaults/main.yml
        ├── templates/httpd.conf.j2
        └── files/index.html

呼び出しプレイブックは次のように短いです。role の中に構成が集まっているので、プレイブックの本文は role を呼ぶだけです。

site.yml
---
# site.yml
- name: ウェブサーバーグループを構成する
  hosts: web
  become: true
  roles:
    - role: webserver
      vars:
        http_port: 8080
        server_admin: web@example.com

実行と冪等性の確認は、いつもどおり 2 回回して 2 回目の実行で changed が 0 になるかを見ます。

実行と冪等性確認
$ ansible-navigator run site.yml -m stdout
$ ansible-navigator run site.yml -m stdout   # 2 回目の実行: changed=0 なら冪等

試験のポイント #

  • ansible-galaxy role init <名前> で標準ディレクトリ構造をまず生成します。
  • role のディレクトリは taskshandlersdefaultsvarstemplatesfilesmeta で、各 main.yml が自動の入口です。
  • role 内部で templatesrctemplates/copysrcfiles/ を自動的に探すので、全体パスは書きません。
  • roles キーは通常 task より先に実行されます。条件・繰り返しで動的に呼び出す必要があれば include_role、静的な取り込みは import_role を使います。
  • role 変数の優先順位で defaults が最も低いです。外部から上書きする値は defaults に、固定の値は vars に置きます。
  • role 間の前後関係は meta/main.ymldependencies で宣言します。
  • role はプレイブックの隣の roles/ansible.cfgroles_path から探します。作業ディレクトリの roles/ が最も安全です。

まとめ #

この記事で押さえたこと:

  • role は task・handler・変数・テンプレート・ファイルを標準ディレクトリにまとめて再利用する単位です。
  • ansible-galaxy role init で骨組みを作り、必要なディレクトリだけ埋めます。
  • 呼び出しは roles キー・import_role (静的)・include_role (動的) で、動作が異なります。
  • defaults は優先順位が最も低く、外部から上書きしやすい既定値を置きます。
  • 依存関係は meta/main.ymldependencies で、探索パスは roles_path で扱います。

次へ — Collection #

role で構成をモジュール化したなら、その次は複数の role とモジュール・プラグインをまとめて配布する単位である collection です。

#12 Collection: Galaxy、Automation Hub では、collection の構造と ansible-galaxy collection installrequirements.yml で依存 collection をインストールする方法、Ansible Galaxy と Automation Hub の違い、そして FQCN (ansible.builtin.dnf の形) でモジュールを正確に参照する習慣まで整理します。

X