KeycloakをKubernetes上で動かすとき、詰まるのは大抵Operatorの使い方ではなく、ノード間クラスタリングとキャッシュの挙動です。Operator/CRD、jgroupsによるノード発見、証明書とIngress、スケール時のセッションまで、現行のQuarkus版を基準に実務のポイントを整理します。
KeycloakをKubernetes上で動かす案件は増えていますが、実際に運用フェーズで詰まるのは「Operatorの入れ方」ではありません。ノード間のクラスタリング、キャッシュStackの選び方、そしてスケールやローリング更新時のセッションの挙動です。本記事では現行のQuarkusベースのディストリビューション(WildFly版は非推奨、バージョン依存は公式で要確認)を前提に、Operator/CRDの扱いから、jgroupsによるノード発見、証明書・Ingress、EKSの勘所までを実務目線で整理します。
01Operatorが実際に生成するもの
Keycloak Operatorは、CRD(Keycloak と KeycloakRealmImport)をクラスタに登録し、Keycloak カスタムリソース(CR)を1つ書けば必要なワークロード一式を面倒みてくれる仕組みです。インストールはCRDを2本 kubectl apply し、Operator本体をデプロイするか、本番なら Operator Lifecycle Manager(OLM)経由が推奨されています。詳細は公式のOperator Installationガイドを参照してください。
ここで運用上おさえておきたいのは、現行のOperatorが Keycloak カスタムリソースから生成するワークロードは DeploymentではなくStatefulSet だという点です。状態自体はDBと(必要に応じて)分散キャッシュに載るためPodは比較的ステートレスに扱えますが、安定したネットワークIDや順序付きの起動といったクラスタリング要件を満たすために、Operatorは意図的にStatefulSetを採用しています。Podテンプレートを直接いじりたい場合は、CRの unsupported.podTemplate にPodテンプレートの生表現を書き込めます。ラベル・アノテーション・追加ボリューム・マウントを差し込む逃げ道として実務では重宝しますが、名前の通り「unsupported」なので乱用は避けます。
additionalOptions にkey-valueで渡せます。プレーン値だけでなくSecret参照も書けるので、DBパスワードやプロバイダ固有オプションはここでSecret化して流し込むのが定石です。02キャッシュStackの選択 — jdbc-pingが現行の既定
Keycloakの分散キャッシュはInfinispanで、ノード発見はjgroupsのdiscoveryプロトコルが担います。ここが近年もっとも変わった部分です。かつてKubernetesでは KC_CACHE_STACK=kubernetes(DNS_PING)を使うのが定番でしたが、公式のキャッシュ設定ガイドによれば、分散キャッシュ有効時の 既定Stackは jdbc-ping になり、従来の kubernetes / tcp / udp は非推奨(deprecated)扱いです(バージョン依存のため公式で要確認)。
- jdbc-ping:ノードが自分の情報をDBのテーブルに書き込み、そこを見て相互発見する方式。TCPトランスポートが既定。追加のヘッドレスサービスやマルチキャストが不要で、DBさえあれば成立するためKubernetesと相性が良く、26.x系のデフォルトとの後方互換もあります。
- kubernetes(DNS_PING、非推奨):ヘッドレスサービスのFQDNを
jgroups.dns.queryに指定し、DNS解決でノードを見つける方式。既存構成では今も動きますが、新規はjdbc-pingが無難です。
クラスタ内通信は 7800 番(ユニキャストのデータ転送、cache-embedded-network-bind-port で調整)、障害検知用に 57800 番(既定でオフセット)を使います。NetworkPolicyでPod間を絞っている環境では、この2ポートをノード間で開けておかないとクラスタが組めません。ここはハマりどころなので着任時に必ず確認します。
03セッションは「DBが正、キャッシュはL1」という前提
スケールやローリング更新の設計で最初に共有すべき事実は、近年のKeycloakでは 通常のユーザーセッションはDBに永続化され、キャッシュには必要に応じてオンデマンドで載る(persistent user sessions)という点です。つまりキャッシュは真実の源(source of truth)ではなくL1的な位置づけになりました。これによりPodが1つ落ちてもセッションはDBに残り、再スケジュール後もログイン状態が維持されます。
- クラスタ全体のキャッシュを失っても、ユーザーは再ログインを強制されにくくなった(認証セッションなど一部の揮発キャッシュは別)。
- ローリング更新でPodを順次入れ替えても、セッションの実体はDB側なのでダウンタイムなく進めやすい。
- その代わりDBへの負荷とコネクション設計がより重要になります。詳細な調整はKeycloakのデータベースチューニング、キャッシュ側の作り込みはInfinispanキャッシュチューニングを参照してください。
db.poolMinSize / poolInitialSize / poolMaxSize を同値(例:30)に揃えると、Postgres側のprepared statementキャッシュが効きやすくなります。HAガイドでも同値化が推奨されています。04Ingressと証明書 — 「どこでTLSを終端するか」を先に決める
KeycloakはOIDC/SAMLのメタデータやリダイレクトURLに自分のホスト名を埋め込むため、外部から見えるURLと内部の通信経路がズレると事故になります。設計の起点は「TLSをどこで終端するか」です。
- ホスト名:CRの
spec.hostname.hostnameに外部公開URLを明示します。ここを固定しないと、issuerやリダイレクトが内部Service名になってブローカリングやトークン検証が壊れます。 - TLS終端をPodまで通す場合:
spec.http.tlsSecretに証明書Secretを参照させ、Keycloak自身にTLSを持たせます。金融系などエンドツーエンド暗号が要件のときはこちら。 - Ingress/ALBで終端する場合:Pod手前で終端し、内部はHTTPにする構成。この場合は
proxy-headers(xforwardedなど)を正しく設定し、Keycloakに「自分は背後にプロキシがいる」と教える必要があります。ここを忘れるとX-Forwarded-*が無視され、生成URLがhttpに落ちます。 - 外部への信頼:LDAP/ADや外部IdPをTLS越しに叩く場合、CAをConfigMapから
truststoresとしてマウントします。自己署名や社内CAの環境では必須です。
ビルド時オプションと実行時オプションの区別(proxyやhostname系がどちら扱いか)はバージョンで動くので、ビルド時/実行時設定の分離もあわせて確認してください。
05スケールとローリング更新の実務
Operatorはデフォルトで scheduling スタンザ(affinity、tolerations、topology spread constraints、priority class)を公開しており、既定でもゾーン・ノードをまたいでPodを分散配置します。HAガイドでは3レプリカを複数AZに分けるのが基本形です。
- レプリカ数の増減:
jdbc-pingなら新ノードはDBを見て自動でクラスタに参加します。追加のヘッドレスサービス調整は不要です。 - ローリング更新:メジャー/マイナーをまたぐアップグレードでは、キャッシュのプロトコル互換性が問題になり得ます。バージョン跨ぎの更新手順はバージョンアップグレードと公式のUpgrading Guideで必ず確認してください。
- プローブ:
periodSecondsとfailureThresholdでreadiness/liveness/startupを調整できます。起動が重いKeycloakではstartupProbeを緩めておかないと、初回起動でCrashLoopに見える事故が起きます。
06EKSでの勘所
EKS特有で着任時に確認するポイントを絞ると次の通りです。
- NetworkPolicy / SG for Pods:前述の7800/57800をノード間で許可。Security Groups for Podsを使っている環境では、SG側でも塞がっていないか二重に確認します。
- DBはRDS/Aurora Postgres:HAガイドでもAurora前提の記述があり、
db-tls-mode=verify-serverでサーバー証明書を検証する構成が推奨されます。RDSのCAをtruststoreに入れます。 - ALB Ingress Controller:TLS終端をALBに寄せるなら証明書はACM、Keycloak側はproxy-headers。sticky sessionは
jdbc-ping+ 永続セッションなら必須ではありませんが、認証フロー途中の揮発状態を考えると有効化しておくと安定します。 - IRSA:Secrets ManagerやCloudWatchへ出す場合はIRSAでPodに権限を付与。CR側は
unsupported.podTemplateでserviceAccountNameを差し込みます。
マルチアカウント構成でEKSを運用している場合、認証基盤の配置とアカウント境界の設計はマルチアカウント統制の観点でも整理しておくと後の統制が楽になります。より広いHA設計はAWS上でのKeycloak HA構成にまとめています。
07まとめ — 詰まるのはOperatorではなくクラスタリング
KubernetesでのKeycloak運用は、Operator/CRDのおかげで宣言的なデプロイ自体は素直です。実務で効くのは、(1) キャッシュStackは現行なら jdbc-ping を既定に据える、(2) セッションはDBが正でキャッシュはL1という前提でスケール・更新を設計する、(3) ホスト名とTLS終端位置を最初に固定してproxy-headersを合わせる、(4) 7800/57800とDB接続をネットワーク・SGレベルで通す、という4点です。ここを押さえれば、スケールやローリング更新で「なぜかログアウトされる」「ノードがクラスタに入らない」といった典型的な事故はほぼ回避できます。EMWでも同様の構成でエンタープライズの認証基盤を実構築しており、導入事例もあわせてご覧いただけます。