メインコンテンツまでスキップ

高可用性 Enterprise

備考

高可用性は Semaphore Enterprise エディションで利用できます。

Semaphore UI は、ロードバランサーの背後で複数のインスタンスを同時に稼働させるアクティブ-アクティブ構成の高可用性 (HA) デプロイメントをサポートしています。各インスタンスは、UI リクエスト、API 呼び出し、スケジュールされたジョブ、タスク実行のすべてを処理できます。1 つのインスタンスに障害が発生しても、残りのノードが中断なく動作を継続します。

アーキテクチャ​

一般的なアクティブ-アクティブ構成のデプロイメントは、次のコンポーネントで構成されます。

ロードバランサー — ユーザーはロードバランサー (NGINX、HAProxy、クラウドのロードバランサーなど) を経由して接続します。ロードバランサーは、HTTP および WebSocket トラフィックを利用可能な Semaphore ノードに分散します。

Semaphore ノード — 各ノードは Semaphore UI の同一のインスタンスを実行します。どのノードでもユーザーリクエストの受信、自動化ジョブの開始、スケジュールされたタスクの処理、リアルタイム更新の送信が可能です。すべてのノードは対等であり、プライマリノードやスタンバイノードは存在しません。

共有データベース — すべてのインスタンスは共有の PostgreSQL または MySQL データベースに接続します。データベースは、プロジェクト、テンプレート、インベントリ、スケジュール、タスク履歴、ユーザーアカウント、RBAC 設定に関する唯一の信頼できる情報源 (single source of truth) として機能します。

警告

SQLite と BoltDB は HA デプロイメントではサポートされていません。PostgreSQL または MySQL を使用してください。

Redis — Redis は、複数のノードを 1 つのシステムとして動作させるための調整レイヤーを提供します。Redis には次の 3 つの役割があります。

  • 分散ロック により、特定のジョブを同時に実行するインスタンスが 1 つだけになることを保証し、タスクの重複実行を防ぎます。
  • 共有タスクキューの状態 により、タスクキューが維持され、ジョブは必ず 1 つのワーカーだけに取得されます。すべてのノードが同じキューを参照し、実行を調整します。
  • Pub/Sub メッセージング により、ノードはタスクの更新、クラスター通知、キャッシュの無効化、UI 状態の変更などのイベントをブロードキャストできます。これにより、すべてのノードがリアルタイムで同期されます。

前提条件​

HA を設定する前に、次のものが必要です。

  • Semaphore Enterprise のサブスクリプションキー。
  • すべてのノードからアクセスできる共有の PostgreSQL または MySQL データベース。
  • すべてのノードからアクセスできる Redis サーバー、または Redis Sentinel 構成。Redis Cluster はサポートされていません。
  • HTTP および WebSocket トラフィックをサポートする ロードバランサー。
  • Semaphore インスタンスを実行する 2 台以上のサーバー。

すべての Semaphore ノードは、同じデータベース、同じ Redis バックエンド、同じ設定を使用する必要があります (ノードごとに一意でなければならない ha.node_id を除く)。

設定​

各ノードの config.json に ha ブロックを追加して HA を有効にします。

{
"dialect": "postgres",
"postgres": {
"host": "db.example.com:5432",
"name": "semaphore",
"user": "semaphore",
"pass": "***"
},

"ha": {
"enabled": true,
"node_id": "node-1",
"redis": {
"addr": "redis.example.com:6379",
"db": 0,
"pass": "***"
}
},

"cookie_hash": "...",
"cookie_encryption": "...",
"access_key_encryption": "..."
}

各ノードには一意の ha.node_id が必要です。それ以外の設定はすべてのノードで同一にしてください。

環境変数​

環境変数を使用して HA を設定することもできます。

SEMAPHORE_HA_ENABLED=true
SEMAPHORE_HA_NODE_ID=node-1
SEMAPHORE_HA_REDIS_ADDR=redis.example.com:6379
SEMAPHORE_HA_REDIS_DB=0
SEMAPHORE_HA_REDIS_PASS=***

Redis Sentinel​

Redis サーバー自体の障害に耐えるには、単一のアドレスではなく Redis Sentinel 構成をクラスターの接続先として指定します。ha.engine を redis_sentinel に設定し、Sentinel の構成を ha.redis_sentinel に記述します。

{
"ha": {
"enabled": true,
"node_id": "node-1",
"engine": "redis_sentinel",
"redis_sentinel": {
"master_name": "mymaster",
"addrs": ["sentinel-1.example.com:26379", "sentinel-2.example.com:26379", "sentinel-3.example.com:26379"],
"pass": "***"
}
}
}

