Red Hat Certified Engineer (RHCE) #11 Role の作成と使用
#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 が自動的にロードされる入口です。標準構造は次のとおりです。
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 コマンドが標準構造を一度に生成します。
# 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
- 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: truehandlers/main.yml #
---
# roles/webserver/handlers/main.yml
- name: restart httpd
ansible.builtin.service:
name: httpd
state: restarteddefaults/main.yml #
---
# roles/webserver/defaults/main.yml
http_port: 80
server_admin: admin@example.comdefaults に置いた変数は優先順位が最も低いので、プレイブックから role を呼び出すときに同じ名前の値を渡せば手軽に上書きできます。role を使う側が調整する余地がある値は、vars ではなく defaults に置くのが慣例です。
role を使う #
書いた role をプレイブックから呼び出す方法は大きく 3 つあります。roles キー、include_role、import_role で、動作のしかたがそれぞれ異なります。
roles キー #
最も単純な方法は、play レベルの roles キーに並べることです。role の task が play の通常 task より先に実行されます。
---
# site.yml
- name: ウェブサーバーを構成する
hosts: web
become: true
roles:
- webserver変数を渡すには次のように書きます。
roles:
- role: webserver
vars:
http_port: 8080include_role と import_role #
roles キーの代わりに task 一覧の中で role を呼び出すと、実行位置を直接制御できます。このとき 2 つのモジュールの違いが試験のポイントです。
| 項目 | import_role | include_role |
|---|---|---|
| 処理時点 | 静的 (static)。プレイブックのパース段階で前もって取り込む | 動的 (dynamic)。実行時点で取り込む |
| when の適用 | 内部のすべての task に条件が個別に適用される | role 呼び出し 1 回に条件が適用される |
| loop の使用 | 不可 | 可能。role を繰り返し呼び出す |
| tag の継承 | 内部 task が tag を継承する | 呼び出し task にのみ tag が適用される |
---
- 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.yml の dependencies に宣言します。宣言された依存 role は現在の role より先に実行されます。
---
# roles/webserver/meta/main.yml
dependencies:
- role: common # webserver より先に実行されます
- role: firewall
vars:
firewall_ports:
- 80/tcpcommon と firewall role が webserver より先に実行されるので、共通パッケージのインストールやファイアウォールの開放といった先行作業を依存関係にまとめておけます。依存関係にも上記のように vars で値を渡せます。
roles_path: role を探す場所 #
Ansible は role を次の順で探します。
- プレイブックと同じ位置の
roles/ディレクトリ ansible.cfgのroles_pathに指定したパス
したがってプレイブックの隣に roles/ ディレクトリを置けば、別途の設定なしで role を認識します。別の位置に role を置くには 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
- 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 のディレクトリは
tasks・handlers・defaults・vars・templates・files・metaで、各main.ymlが自動の入口です。 - role 内部で
templateのsrcはtemplates/、copyのsrcはfiles/を自動的に探すので、全体パスは書きません。 rolesキーは通常 task より先に実行されます。条件・繰り返しで動的に呼び出す必要があればinclude_role、静的な取り込みはimport_roleを使います。- role 変数の優先順位で
defaultsが最も低いです。外部から上書きする値は defaults に、固定の値は vars に置きます。 - role 間の前後関係は
meta/main.ymlのdependenciesで宣言します。 - role はプレイブックの隣の
roles/とansible.cfgのroles_pathから探します。作業ディレクトリのroles/が最も安全です。
まとめ #
この記事で押さえたこと:
- role は task・handler・変数・テンプレート・ファイルを標準ディレクトリにまとめて再利用する単位です。
ansible-galaxy role initで骨組みを作り、必要なディレクトリだけ埋めます。- 呼び出しは
rolesキー・import_role(静的)・include_role(動的) で、動作が異なります。 defaultsは優先順位が最も低く、外部から上書きしやすい既定値を置きます。- 依存関係は
meta/main.ymlのdependenciesで、探索パスはroles_pathで扱います。
次へ — Collection #
role で構成をモジュール化したなら、その次は複数の role とモジュール・プラグインをまとめて配布する単位である collection です。
#12 Collection: Galaxy、Automation Hub では、collection の構造と ansible-galaxy collection install、requirements.yml で依存 collection をインストールする方法、Ansible Galaxy と Automation Hub の違い、そして FQCN (ansible.builtin.dnf の形) でモジュールを正確に参照する習慣まで整理します。