「これだけ見ればAWS上でKeycloak本番が作れる」を目指した旗艦リファレンスです。状態はAuroraに集約しコンテナはステートレス、クラスタ形成はJDBC_PING2、LBはNLB——実構築の勘所を構成図と手順で示します。
01この構成が解く問題 — 「ログインが不安定」の正体
Keycloakを冗長化してAWSに載せたのに「たまにログインし直しになる」「二要素の途中でセッションが切れる」——こうした相談の大半は、複数ノードの間でセッションキャッシュが共有できていないことが原因です。本記事は、Fargate(ECS)+ Aurora PostgreSQL + NLB という、私たちが本番で実際に組んできた構成をリファレンスとして提示します。要点さえ押さえれば、SIerや情シスの担当者が「この通り作れば本番相当が動く」と確信できるレベルまで落とし込むことが狙いです。
設計の背骨はシンプルです。状態はAuroraだけに集約し、Fargateのコンテナは完全にステートレスにする。そのうえで、ステートレスなノード同士が「同じクラスタの仲間」だと認識し合えるように、Infinispanのクラスタ形成をAWSの制約に合わせて正しく設定する。この2点が崩れなければ、コンテナは何度落として入れ替えても、利用者から見た認証は安定し続けます。
02全体アーキテクチャ
まず全体像です。クライアントからのHTTPSはpublicサブネットのNLBで受け、privateサブネットに置いた2AZ分のFargateタスクへ流します。永続状態はprivateサブネットのAurora PostgreSQL(Multi-AZ)に集約。イメージ配布(ECR)、認証情報(Secrets Manager)、ログ・メトリクス(CloudWatch)、構成バックアップ(S3)は周辺サービスとして配置し、NATコストを抑えるためにVPCエンドポイントを併用します。
ポイントは、リクエストの流れ(青)とバックアップの流れ(金)が明確に分かれていることです。利用者トラフィックはNLB→Fargate→Auroraと一直線に下り、バックアップはAuroraのPITR/スナップショットとrealmエクスポートのS3保管という別系統で守られます。ECS上でのHA設計の考え方はKeycloak HA構成 on AWSでも整理しています。
03Fargate(ECS)— ステートレスなKeycloakをどう載せるか
コンテナイメージは、Quarkusベースの最適化イメージを使います。Dockerビルド時にkc.sh buildを済ませ、起動はstart --optimizedで行うと、毎回の起動時にビルド工程が走らず立ち上がりが速くなります。ビルド済みイメージはECRに置き、Fargateから引きます。
- タスクは2以上・マルチAZ。privateサブネットに配置し、直接インターネットには出しません。
- コンテナはステートレス。ローカルディスクに何も残さない前提にします(realmもユーザーもAurora側)。
- 主要な設定キー(値はバージョン依存のため公式で要確認):
KC_DB=postgres、KC_DB_URL、KC_HOSTNAME(公開URL)、経路にxforwardedヘッダを付与するL7要素がある場合のKC_PROXY_HEADERS=xforwarded、キャッシュ関連はKC_CACHE=ispnとKC_CACHE_STACK=jdbc-ping。
NLB(L4)はX-Forwarded系ヘッダを付与しないため、NLBでTLS終端する構成ではKC_PROXY_HEADERS=xforwardedは実効性がなく、正しいURL生成は公開URLを指定するKC_HOSTNAMEで担保します。xforwardedヘッダを信頼させたい場合は、ヘッダを付与・サニタイズするL7要素(ALB等)を経路に置く必要があります。なお旧proxyオプション(KC_PROXY)はKeycloak 24で非推奨化され、Keycloak 26.0.0で削除されました。代替はproxy-headers(KC_PROXY_HEADERS)です。詳細はKeycloak公式のhostname(v2)ガイドを確認します。WildFly版からの移行を含む土台づくりはQuarkus版への移行にまとめています。
04イメージのビルドとpush — Fargateではビルドしない
「では、その最適化イメージはどこでビルドするのか」を明確にしておきます。答えはFargateではありません。Fargateは実行時にECRからイメージをpullするだけで、ビルド環境ではありません。ビルドはCI/CDパイプライン側で行い、ECRへpushします。
鍵になるのがマルチステージDockerfileです。builderステージでkc.sh buildを実行してプロバイダ・DB・機能フラグを焼き込み、最終ステージではstart --optimizedで起動する形にします。骨子は次のとおりです。
- builderステージ:独自のproviders/themesがあればCOPYし、
KC_DBやKC_HEALTH_ENABLED等のbuild-timeオプションを与えてkc.sh buildを実行。 - 最終ステージ:builderの
/opt/keycloak/をコピーし、ENTRYPOINTをkc.sh start --optimizedに。 - このイメージをタグ付けしてECRへpush。Fargateのタスク定義はそのイメージURIを指すだけです。
docker buildを回す場所は、CI/CDの選択で決まります。
- AWS純正なら CodeBuild + CodePipeline:source→CodeBuildが
docker buildしてECRへpush→ECSへデプロイ、という一本のパイプラインにまとめられます。 - GitHub Actions / GitLab CI なら:ランナーでビルドし、OIDCでECRにキーレス認証してpushします(長期アクセスキーをCIに置かない)。CI側の設計はインフラのCI/CDパイプライン設計もあわせてご覧ください。
kc.sh buildはイメージビルド時に一度だけ走らせ、Fargateではstart --optimizedで起動します。これを守らないと、起動のたびに裏でauto buildが走り「起動が遅い」の原因になります(Quarkus版への移行で詳述)。ビルドは必ずCI側(またはローカル)、Fargateでは走らせない、が鉄則です。この「build→push」パイプラインは、Terraformのstateロック(S3+DynamoDB)と同じデプロイ基盤(CI/CD側)のレイヤーに属します。実行時のFargate/Aurora/NLBとは層が違う、と整理すると全体像がすっきりします。
05【最重要】Infinispanのクラスタ形成 — JDBC_PINGで組む
ここが本構成で最も陥りやすい箇所です。KeycloakのセッションキャッシュはInfinispanで分散共有されますが、その前提としてノード同士がお互いを発見(ディスカバリ)できる必要があります。ところがFargateはマルチキャストが使えず、コンテナのIPも起動のたびに変わるため、デフォルトのマルチキャスト前提のディスカバリは機能しません。ここを外すと、片方のノードで作られたセッションがもう片方から見えず、「ログインが不安定」という症状になります。
定番の解決策がJDBC_PING(現行はJDBC_PING2)です。追加インフラを立てず、既存のPostgresのテーブルをノードの"名簿"として使う方式で、各ノードが自分のIP/ポートをテーブルに登録し、そこから他ノードを発見します。発見後は直接ユニキャスト(既定TCP 7800)でキャッシュを同期します。
朗報として、Keycloak 26系ではこのjdbc-pingスタックが分散キャッシュ有効時のデフォルトになり、公式ドキュメント上もJDBC_PING2によるDB登録が既定と明記されています。つまり近年のバージョンでは、以前のようにカスタムのキャッシュ設定XMLを持ち込まなくても、KC_CACHE_STACK=jdbc-pingを指定するだけでこの構成に乗れます。設定の詳細はKeycloak公式の分散キャッシュ設定で確認してください。
DNS_PING(ECS Service Connect / Cloud Map)も選択肢ですが、追加のサービスディスカバリ設定が要ります。「Postgresが既にそこにある」以上、まずはJDBC_PING2が最も部品が少なく堅い、というのが私たちの実務判断です。
06なぜALBではなくNLBか
ロードバランサをNLB(L4)にする理由は、TCPパススルーの素直さと、ACMによるTLS終端の扱いやすさです。
- TCPパススルーの素直さ。L4で余計な解釈を挟まないぶん、挙動が読みやすくトラブルシュートも容易です。
- SPNEGO/Kerberos(ブラウザ統合認証)とも相性が良い。ただし正確には、純粋なKerberos/SPNEGOはチケットを毎リクエストの
Authorization: Negotiateヘッダで提示するステートレス方式なのでALB(L7)でも動作します。接続に紐づいて張り替え・多重化で壊れるのはNTLMの方で、KDC(88番)の通信はクライアント〜AD間でありロードバランサは通りません。Kerberosで本当に効くのはSPNと公開ホスト名(KC_HOSTNAME)の一致で、NLBのTCP素通しはその整合をシンプルに保ちやすいという利点です。
TLS証明書の置き場所は明確です。ACMで発行/取り込んだ証明書をNLBのTLSリスナー(443)に載せてTLS終端します。ACM証明書はNLBのTLSリスナーで直接利用でき、自動更新も効きます。要件次第でコンテナまでTLSパススルーする構成も取れますが、運用の素直さからはNLB終端が基本です。詳細はNLBのサーバー証明書(ACM)公式を参照してください。AD/LDAP連携やKerberosを使う場合も、要点はSPNと公開ホスト名の整合で、NLBのTCP素通しはその設計をシンプルに保ちやすい、というのが実利です(Kerberos自体はALB(L7)でも動作します)。
07認証情報の注入 — Secrets Manager
DBのユーザー/パスワードや管理者パスワードは、タスク定義から環境変数に平文で書かないのが鉄則です。Secrets Managerに置き、ECSタスク定義のsecrets参照で注入します。こうすればイメージにもタスク定義のプレーンな値にも機密が残らず、ローテーションも回せます。Aurora作成時にSecrets Managerでの認証情報管理を有効にしておくと、DB側との整合も取りやすくなります。セキュリティ全般の締め方はKeycloakセキュリティ堅牢化にまとめています。
08観測性 — ログ・メトリクス・ヘルスチェック
コンテナログはFireLens(Fluent Bit)経由でCloudWatch Logsへ送ります。メトリクスはCloudWatch(Container Insights等)で取得。Keycloak側は管理用のポート9000にヘルスエンドポイント(/health/ready、/health/live)を公開でき、NLBのヘルスチェックは9000の/health/readyに向けます。ヘルスチェックは既定で無効なので、KC_HEALTH_ENABLED=trueで有効化するのを忘れないでください。詳細はKeycloak公式のヘルスチェックを参照。ログ収集エージェントの選定や監視・アラートの閾値設計は、認証基盤の重要度に応じて別途詰めることをおすすめします。
09バックアップ — 守るのはDBだけ
状態がAuroraに集約されているので、バックアップ戦略も明快です。
- 主軸はAuroraの自動バックアップ(PITR)+手動スナップショット。Auroraの自動バックアップは継続的・増分で、保持期間(1〜35日)内の任意時点へ復元できます。保持期間を超えて残したいものは手動スナップショット(無期限)で確保します。
- 構成のバックアップとして、realmの定期エクスポートをS3へ。
kc.sh exportでrealm定義(クライアント/ロール等)をJSONに書き出し、S3に保管します。DBのPITRが「時点復元」なのに対し、realmエクスポートは「設定を人間が読める形で持つ/別環境へ移す」ための保険です。
コンテナは捨てても平気、守るのはDB——この原則があるからこそ、復旧手順は「Auroraを戻す/エクスポートを取り込む」に集約でき、Fargate側は最新イメージから作り直すだけになります。DBのチューニングや復旧の勘所はKeycloakのデータベースチューニングを参照してください。より広いDR設計(RPO/RTO)は要件次第で別途詰めます。エクスポート/インポートの正確な手順はKeycloak公式のimport/exportで確認します。
10ネットワークとコスト、そしてDynamoDBの扱い
ネットワークはprivateサブネット主体、Security Groupは最小構成にします。
- NLB → Fargate(サービスポート、例:8443)
- Fargate → Aurora(5432)
- Fargate ↔ Fargate(クラスタ用7800、相互許可)
- Fargate → Secrets Manager / ECR / CloudWatch(HTTPS)
privateサブネットから各AWSサービスへ出る通信は、VPCエンドポイント(ECR / Secrets Manager / CloudWatch / S3)を使うとNAT Gateway経由の通信量を減らせ、コストと閉域性の両方で有利です。イメージのpullやログ送信の量が読めない初期ほど、この設計差がNAT代に効いてきます。
最後にDynamoDBの位置づけを正確に。Keycloak本体は実行時構成でDynamoDBを使いません(状態はAuroraのみ)。DynamoDBが登場するのは、この構成をコードで作る側——Terraformのstateロック(S3+DynamoDB)というデプロイ基盤の部品としてだけです。IaCのstate管理はIaCのstate管理にまとめています。
11用意するものチェックリスト
- ECRリポジトリと、
kc.sh build済みの最適化イメージ - Aurora PostgreSQL(Multi-AZ)クラスタと接続情報(
KC_DB_URL) - NLB(TLSリスナー443)+ ACM証明書
- Security Group一式(NLB→Fargate、Fargate→Aurora:5432、Fargate↔Fargate:7800、Fargate→Secrets/ECR/CloudWatch)
- Secrets Manager(DB認証情報・管理者パスワード)
- CloudWatchロググループ(FireLens出力先)
- S3バケット(realmエクスポート保管先)
- VPCエンドポイント(ECR / Secrets Manager / CloudWatch / S3)
- Keycloak設定キー:
KC_DB/KC_DB_URL/KC_HOSTNAME/KC_PROXY_HEADERS/KC_CACHE=ispn/KC_CACHE_STACK=jdbc-ping/KC_HEALTH_ENABLED=true
—まとめ
本構成の勘所は3つに集約されます。(1)状態はAuroraに集約しコンテナはステートレスにする。(2)クラスタ形成はJDBC_PING2で、既存Postgresをノードの名簿として使う(Keycloak 26系では既定)。(3)統合認証を活かすためLBはNLB(L4)、TLS証明書はACMをNLBに載せる。ここさえ外さなければ、コンテナは自由に入れ替えられ、守るべきはDBだけ、という運用に収束します。バージョン依存の設定値は必ず公式ドキュメントで最終確認してください。
—参考(一次情報)
- Keycloak — Configuring distributed caches(jdbc-ping / JDBC_PING2)
- Keycloak — Configuring Keycloak for production
- Keycloak — Configuring the hostname (v2)
- Keycloak — 26.0.0 released(proxy オプション削除・proxy-headers へ移行)
- AWS — What is a Network Load Balancer?(L4 / X-Forwarded 系ヘッダを付与しない)
- Keycloak — Tracking instance status with health checks
- Keycloak — Importing and exporting realms
- AWS — Server certificates for your Network Load Balancer
- AWS — Overview of backing up and restoring an Aurora DB cluster