はじめに
Courtyard Sessions の新しいパッケージは、ホームページが正常に見える一方で、訪問者向けダウンロードがありません。この独立環境は、元のサイトが両バックエンドで正常な状態から始まります。準備済み Ansible リリース処理を改良し、候補版が失敗したら以前の利用可能なリリースへ戻し、別ノードの更新前に停止させましょう。
先に「ホストを 1 台ずつ更新する」を完了するか、その点検ツール、委任、順次バッチ、copy/file 操作、HTTP コンテンツ全体の検証と同等の実践経験が必要です。このラボでは、実際のリンク先の記録と block/rescue による復旧を学びます。時間は作者の見積もりであり、初学者の所要時間を実測したものではありません。
失敗から復旧してリリースを停止する
このステップでは、実際の直前リリースを記録し、内容検証の失敗後に現在のノードを復旧して、次のバッチを明示的に止めます。
Website タブを開きます。元の見出しは Good things, around the corner. です。The program と Download the program (CSV) を使い、元の 2 つの催しを確認してください。ゲートウェイと 2 台の SSH バックエンドは前のラボから独立しており、以前のファイルや稼働プロセスは不要です。
Controller タブへ切り替えます。開始位置は /home/labex/project です。準備済みの inspect.yml と rollout.yml は前のラボのツールです。選択されたパッケージと現在の状態を読みます。
cat group_vars/all.yml
ls packages/courtyard-incomplete
ansible-playbook inspect.yml
release_id は courtyard-incomplete を選んでいます。ホームページと識別情報はありますが、schedule.csv はありません。実際の両バックエンドは、まだ courtyard-preview を示しています。提供されたすべてのパッケージのバイト列を変更しないでください。CSV を追加するとリリースの欠陥が隠れ、復旧の練習になりません。保護のないリリース処理はまだ実行しません。
block は失敗する可能性のあるタスクをまとめ、その中の通常のタスクが失敗すると rescue のタスクが動きます。ファイルシステムのメタデータを読む stat を使い、block の前で current を記録します。follow: false は、リンク先のディレクトリではなくリンクそのものを調べます。保存した previous.stat.lnk_source は実際の直前のリンク先です。これにより、固定ディレクトリを仮定せず、それまで稼働していたリリースへ戻せます。
復旧では、リンクを戻し、復旧後のホームページと CSV 全体を要求して、両方を直前のパッケージと比較します。basename フィルターは保存したリンク先からリリースディレクトリ名を取り出し、既存の lookup('file', ...) の比較で対応するローカルパッケージを読めるようにします。正常に復旧したノードだけをプールに戻します。復旧後のいずれかのリソースが欠けていたり内容が誤っていたりすると、その rescue タスクが失敗し、ノードは外れたままになります。別のバックエンドは訪問者への提供を続けます。
rescue が成功すると、Ansible の失敗処理の判断において、元のタスク失敗の影響が取り消されます。そのため、復旧成功後は any_errors_fatal だけでは後続バッチを停止できません。rescue の最後で明示的に fail を実行すると、候補版の拒否を報告して次のバッチを止められます。元のサイトが正常に戻っていても、非ゼロ終了はこの演習の意図した結果です。無効な YAML や到達不能な SSH ホストは、この rescue を起動しません。あらゆる問題をロールバックできる保証ではありません。
rollout.yml を次の完全な版で置き換えます。cat > が置き換えるのはこの学習者用プレイブックだけです。引用符付きのヒアドキュメント区切りは Ansible 式を保持します。示されたスペースを保ってください。
cat > rollout.yml <<'YAML'
- name: Release one website backend at a time
hosts: website
order: sorted
gather_facts: false
serial: 1
any_errors_fatal: true
tasks:
- name: Remember the actual previous release link
ansible.builtin.stat:
path: "{{ release_root }}/current"
follow: false
register: previous
- name: Update this backend or recover it
block:
- name: Drain this backend from the pool
ansible.builtin.uri:
url: "{{ pool_api }}/pool"
method: POST
body_format: json
body:
node: "{{ inventory_hostname }}"
enabled: false
delegate_to: controller
- name: Create this release directory
ansible.builtin.file:
path: "{{ release_root }}/releases/{{ release_id }}"
state: directory
mode: '0755'
- name: Copy the selected package over SSH
ansible.builtin.copy:
src: "{{ package_root }}/{{ release_id }}/"
dest: "{{ release_root }}/releases/{{ release_id }}/"
mode: '0644'
- name: Activate the selected release
ansible.builtin.file:
src: "{{ release_root }}/releases/{{ release_id }}"
dest: "{{ release_root }}/current"
state: link
- name: Read the real candidate homepage
ansible.builtin.uri:
url: "http://{{ hostvars[inventory_hostname].ansible_host }}:{{ backend_port }}/"
return_content: true
delegate_to: controller
register: homepage
- name: Require the complete candidate homepage
ansible.builtin.assert:
that: homepage.content == lookup('file', package_root ~ '/' ~ release_id ~ '/index.html', rstrip=False)
- name: Read the real candidate download
ansible.builtin.uri:
url: "http://{{ hostvars[inventory_hostname].ansible_host }}:{{ backend_port }}/schedule.csv"
return_content: true
delegate_to: controller
register: download
- name: Require the complete approved program
ansible.builtin.assert:
that: download.content == lookup('file', package_root ~ '/' ~ approved_release ~ '/schedule.csv', rstrip=False)
- name: Rejoin only after both business checks
ansible.builtin.uri:
url: "{{ pool_api }}/pool"
method: POST
body_format: json
body:
node: "{{ inventory_hostname }}"
enabled: true
delegate_to: controller
rescue:
- name: Restore the previous release link
ansible.builtin.file:
src: "{{ previous.stat.lnk_source }}"
dest: "{{ release_root }}/current"
state: link
- name: Read the restored homepage
ansible.builtin.uri:
url: "http://{{ hostvars[inventory_hostname].ansible_host }}:{{ backend_port }}/"
return_content: true
delegate_to: controller
register: restored_home
- name: Require the complete previous homepage
ansible.builtin.assert:
that: restored_home.content == lookup('file', package_root ~ '/' ~ (previous.stat.lnk_source | basename) ~ '/index.html', rstrip=False)
- name: Read the restored download
ansible.builtin.uri:
url: "http://{{ hostvars[inventory_hostname].ansible_host }}:{{ backend_port }}/schedule.csv"
return_content: true
delegate_to: controller
register: restored_download
- name: Require the complete previous program
ansible.builtin.assert:
that: restored_download.content == lookup('file', package_root ~ '/' ~ (previous.stat.lnk_source | basename) ~ '/schedule.csv', rstrip=False)
- name: Rejoin only after both restoration checks
ansible.builtin.uri:
url: "{{ pool_api }}/pool"
method: POST
body_format: json
body:
node: "{{ inventory_hostname }}"
enabled: true
delegate_to: controller
- name: Stop subsequent batches after recovery
ansible.builtin.fail:
msg: Candidate failed; the previous release is healthy again. Stop this rollout.
YAML
どちらのバックエンドも変更する前に、構文を検証します。
ansible-playbook rollout.yml --syntax-check
次に、不完全なリリースを試します。このコマンドは、復旧後に失敗することが想定されています。
ansible-playbook rollout.yml
直後に終了ステータスを読みます。$? は直前のコマンドの終了ステータスで、echo が表示します。ほかのコマンドを実行すると、シェルが保存した値が置き換わります。
echo $?
通常は 2 など、非ゼロのステータスになります。出力では、新ホームページの検証が成功し、候補のダウンロードは HTTP 404、復旧後のホームページと CSV は検証に成功し、最後に Stop subsequent batches after recovery が意図的に失敗します。HTTP 404 はダウンロードの欠落を示します。SSH 接続は復旧に引き続き利用できます。web2 はリリースタスクへ進んではいけません。
観察ツールを再実行します。
ansible-playbook inspect.yml
両バックエンドが courtyard-preview を示し、両方が有効でなければなりません。Website タブへ切り替え、再読み込みし、The program を開いて CSV を再度ダウンロードし、元の 2 つの催しが利用できることを確かめます。Plan a visit も引き続きアクセシビリティ情報を表示する必要があります。
復旧後のサイトでは、元の 2 つの催しと完全なダウンロードを引き続き利用できます。

通常の YAML やファイル名のミスなら、ls で確認し、プレイブック全体を修正して構文検証をやり直します。復旧処理が機能する前に、自分の編集でリンクを切り替えてしまった場合は、まず上の正しいプレイブックを保存してください。既に学んだ正常なリリース処理を使い、リリースと期待するプログラムの両方に既知の元パッケージを明示して復旧できます。引用符により、2 つの代入を 1 つの --extra-vars 引数にまとめます。
ansible-playbook rollout.yml --extra-vars "release_id=courtyard-preview approved_release=courtyard-preview"
この復旧コマンドは成功するはずです。inspect.yml で両ノードを確認してから、元の ansible-playbook rollout.yml コマンドで不完全な候補版を再実行し、必要な失敗と復旧を示します。VM のリセットや候補パッケージの書き換えは行いません。プレイブックを再利用可能に保つため、プールへの要求には提供された pool_api を、ファイルパスには release_root と package_root を、リリースタスクにはここで示した直接のモジュールを使ってください。
まとめ
実際にダウンロードが欠けた失敗から、本当の直前のサイトへ復旧しました。2 つの復旧確認がプールへの復帰を制御し、rescue が成功しても、明示的な失敗により後続の Ansible バッチが停止します。



