はじめに
Courtyard Sessions のサイトは、2 台のバックエンドで稼働しています。新しいプログラムは準備済みですが、両方を同時に更新すると、訪問者がサイトを利用できなくなる可能性があります。Ansible で稼働状態を確認し、新しいパッケージを 1 台ずつ配布しましょう。
基本的なターミナル操作と、小規模な Ansible インベントリ、変数、file/copy タスクの実践経験が必要です。ゲートウェイ、SSH アクセス、パッケージは準備済みです。このラボでは、実行場所、委任、バッチ、リリースリンク、HTTP コンテンツ全体の検証を使用前に説明します。所要時間は、読解と通常の修正を含めた作者の見積もりです。
各タスクの実行場所を確認する
このステップでは、各バックエンドの稼働リリースと、実際に有効なルーティングプールを表示する、読み取り専用の点検プレイブックを作成します。
Courtyard Sessions の訪問者向けサイトは既に動作しています。Website タブを開きます。Good things, around the corner. を読み、The program をクリックしてください。ページが 2 つの催しにスクロールし、最初に Coffee in the courtyard が表示されます。Download the program (CSV) をクリックしてダウンロードしたファイルを開きます。ヘッダーは time,session,place で、その後に表示された 2 つの催しが続きます。CSV は、フィールドをコンマで区切ったプレーンテキストの表です。この実際の訪問者向けダウンロードは、更新中も利用可能である必要があります。
ゲートウェイは、短い HTTP リクエストを 2 台のバックエンドに振り分けます。有効なプールからバックエンドを外すと、そのバックエンドへの新たなゲートウェイリクエストが止まります。追加すると、再び振り分け対象になります。この小さな準備済みゲートウェイは、長時間接続のドレインや、クラウドのリリース基盤全体を再現するものではありません。両バックエンドは SSH で管理するコンテナーで、このトレーニング VM のカーネルを共有します。コントローラーは、Ansible とゲートウェイを実行するマシンです。
Controller タブへ切り替えます。開始ディレクトリは /home/labex/project です。pwd は現在のディレクトリを、cat はファイルの内容を表示します。
pwd
cat inventory.yml
cat group_vars/all.yml
インベントリは、YAML と互換性のある構造化データです。website グループには web1 と web2 が含まれ、それぞれ実際の ansible_host アドレスと準備済み SSH 接続情報を持ちます。control グループには、ローカル接続を使う controller が含まれます。このプロジェクトの ansible.cfg が選択する inventory.yml を使ってください。イメージの初期 inventory.ini は、このコースのインベントリではありません。
グループ変数には、backend_port(8090)、コントローラー専用の pool_api、管理を許可された release_root、ローカルの package_root、リリース識別子が用意されています。baseline_release は courtyard-preview、approved_release は courtyard-weekend です。これらはディレクトリ名であり、Ansible やソフトウェアのバージョン番号ではありません。HTTP ポートは、あるアドレスで待ち受けるサービスを識別します。/identity は、バックエンドが実際に実行しているリリースを示す構造化テキスト、JSON を返します。/pool は、コントローラーの現在の有効ノード一覧を返します。
コマンドが「No such file or directory」と表示したら、ls で綴りを確認し、必要なら cd /home/labex/project で戻ってください。読み取りコマンドは安全に再実行できます。提供されたすべてのパッケージファイル、接続データ、元のリリースファイル、主催者の非公開メモを変更せず保持します。
既定では、Ansible は選択された各バックエンドで SSH 経由でタスクを実行します。バックエンド上の 127.0.0.1 は、そのバックエンド自身です。delegate_to: controller を指定すると、そのタスクだけをコントローラーで実行します。コントローラー自身の 127.0.0.1 では、プール API が動いています。委任しても inventory_hostname は変わらず、その時点の管理対象バックエンドを示します。そのため、コントローラーのリクエストから正しいバックエンドを指定できます。
inspect.yml を作成します。cat > filename はファイルを書き込み、同名ファイルがあれば置き換えます。<<'YAML' はヒアドキュメントの開始です。単独行の終了マーカー YAML まで貼り付けてください。引用符付きの区切り文字により、貼り付けたテキストはそのまま保持され、Ansible がプレイブック実行時に式を処理します。インデントにはスペースを使います。{{ variable }} は Ansible 変数を挿入します。gather_facts: false は、この作業に不要なマシン情報の収集を省略します。
uri モジュールは実際の HTTP リクエストを送ります。既定の GET メソッドはリソースを読み取ります。register は実際の結果に名前を付けて保存します。JSON 応答では .json に解析済みのフィールドが入ります。debug は保存した観察結果を表示するだけで、リリースやプールの状態を作成するものではありません。
cat > inspect.yml <<'YAML'
- name: Inspect the running website without changing it
hosts: website
gather_facts: false
tasks:
- name: Read this backend on its own loopback address
ansible.builtin.uri:
url: "http://127.0.0.1:{{ backend_port }}/identity"
register: backend
- name: Read the pool on the controller
ansible.builtin.uri:
url: "{{ pool_api }}/pool"
delegate_to: controller
register: pool
- name: Show the actual release and active pool
ansible.builtin.debug:
msg:
node: "{{ inventory_hostname }}"
release: "{{ backend.json.release }}"
active: "{{ pool.json.active }}"
YAML
プレイブックを実行します。ansible-playbook はタスクを読み、インベントリに適用します。
ansible-playbook inspect.yml
各バックエンドで release: courtyard-preview が表示され、active に web1 と web2 の両方が含まれるはずです。表示形式やアドレスは異なる場合があります。最初の uri はバックエンドで動き、プールの読み取りは web1 -> controller のような委任表示になる点に注目してください。どちらも読み取り専用です。
YAML のインデントが誤っていたら、完全なブロックを再コピーしてファイルを修正し、再実行します。このプレイブックは、ファイルのコピーやプール変更を行ってはいけません。これで、リリース後にも再利用できる便利な観察ツールができました。
バックエンドを 1 台ずつリリースする
このステップでは、承認済みサイトパッケージを SSH で配布し、実際の訪問者向け内容を検証してからバックエンドをサービスへ戻します。その後で次のバックエンドに進みます。
Controller タブの /home/labex/project に留まります。パッケージ一覧を表示し、承認済みプログラムを読みます。ls はディレクトリの項目を一覧表示します。
ls packages
cat packages/courtyard-weekend/schedule.csv
承認済みプログラムには、Bread and stories、Acoustic afternoon、Print a postcard の 3 つの催しがあります。ホームページだけでは不十分です。訪問者には、それに対応する完全なダウンロードも必要です。courtyard-incomplete パッケージは次のラボで使います。ここでは、既存の release_id 変数が指定しているとおり、courtyard-weekend を選びます。
プレイブックは serial: 1 を使い、1 ホストのバッチを処理します。そのバッチ内で、現在のノードをプールから外し、パッケージをコピーし、アクティブなリリースリンクを切り替え、2 つの業務リソースを確認してからノードを戻します。それが終わって初めて次のバッチを始めます。order: sorted により、この例は web1 から始まります。any_errors_fatal: true は未処理の失敗を明示的に伝播させ、プレイを停止します。ここで使う 1 ホストのバッチでは、そのホストの失敗だけでもバッチ全体が失敗します。この設定は、完了済みタスクを取り消しません。次のラボで復旧を追加します。
プール API は、JSON のリクエスト本文を伴う /pool への HTTP POST を受け付けます。node は管理対象ノード名、enabled は外すときにブール値 false、追加するときに true を指定します。body_format: json は本文を正しくシリアライズします。プールのタスクはコントローラーで、ファイルのタスクは選んだ SSH バックエンドで動きます。
file に state: directory を指定するとディレクトリを作成します。モード '0755' は所有者に書き込みを、全員に読み取りとディレクトリ通過を許可します。copy は、選んだコントローラーのパッケージをバックエンドへ送ります。src 末尾の / はディレクトリの中身をコピーし、読み取り可能なファイルのモードを '0644' にします。file に state: link を指定するとシンボリックリンクを作成または更新します。current はリリースディレクトリを指します。準備済み HTTP サーバーは、リクエストのたびにこのリンクを通じて実際のファイルを提供します。リンクを切り替えると、サーバーを再起動せずにアクティブなサイトが変わり、元のリリースディレクトリはそのまま残ります。
コントローラーで行う HTTP プローブでは、hostvars[inventory_hostname].ansible_host で管理対象バックエンドのアドレスを明示的に取得します。委任中に接続変数の参照先を明示しないと、委任先ホストの値になる場合があります。return_content: true は実際の応答本文を .content に保存します。
assert は that の式が真であることを要求します。lookup('file', ...) はコントローラー上の期待するパッケージファイルを読み、~ はパス文字列を連結します。rstrip=False は末尾の改行を保持するため、末尾を削った断片ではなく本文全体を比較できます。プールへ戻す前に、候補のホームページ全体と承認済み CSV 全体が一致しなければなりません。404 は、サーバーが応答したものの、そのリソースがないことを示します。接続エラーはサービスに到達できないことを示します。どちらも正常なリリースとはいえません。
完全なリリース処理を書きます。ヒアドキュメントは、点検ツールと同じファイル作成方法です。
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: 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
YAML
まずリリースせずに YAML とタスク構造を確認します。--syntax-check は構文を検証しますが、業務結果の正しさまでは証明しません。
ansible-playbook rollout.yml --syntax-check
綴りやインデントのエラーが出たら、完全なファイルを再コピーして確認し直します。その後、実際のリリースを実行します。
ansible-playbook rollout.yml
web1 のタスクが終了してから web2 のタスクが始まるはずです。両ホストとも最後は failed=0、unreachable=0 になります。プールから外す・戻す操作のリクエストは ok と表示される場合があります。要約の changed 件数より、HTTP の成功と実際の効果が重要です。通常の編集ミスで止まったら、ファイル全体を修正し、同じ承認済みパッケージで再実行します。再実行でも、外す・確認する・戻す操作が行われます。何も行わないデプロイではありません。
元のツールで稼働状態を確認します。
ansible-playbook inspect.yml
両ノードが courtyard-weekend を示し、両方がプールで有効になっているはずです。Website タブへ切り替えて再読み込みします。見出しは A little room for a good afternoon. です。The program をクリックして 3 つの催しを確認し、CSV をもう一度ダウンロードして新しいファイルを開き、3 行すべてを確認します。以前のダウンロードがあると、ブラウザーがファイル名に番号を付けることがあります。Plan a visit からは、引き続き段差のない庭側入口の案内へ移動できる必要があります。
更新されたプログラムには承認済みの 3 つの催しがすべて含まれ、実際の CSV ダウンロードは一覧の横にあります。

この結果は、準備済みの短いリクエストを扱う環境で、2 ノードの更新が機能したことを示します。任意の本番トラフィックやアプリケーションで無停止を保証するものではありません。
まとめ
読み取り専用の点検ツールを作成し、Ansible による順次配布で、実際のサイトと完全なプログラムのダウンロードを提供しました。プール変更の委任と実際の内容確認により、各管理対象バックエンドの状態に基づいてリリースを判断できました。



