【Ansible】block-whenという書き方について
【結論】
Ansibleに
if-else
はないけど、block-when
はあった。block-when
という書き方が最適解かどうかはご自身で判断ください😑Don't repeat yourself (DRY)信者がAnsibleと付き合うには妥協が必要。
【目次】
はじめに
現在業務でAnsibleを使って、ファイアウォール(FW)のプロビジョニング作業を自動化するための開発を行っています。
システム構成の概略は下図になります。
(FWのプロビジョニングに関係する部分だけを記載しています)
システムの概要をざっくりいうと、A,B,Cユーザーが各々で管理していたサーバーを一箇所で管理するためのシステムです。
新たにxxユーザーを追加する場合は次の手順で行えます。
- xxユーザー専用のサーバーを用意する。
- ユーザー情報を構成管理DBに登録する。
- Ansibleのプレイブックを実行すると、xxサーバーの設定を行い、FW_1とFW_2の穴あけ作業を自動で行ってくれる。
と言う一連の流れの内、私はFW_1とFW_2の穴あけ作業の自動化部分を担当しています。
FW(ファイアウォール)のプロビジョニング内容
1台のFWへのプロビジョニングは次の手順で行います。
- 事前確認(正常な状態か?あける予定の穴はまだあいていないか?...etc)
- 設定処理
- 事後確認(穴はきちんとあいたか?設定中に状態異常になっていないか?...etc)
今回のシステム構成では2台のFWに対してプロビジョニングを行います。
その場合の処理の流れを表したのが下図になります。
赤四角はFWに対して操作を行なっている部分です。FWによって必要なデータが変わってくるため、個別に渡してあげる必要があります。
また、FW_2では一部行う必要のない処理もあるため、赤四角の破線で表しています。
実装方針
FW_1とFW_2の差は渡すデータだけです。
よって、データは事前に定義しておき、事前確認、設定、事後確認は共通化する方針で実装しました。
全ソースを乗せると長くなる上に、事前確認、設定、事後確認はほぼ同じであるため、事前確認部分のソースだけを載せます。
【ファイル構成】
$ tree ├── fw_main.yml ├── tasks ├── fw_provisioning.yml └── set_config.yml
【ソース①(./fw_main.yml
)】
--- - hosts: localhost # 自分自身に接続する tasks: - name: 設定情報の定義 include_task: set_config.yml - include_task: fw_ provisioning.yml vars: in_config: "{{ item }}" with_items: - "{{ fw1_config }}" - "{{ fw2_config }}"
【ソース②(./tasks/set_config.yml
)】
--- # 構成管理DBからデータ取得 # (省略) # FW_1とFW_2の設定情報定義 - name: 接続先情報の定義 set_fact: fw1_setting: pre_check_1: "確認内容1" pre_check_2: "確認内容2" pre_check_3: "確認内容3" # 設定処理と事後チェック用の定義は省略 fw2_setting: pre_check_1: "確認内容1" pre_check_2: "確認内容2" pre_check_3: false # 設定処理と事後チェック用の定義は省略
【ソース③(./tasks/fw_provisioning.yml
)】
--- - name: 変数設定 set_fact: pre_check_1: "{{ in_config.pre_check_1 }}" pre_check_2: "{{ in_config.pre_check_2 }}" pre_check_3: "{{ in_config.pre_check_3 }}" # ------------------------------- # 事前チェック - block: - name: 事前チェック1 include_task: fw_pre_check1.yml vars: in_data: "{{ item }}" with_items: - "{{ pre_check_1 }}" when: ( pre_check_1 != false) - block: - name: 事前チェック2 include_task: fw_pre_check2.yml vars: in_data: "{{ item }}" with_items: - "{{ pre_check_2 }}" when: ( pre_check_2 != false) - block: - name: 事前チェック3 include_task: fw_pre_check3.yml vars: in_data: "{{ item }}" with_items: - "{{ pre_check_3 }}" when: ( pre_check_3 != false) # ------------------------------- # 設定処理と事後チェックは省略
自己満足ポイントは、FW_1とFW_2のデータは共通で定義し、FW_2で必要ない定義はfalseを設定しておき、【ソース③(./tasks/fw_provisioning.yml
)】で.yml
を呼ぶかどうかをblock-when
で判定するようにしている点です。
【ソース③(./tasks/fw_provisioning.yml
)】で呼んでいる事前チェック1〜3の.yml
もループを回せば記述量を減らせそうですが、そこまでやると可読性が下がる上に、Ansibleにそこまで求めると沼にはまってしまうので目を瞑っています。
補足
ちなみに、block-when
は私が勝手に言っているだけで、本来のblock
は例外処理を実装するために用意されています。
- Ansible→
block-rescue-always
- Java→
try-chatch-finally
- Python→
try-except-finally
- Ruby→
begin-rescue-ensure-end
さいごに
1月程Ansibleと関わって身にしみたことは、「Don't repeat yourself (DRY)信者がAnsibleと付き合うには、
過度な期待や理想を追い求めず、信念を曲げて妥協に妥協を重ねる必要がある」ことを理解しました。