#【Ansible 備忘録 ⑤】プレイブックの基本-特殊なディレクティブについて-
プレイブックの処理は、再利用性を高めるために、反復処理や条件分岐などの処理を行うための
ディレクティブが用意されている。
Ansible では、特定の条件の時のみタスクを実行したい時に利用する「when」ディレクティブを用意している。
例)
Web サーバーと DB サーバーでインストールするパッケージが異なる場合
tasks:
- name: Webサーバーへ Apache のインストール
yum: name=httpd state=installed
when: host_role=web
- name: DBサーバーへ PostgreSQL のインストール
yum: name=postgresql11-server state=installed
when: host_role=db
ファクト変数との連携によって、サーバーに適切なタスクを指定することもできる。
例)
RedHat 系(RedHat、CentOS、Amazon Linux2 など)は Apache をインストールする。
Debian 系(Debian、Mint、Ubuntu など)は Nginx をインストールする。
tasks:
- name: 【RedHat】Apache のインストール
yum: name=httpd state=installed
when: ansible_os_family == "RedHat"
- name: 【Debian】Nginx のインストール
apt: name=nginx state=installed update_cache=yes
when: ansible_os_family == "Debian"
変数フィルタとの連携によって、細かなエラーハンドリングが実装できる。
レジスタ変数に処理結果(succeeded、failed、changed、skipped)が挿入されることを利用して、
「when」ディレクティブ内で変数フィルタによる条件判定を実装できる。
例)
tasks:
- shell: /usr/bin/alias ls="ls -ltar"
register: result
ignore_errors: True
## result == failed の場合
- debug: msg="失敗しました"
when: result|failed
## result == changed の場合
- debug: msg="変更しました"
when: result|changed
## result == succeeded の場合
- debug: msg="成功しました"
when: result|succeeded
## result == skipped の場合
- debug: msg="スキップされました"
when: result|skipped
Ansible は、タスク実行に以上が発生した場合、処理が停止してコマンドのリターンコードが返ってくるが、
成功するまで処理を継続するなど実行結果を意図的にコントロールすることができる。
Ansible は、「shell」や「command」といったコマンドモジュール群を使用すると、状態が変化していなくても「changed」という結果が返ってくる。
このままでは、変更があったのかわからないので、 Ansible は「chenged_when」というディレクティブを用意している。
「changed_when」ディレクティブを利用することで、タスクに変更があったのかどうかを判定して、「changed」にするかを決定する。
また、「failed_when」ディレクティブという「failed」にする判定を実装できるディレクティブも用意されている。
例)
モジュールが存在するので、「ok」にしたい場合
tasks:
- name: Apache の存在確認
command: which httpd
register: changed_result
changed_when: changed_result.rc != 0
ディレクトリすでに存在するが、「failed」にしたくない場合
tasks:
- name: ディレクトリ作成
command: mkdir /home
register: failed_result
failed_when: failed_result.stderr == ""
Ansible では、1 つのタスクが失敗すると、そのターゲットノードではエラー対象のタスク以降のタスクは実行されない。
タスクが失敗しても後続のタスクを実行したい場合、「ignore_errors」ディレクティブを失敗する可能性のあるタスクに設定することで、失敗しても後続のタスクを実行できる。
例)
エラーを無視して継続する場合、「ignore_erorrs: true」を設定する。
tasks:
- name: エラーを無視して継続するタスク
shell: /bin/false
ignore_errors: true
- name: このタスクも実行される
shell: echo "test"
同じタスクを繰り返し何度も実行したい場合、ループを利用することでタスクを再利用できる。
プレイブックの内容が重複する場合、「with_items」ディレクティブを利用することで、繰り返し表現を定義できる。
「with_items」ディレクティブの後にシーケンスで繰り返し変数に導入したい項目を定義できる。変数は「{{ item }}」という変数の中に入り、指定の値が繰り返し代入される。
例)
同じタスクで複数のユーザーを追加する場合
tasks:
- name: ユーザーの追加(グループは管理者)
user:
name: "{{ item }}"
state: present
groups: wheel
with_items:
- kento75
- takano
- gakky
マッピングも使用できる。
例)
ユーザーの追加処理をユーザーごとに違うグループで行いたい場合
tasks:
- name: ユーザーの追加
user:
name: "{{ item.name }}"
state: present
groups: "{{ item.groups }}"
with_items:
- { name: "kento75", groups: "admin" }
- { name: "gakky", groups: "wheel" }
「with_nested」ディレクティブは、ネストされた多重のシーケンスを交互に繰り返し呼ぶことができる。
こちらは、「with_items」と異なり、プログラミング言語のリスト構造に似ている。
例)
下の例では、それぞれの DB にユーザー(kento75 と gakky)を追加している。
tasks:
- name: DBへのユーザー追加
postgresql_user:
db: "{{ item[0] }}"
name: "{{ item[1] }}"
password: "{{ demo }}"
priv: ALL
state: present
with_nested:
- [ "dev_db", "stg_db", "prod_db" ]
- [ "kento75", "gakky" ]
「block」を利用することで、指定のタスクをグループ化できる。
グループ化した全てのタスクに対して同一の「when」や「tag」などのディレクティブを指定できる。
例)
tasks:
## RedHat系を対象としたインストール
- block:
- copy: src=epel.repo dest=/etc/yml.repos.d/epel_andible.repo
- yum: name={{ item }} state=present
with_items:
- libselinux-python
- nginx
when: ansible_os_family == "RedHat"
## Debian系を対象としたインストール
- block:
- apt_repository: repo="ppa:nginx/stable"
- apt: name={{ item }} state=installed
with_items:
- python-selinux
- nginx
when: ansibe_os_family == "Debian"
## ここは共通なのでグループ化しない
- template: src=nginx.conf.j2 dest=/etc/nginx/nginx.conf
- service: name=nginx state=restarted
「block」には、プログラミング言語の try-catch-finally のような機能が用意されている。
通常は、「block」内でのタスクでエラーが返ってきた時点でタスクが終了してしまうが、「rescure」ディレクティブ(プログラミング言語でいうところの catch)を利用することで、エラーが発生した時のみ実行する処理を定義できる。また、常に実行する処理を「always」ディレクティブ(プログラミング言語でいうところの finally)で定義できる。
例)
tasks:
- block:
- command: /bin/false
- debug: msg="command ディレクティブでエラーが発生するのでこのタスクは実行されない"
rescure:
- debug: msg="上でエラーが発生したのでこのタスクは実行される"
- command: /bin/false
- debug: msg="command ディレクティブでエラーが発生するのでこのタスクは実行されない"
always:
- debug: msg="常に実行されるタスク"
ループの呼び出しには、「with_dict」とか「with_sequence」など色々用意されているが、使ったことないので記載しない。