スケーリング
複数のアプリノードとワーカーに分割した AnsibleForms と、一つ以上の RTE
ロール
すべての AnsibleForms コンテナーは同じサーバーコードを実行し、 AF_ROLE が何をするかを決めます。
AF_ROLE | イメージ | 実行するもの | 数 |
|---|---|---|---|
all (未設定) | ansibleforms | Web アプリとワーカーを一つのプロセスで。既定です | 1 |
app | ansibleforms | Web インターフェースと API | ロードバランサーの背後にいくつでも |
worker | ansibleforms | バックグラウンドの処理。データベーススキーマとそのアップグレード、構成シード、スケジュール、夜間バックアップ、リポジトリの同期、クリーンアップ | 稼働するのは一つ。ほかは待機して引き継ぎます |
rte | ansibleforms-rte-full (または -rte-core 、 -rte-base ) | プレイブック | いくつでも |
最小のインストールは、 AF_ROLE のないコンテナー一つと RTE 一つです。 Docker Compose プロジェクト と Helm チャート はこの構成で動作します。 AF_ROLE にそれ以外の値を指定すると、プロセスは起動時に停止します。
RTE
プレイブックを実行するランタイム環境である RTE には、専用のセクション ランタイム環境 があります。そこでは、RTE が ジョブを実行する仕組み、 そのイメージ 、 実行方法 、ランナーまたはレプリカとしての 複数の RTE 、 そして 別のデータセンターの RTE を扱います。
複数のアプリノード
より多くのユーザーに対応したり、ノードを一つ失っても稼働を続けたりするには、アプリをロードバランサーの背後の app ノードと worker に分割します。
ノードが共有するもの
ノードはデータベースと共有ボリュームを通じて互いに整合を取ります。
- データベース :ジョブ、ワーカーのロック、デザイナーのロック、ノードのリスト、ランナーのリース(各 RTE のソケットを どのアプリノードが持っているか)、そして各ノードのキャッシュを最新に保つ変更通知。一つのノードで編集した認証情報、ランナー、スケジュールは、 5 秒以内にすべてのノードで有効になります
- 永続ボリューム (
/app/dist/persistent):フォーム、設定、リポジトリ、アップロード、バックアップ、管理対象の.env。 すべてのアプリノードとワーカーが同じものを読み書き可能でマウントします - ssh フォルダー (
/home/node/.ssh):ssh 経由の git に使うキーと既知のホスト。ワーカーが最初の起動時にキーを作成します ACCESS_TOKEN_SECRET:すべてのアプリノードで同じにします。そうしないと、一つのノードが署名した トークンが次のノードで拒否されます。アプリノードはこれがないと起動しませんENCRYPTION_SECRET:すべてのアプリノードとワーカーで同じにします。そうしないと、一つのノードが 保存した認証情報を別のノードで読めません。ソケットモードの RTE はこれを読みません。必要なのは、自分自身を登録する非推奨のデータベースモードの RTE だけです
すべてのコンテナーに同じデータベースユーザーも与えてください。待機中のワーカーが停止したワーカーのセッションを終了できるのは、それが自分と同じユーザーの場合だけです。
ワーカーのロック
同時に稼働するワーカーは一つだけです。稼働中のワーカーはデータベースのロック ansibleforms_worker を保持します。停止するとデータベースがロックを解放し、 待機中のワーカーが 10 秒以内にそれを取得します。ワーカーのホストが別れを告げずに停止すると、データベースは何時間もロックを保持し続けることがあるため、 2 分間どのワーカーもハートビートを書き込まなければ、待機中のワーカーがそのセッションを終了して引き継ぎます。
別のワーカーにロックを奪われたワーカーは停止し、そのコンテナーは待機側として再起動します。起動に失敗したワーカーも停止するので、 何もせずにロックを保持し続けることはありません。Galera とグループレプリケーションは、データベースノード間でロックを共有しません。すべての ワーカーを同じデータベースノードに向けてください。
ジョブとノード
各コンテナーは、何も設定しなくても自分に <role>-<hostname>-<port> という名前を付けます。 ステータス ページにはそのすべてがロールと バージョン、どれがワーカーかとともに表示され、異なるバージョンが動作している場合は警告が出ます。
RTE で実行中のジョブはその RTE に属し、それを開始したアプリノードがなくなっても続行します。それ以外のジョブ(AWX やほかのランナーの テンプレートジョブ、ワークフロージョブ、非推奨のマルチステップフォームのステップ)は、それを開始したアプリノードに属します。再起動したコンテナーは 自分の未完了のジョブを終了し、置き換えられた Pod のように 2 分間応答しなくなったコンテナーのジョブはワーカーが終了します。
どのノードに SIGHUP を送っても、 構成シード が再適用されます。アプリノードはワーカーにそれを依頼します。
アップグレード
先にワーカー をアップグレードし、その後でアプリノードをアップグレードします。ワーカーがスキーマにパッチを当て、新しいバージョンのアプリノードは スキーマにパッチが当たるまで待機し、何を待っているかをログに記録します。先にアップグレードしたアプリノードはワーカーが追いつくまで準備ができていない状態のままで、 その間は古いノードが処理を続けます。RTE は、同じ契約で通信できる限り、いつ追従してもかまいません。
制限
一つのノードにとどまるものもいくつかあります。
- チャットアシスタント は、会話を一つのアプリノードのメモリに保持します。ロードバランサーでスティッキーセッションを使う(nginx の
ip_hash)か、アプリノードを一つにしてください - ログ は各コンテナーにとどまります。複数のプロセスが一つのファイルをローテーションすると行が失われるため、
LOG_PATHは 共有ボリュームの外に設定してください。ログビューアーは応答したノードのログを表示するので、ログは一元的に収集してください - 設定ページで変更した 設定 は共有ボリューム上の管理対象の
.envに書き込まれ、すべてのノードに反映されます。再起動が必要な設定は、 すべてのノードで再起動が必要です
構成例
アプリのリポジトリには、 examples/scale に完全な出発点があります。
| サービス | イメージ | ロール |
|---|---|---|
db | mariadb:11 | データベース。 AnsibleForms スキーマにだけ権限を持つユーザー付き |
app1 、 app2 | ghcr.io/ansibleforms/ansibleforms:7 | AF_ROLE=app 。ロードバランサーの背後 |
worker1 、 worker2 | ghcr.io/ansibleforms/ansibleforms:7 | AF_ROLE=worker 。二つ目は待機して引き継ぎます |
rte | ghcr.io/ansibleforms/ansibleforms-rte-full:7 | RTE_MODE=inbound と RTE_FILES=local でプレイブックを実行します。アプリがそこに接続します |
lb | nginx:stable | ポート 8443 で HTTPS を終端し、 ip_hash でブラウザーをアプリノードに振り分けます |
RTE のランナー rte と、それがサインインに使う認証情報(アドレス http://rte:8000 と RTE_TOKEN )は、ワーカーが起動のたびに適用する 構成シード seed.yaml で宣言されています。RTE は RTE_URL を設定せず、ホストには公開されません。
アプリノードとワーカーは一つの .env を共有し、そこにはデータベースユーザー、 ENCRYPTION_SECRET 、 ACCESS_TOKEN_SECRET 、 RTE_TOKEN 、 CONFIG_SEED_PATH 、ロードバランサーの背後なので HTTPS=0 、そして共有ボリュームの外の LOG_PATH があります。RTE には env_file がなく、 compose.yaml から RTE_TOKEN だけを受け取るので、データベースのパスワードは保持しません。アプリノード、ワーカー、RTE は同じ 永続ボリュームと ssh ボリュームをマウントします。
cp .env.example .env # and fill in the secrets
docker compose up -d # then https://localhost:8443