Red Hat Certified Engineer (RHCE) #9 Tag と条件分岐: when、loop、until
#8 Error handling で失敗の扱い方を押さえたなら、今回は いつ task を実行するか、何回反復するか、どの部分だけ選んで回すか を決める流れの制御を整理します。when で条件をかけ、loop でリストを反復し、until で成功するまで再試行し、tags でプレイブックの一部だけを実行します。この 4 つは RHCE 実技でほぼ毎回登場するツールです。
when: 条件付き実行 #
when は task を実行するかしないかを決めるキーです。条件が真なら実行し、偽ならスキップします。値は Jinja2 式ですが、when の中では変数名を {{ }} なしでそのまま書きます。
- name: Apache パッケージのインストール
ansible.builtin.dnf:
name: httpd
state: present
when: ansible_facts['os_family'] == "RedHat"条件が偽のホストでは task が skipping と表示され、冪等性に影響を与えません。
fact ベースの分岐 #
試験で最もよくあるパターンは OS の種類やバージョンによって違う作業 をする分岐です。ansible_facts で収集された値を条件に使います。
- name: Debian 系で apache2 をインストール
ansible.builtin.apt:
name: apache2
state: present
when: ansible_facts['os_family'] == "Debian"
- name: RHEL 9 以上でのみ実行
ansible.builtin.debug:
msg: "RHEL 9 以上です"
when:
- ansible_facts['distribution'] == "RedHat"
- ansible_facts['distribution_major_version'] | int >= 9distribution_major_version は文字列なので、| int フィルターで整数に変換してから比較します。この変換を忘れると数値比較がずれることがあります。
and、or の組み合わせ #
複数の条件をまとめるとき 2 つの方式があります。リストで並べると すべての条件が真 (and) のときに実行されます。
- name: 2 つの条件がどちらも真のとき
ansible.builtin.debug:
msg: "条件を満たした"
when:
- ansible_facts['os_family'] == "RedHat"
- ansible_facts['memtotal_mb'] > 2048or や複合論理は 1 行の式で書きます。括弧で優先順位を明確にします。
- name: RHEL または Fedora のとき
ansible.builtin.debug:
msg: "Red Hat 系"
when: >
ansible_facts['distribution'] == "RedHat" or
ansible_facts['distribution'] == "Fedora"変数が定義されているかの検査 #
変数が定義されているかで分岐することもよくあります。is defined、is undefined、is none を使います。
- name: extra_pkg 変数が定義されている場合のみインストール
ansible.builtin.dnf:
name: "{{ extra_pkg }}"
state: present
when: extra_pkg is defined値の真偽で分岐するときは変数名をそのまま置くか、when: enable_service | bool のように | bool フィルターを使います。
loop: リストの反復 #
loop は 1 つの task をリストの各項目に対して反復実行します。反復中の現在の項目は item 変数で参照します。
- name: 複数のパッケージをインストール
ansible.builtin.dnf:
name: "{{ item }}"
state: present
loop:
- httpd
- mariadb-server
- phpパッケージのインストールのようにモジュールがリストを直接受け取る場合は name: "{{ packages }}" で一度に渡すほうが効率的ですが、loop はユーザー作成のように項目ごとに独立した呼び出しが必要なときに真価を発揮します。
ユーザー一覧の一括作成 #
試験定番の ユーザー一覧の一括作成 は loop の代表例です。各項目を dict にすれば、項目内の値に item.key でアクセスできます。
- name: ユーザーの一括作成
ansible.builtin.user:
name: "{{ item.name }}"
groups: "{{ item.groups }}"
state: present
loop:
- { name: alice, groups: wheel }
- { name: bob, groups: developers }
- { name: carol, groups: developers }変数ファイルにユーザーリストを置いて loop で参照すれば、プレイブック本文がすっきりします。
- name: 変数で定義したユーザーを作成
ansible.builtin.user:
name: "{{ item.name }}"
uid: "{{ item.uid }}"
state: present
loop: "{{ users }}"with_items は旧表記 #
昔のプレイブックでは with_items を使っていました。動作は同じですが、現在は loop が推奨表記であり、新しいプレイブックは loop で書きます。
# 旧表記 (参考用)
- name: with_items 方式
ansible.builtin.dnf:
name: "{{ item }}"
state: present
with_items: [httpd, vsftpd]with_items はリストを 1 段平坦化 (flatten) する点が loop とわずかに違います。ネストしたリストを扱わない限り結果は同じなので、試験では loop だけを一貫して使えば問題ありません。
dict の反復: dict2items #
リストではなく dict (キーと値の組) を反復するには、dict2items フィルターでリストに変換します。変換後の各項目は item.key と item.value を持ちます。
- name: dict を項目として反復
ansible.builtin.debug:
msg: "{{ item.key }} = {{ item.value }}"
loop: "{{ user_roles | dict2items }}"user_roles が次のような dict なら 2 回反復します。
user_roles:
alice: admin
bob: developerloop_control: loop_var と label #
role や include で loop がネストすると、内側と外側がどちらも item を使って衝突します。このとき loop_control の loop_var で反復変数名を変えます。
- name: 反復変数名の変更
ansible.builtin.user:
name: "{{ user_item.name }}"
state: present
loop: "{{ users }}"
loop_control:
loop_var: user_itemまた dict 項目を反復すると、出力に dict 全体が長々と出てログが散らかります。label で各反復に表示する値だけを指定すると出力がすっきりします。
- name: 出力ラベルの指定
ansible.builtin.user:
name: "{{ item.name }}"
state: present
loop: "{{ users }}"
loop_control:
label: "{{ item.name }}"until: 再試行 #
until は条件が真になるまで task を反復実行します。外部サービスの起動を待ったり、一時的に失敗する作業を再試行したりするときに使います。retries で最大試行回数を、delay で試行間隔 (秒) を指定します。
- name: サービスの応答を待つ
ansible.builtin.uri:
url: http://localhost:8080/health
status_code: 200
register: result
until: result.status == 200
retries: 5
delay: 10上の task は応答が 200 になるまで 10 秒間隔で最大 5 回試行します。条件が最後まで真にならなければ task は失敗します。until を使うには結果を register で捕まえて条件で参照する必要がある、という点が核心です。command のように毎回 changed として捕まるモジュールを照会用途で使うときは、changed_when: false を一緒に置いて冪等性を保ちます (下の総合例で確認できます)。
tags: 部分実行 #
tags は task や play に名札を付けて、プレイブック全体ではなく 必要な部分だけ選んで実行 できるようにします。長いプレイブックで特定の作業だけを素早く回すときに役立ちます。
- name: Apache のインストール
ansible.builtin.dnf:
name: httpd
state: present
tags:
- packages
- name: Apache 設定のデプロイ
ansible.builtin.template:
src: httpd.conf.j2
dest: /etc/httpd/conf/httpd.conf
tags:
- config–tags と –skip-tags #
--tags で指定したタグが付いた task だけを実行します。--skip-tags は逆に、指定したタグを除いて実行します。
# config タグが付いた task だけ実行
ansible-playbook site.yml --tags config
# packages タグだけスキップして残りを実行
ansible-playbook site.yml --skip-tags packagesansible-navigator でも同じオプションを使います。
ansible-navigator run site.yml -m stdout --tags configどのタグが定義されているかを確認するには --list-tags を使います。
ansible-playbook site.yml --list-tagsplay レベルのタグ #
play 全体にタグを付けると、その play のすべての task がタグを継承します。
- name: Web サーバーの構成
hosts: web
tags:
- web
tasks:
- name: httpd のインストール
ansible.builtin.dnf:
name: httpd
state: presentalways と never #
always タグが付いた task は、--tags で別のタグを指定しても 常に実行 されます。ただし --skip-tags always では除外できます。逆に never タグが付いた task は基本的に 実行されず、当該タグやその task の別のタグを明示したときだけ実行されます。
- name: 事前チェック (常に実行)
ansible.builtin.debug:
msg: "プレイブック開始"
tags:
- always
- name: デバッグ情報の出力 (明示したときだけ)
ansible.builtin.debug:
var: ansible_facts
tags:
- never
- debug上でデバッグ task は普段はスキップされますが、--tags debug で呼び出すと実行されます。重い診断作業を普段の実行から外しておくのに役立ちます。
総合例: OS 分岐 + loop + 再試行 #
ここまでのツールを 1 つのプレイブックにまとめます。OS 系列で分岐してパッケージをインストールし、ユーザーを一括作成し、サービスが有効になるまで再試行し、各ステップにタグを付けました。
---
- name: Web サーバーの構成
hosts: web
become: true
vars:
web_users:
- { name: deploy, groups: wheel }
- { name: app, groups: web }
tasks:
- name: 開始通知 (常に実行)
ansible.builtin.debug:
msg: "構成を開始します"
tags:
- always
- name: RHEL 系パッケージのインストール
ansible.builtin.dnf:
name: httpd
state: present
when: ansible_facts['os_family'] == "RedHat"
tags:
- packages
- name: 運用ユーザーの作成
ansible.builtin.user:
name: "{{ item.name }}"
groups: "{{ item.groups }}"
state: present
loop: "{{ web_users }}"
loop_control:
label: "{{ item.name }}"
tags:
- users
- name: サービスの起動
ansible.builtin.service:
name: httpd
state: started
enabled: true
tags:
- service
- name: サービス有効化の確認
ansible.builtin.command: systemctl is-active httpd
register: httpd_state
until: httpd_state.stdout == "active"
retries: 5
delay: 3
changed_when: false
tags:
- serviceこのプレイブックを --tags users で呼び出すと、開始通知 (always) とユーザー作成だけが実行されます。
試験ポイント #
- when は変数名を
{{ }}なしで 書きます。fact ベースの分岐でansible_facts['os_family']、distribution_major_version | intの比較が定番です。 - and はリストで、or は 1 行の式 で書きます。リスト形式で並べると、すべての条件が真のときだけ実行されます。
is definedで変数の存在を検査します。値の真偽判定には| boolを付けます。- loop の現在の項目は
item、dict 項目はitem.nameのようにアクセスします。ユーザー一覧の一括作成が代表的な出題パターンです。 - dict を反復するには
dict2itemsフィルターで変換し、item.key、item.valueでアクセスします。 - ネストした反復は
loop_control.loop_varで変数の衝突を避け、labelで出力を整えます。 - until は
registerと一対 です。retriesとdelayで再試行回数と間隔を決めます。照会用の command にはchanged_when: falseを一緒に置きます。 - tags で部分実行 します。
--tagsで選択、--skip-tagsで除外、--list-tagsで確認します。alwaysは常に、neverは明示したときだけ実行されます。
まとめ #
この記事で押さえたこと:
- when。条件付き実行。fact・変数ベースの分岐、and (リスト)・or (式)、
is defined - loop。リストの反復。
itemの参照、ユーザーの一括作成、dict2items、loop_controlのloop_varとlabel - until。再試行。
register・retries・delayの組み合わせでサービス起動を待つ - tags。部分実行。
--tags・--skip-tags・--list-tags、play レベルのタグ、always・never
この 4 つのツールは RHCSA 自動化編 (#14〜#17) で OS ごとの分岐と一覧の一括処理として絶えず再登場します。手に馴染ませておくと、その後の編が一段とやさしくなります。
次へ: Ansible Vault #
流れの制御は押さえました。ここからはプレイブックに入るパスワードや API キーのような機微な値を安全に扱う方法に進みます。
#10 Ansible Vault: 秘密情報の管理 では、ansible-vault で変数を暗号化し、--ask-vault-pass と vault パスワードファイルでプレイブックを実行し、平文と暗号文を分けて管理する試験定番のパターンまで、自分で作りながら整理します。