各ノードは現在のマスターを Sentinel に問い合わせ、再起動なしでフェイルオーバーに追従します。マスターが切り替わる間に実行中だったタスクはそのまま実行を継続し、新しいタスク、スケジュール、リアルタイム更新は昇格したレプリカ上で継続されます。

環境変数を使用した同じ構成 (Sentinel の一覧は JSON 配列です)。

SEMAPHORE_HA_ENABLED=true
SEMAPHORE_HA_NODE_ID=node-1
SEMAPHORE_HA_ENGINE=redis_sentinel
SEMAPHORE_HA_REDIS_SENTINEL_MASTER_NAME=mymaster
SEMAPHORE_HA_REDIS_SENTINEL_ADDRS='["sentinel-1.example.com:26379","sentinel-2.example.com:26379","sentinel-3.example.com:26379"]'
SEMAPHORE_HA_REDIS_SENTINEL_PASS=***

注意事項:

  • ha.engine によって、どちらのブロックが読み込まれるかが決まります。redis (デフォルト) では ha.redis、redis_sentinel では ha.redis_sentinel が読み込まれます。もう一方のブロックが存在していても無視され、起動時に警告がログに記録されます。
  • redis_sentinel を使用する場合は、master_name と addrs 内の少なくとも 1 つのアドレスが必須です。これらがないとノードは起動しません。
  • user と pass は Redis データノードに対する認証に使用されます。Sentinel 自体が認証を必要とする場合 (sentinel.conf の requirepass) は、sentinel_user と sentinel_pass も設定してください。
  • tls と tls_skip_verify は、Sentinel と Redis データノードの両方の接続に適用されます。

設定リファレンス​

設定ファイルのオプション環境変数説明
ha.enabledSEMAPHORE_HA_ENABLED高可用性モードを有効にします。
ha.node_idSEMAPHORE_HA_NODE_IDこのノードの一意の識別子。
ha.redis.addrSEMAPHORE_HA_REDIS_ADDRRedis サーバーのアドレス (例: localhost:6379)。
ha.redis.dbSEMAPHORE_HA_REDIS_DBRedis データベース番号。
ha.redis.passSEMAPHORE_HA_REDIS_PASSRedis サーバーのパスワード。
ha.redis.userSEMAPHORE_HA_REDIS_USERRedis サーバーのユーザー名。
ha.redis.tlsSEMAPHORE_HA_REDIS_TLSRedis 接続で TLS を有効にします。
ha.redis.tls_skip_verifySEMAPHORE_HA_REDIS_TLS_SKIP_VERIFYRedis の TLS 証明書検証をスキップします。
ha.engineSEMAPHORE_HA_ENGINERedis バックエンド: redis (単一サーバー、デフォルト) または redis_sentinel。
ha.redis_sentinel.master_nameSEMAPHORE_HA_REDIS_SENTINEL_MASTER_NAMESentinel で設定された、監視対象マスターの名前。
ha.redis_sentinel.addrsSEMAPHORE_HA_REDIS_SENTINEL_ADDRSSentinel のアドレス (host:port)。環境変数では JSON 配列。
ha.redis_sentinel.sentinel_userSEMAPHORE_HA_REDIS_SENTINEL_SENTINEL_USERSentinel ノード自体のユーザー名。
ha.redis_sentinel.sentinel_passSEMAPHORE_HA_REDIS_SENTINEL_SENTINEL_PASSSentinel ノード自体のパスワード。
ha.redis_sentinel.dbSEMAPHORE_HA_REDIS_SENTINEL_DBデータノード上の Redis データベース番号。
ha.redis_sentinel.userSEMAPHORE_HA_REDIS_SENTINEL_USERRedis データノードのユーザー名。
ha.redis_sentinel.passSEMAPHORE_HA_REDIS_SENTINEL_PASSRedis データノードのパスワード。
ha.redis_sentinel.tlsSEMAPHORE_HA_REDIS_SENTINEL_TLSSentinel と Redis の接続で TLS を有効にします。
ha.redis_sentinel.tls_skip_verifySEMAPHORE_HA_REDIS_SENTINEL_TLS_SKIP_VERIFYSentinel と Redis の接続の TLS 証明書検証をスキップします。

利用可能なオプションの完全な一覧については、設定を参照してください。

ロードバランサー​

トラフィックを分散するために、Semaphore ノードの前段にロードバランサーを配置します。UI のリアルタイム更新のため、ロードバランサーは WebSocket 接続 をサポートしている必要があります。

NGINX の例​

upstream semaphore {
server node1.example.com:3000 max_fails=3 fail_timeout=10s;
server node2.example.com:3000 max_fails=3 fail_timeout=10s;
server node3.example.com:3000 max_fails=3 fail_timeout=10s;
}

server {
listen 443 ssl;
server_name semaphore.example.com;

ssl_certificate /etc/ssl/certs/semaphore.crt;
ssl_certificate_key /etc/ssl/private/semaphore.key;

location / {
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";

proxy_pass http://semaphore;

proxy_connect_timeout 3s;
proxy_send_timeout 60s;
proxy_read_timeout 60s;

proxy_next_upstream error timeout invalid_header http_500 http_502 http_503 http_504;
proxy_next_upstream_tries 3;
}


location /api/ws {

proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;

proxy_pass http://semaphore;

proxy_connect_timeout 3s;
proxy_send_timeout 1h;
proxy_read_timeout 1h;

proxy_next_upstream error timeout http_502 http_503 http_504;
proxy_next_upstream_tries 3;
}
}

NGINX の設定の詳細については、リバースプロキシを参照してください。

ジョブ実行の仕組み​

マルチノード構成では、タスクの実行は次のような調整されたフローに従います。

  1. ユーザーがタスクを起動します。 ユーザーが UI または API からジョブを開始します。リクエストはどの Semaphore ノードにも届く可能性があります。
  2. タスクのメタデータが保存されます。 リクエストを受け取ったノードがタスクのメタデータをデータベースに書き込み、Redis 経由で処理を通知します。
  3. ノードがタスクを取得します。 利用可能なノードのいずれかが Redis からタスクを取得し、分散ロックを獲得して、データベース上で実行中としてマークします。
  4. タスクが実行されます。 そのノードがタスクをローカルで実行するか、リモートランナーに委譲します。進捗とログはデータベースに書き戻されます。
  5. 結果がブロードキャストされます。 タスクの更新は Redis Pub/Sub を通じて伝播され、すべてのノードと接続中の UI クライアントが同期された状態を保ちます。

ランナーによるスケーリング​

HA により、タスク実行の水平スケーリングも可能になります。ジョブを Semaphore ノード自身だけで実行するのではなく、複数のランナーに実行を委譲できます。これにより、次のことが可能になります。

  • インフラストラクチャ全体にワークロードを分散する。
  • Web/API レイヤーとは独立して自動化処理能力をスケールする。
  • 実行環境を分離して影響範囲を限定する。
  • 多数のノードでタスクを並列実行する。

セットアップ手順については、ランナーを参照してください。

メリット​

  • 信頼性の向上 — 1 つのインスタンスに障害が発生しても、他のインスタンスがトラフィックの処理とジョブの実行を継続します。
  • ダウンタイムなしのメンテナンス — システムを停止することなく、ノードを個別に更新・再起動できます。
  • 水平スケーラビリティ — ロードバランサーの背後に Semaphore ノードを追加して処理能力を増やせます。
  • プライマリノードへの依存なし — すべてのノードが対等であるため、複雑なフェイルオーバー機構が不要です。
  • 一貫したクラスター状態 — 共有データベースと Redis による調整により、すべてのインスタンスが同期された状態を保ちます。

FAQ​

アクティブ-アクティブ構成の高可用性とは何ですか?​

アクティブ-アクティブ構成の HA とは、複数のアプリケーションインスタンスが同時に稼働し、そのすべてがリクエストを処理する構成です。プライマリノードは存在せず、どのインスタンスでもトラフィックの処理とジョブの実行が可能です。

Semaphore は HA モードでなぜ Redis を使用するのですか?​

Redis はインスタンス間の調整レイヤーとして機能します。分散ロック、共有タスクキューの状態、Pub/Sub メッセージングを提供し、複数のノードが同じジョブを同時に実行しないことを保証します。

HA デプロイメントにはどのデータベースを使用すべきですか?​

Semaphore は共有データベースとして PostgreSQL と MySQL をサポートしています。SQLite と BoltDB は複数プロセスからの同時アクセスをサポートしていないため、HA モードでは使用できません。

Redis に障害が発生するとどうなりますか?​

単一の Redis サーバー (ha.engine: redis) を使用している場合、Redis が復旧するまでノードは相互に調整できません。Redis Sentinel を使用している場合は、Sentinel がレプリカを昇格させ、すべての Semaphore ノードが再起動なしで、通常は数秒以内に自動的に切り替わります。

Semaphore ノードの 1 つに障害が発生するとどうなりますか?​

ロードバランサーがトラフィックを残りのノードにルーティングします。実行中のジョブは他のインスタンスで継続され、新しいジョブは利用可能な任意のノードによって取得されます。

水平スケーリングは可能ですか?​

はい。ロードバランサーの背後に Semaphore ノードを追加して Web/API の処理能力を増やし、ランナーを追加してタスク実行の処理能力を増やすことができます。