トップ ブログ AWS ECS Fargate で keycloak をクラスタ構成で動かしてみる

AWS ECS Fargate で
keycloak を
クラスタ構成で動かしてみる

2025/03/05

本記事は、NRIエンジニアによって2023年12月15日にQiitaに投稿された記事です。

Keycloak が ver17 より WildFly から Quarkus へ移行しているが、
Quarkus ベースのバージョンでクラスタ構成を検証している情報が少ないため、
今回 Keycloak を AWS のコンテナ環境でクラスタ構成で動作させてみる。
※本記事は、Keycloakのバージョンアップに伴い、「Keycloakを冗長構成で動かしてみる」と「インフラ管理不要なコンテナ環境のAWS FargateでKeycloakを動かしてみる」の記事を最新化したものです。

本記事でやること

  1. Keycloak の AWS 向けカスタム Docker イメージを ECR にプッシュする。
    1. AWS 向けに S3_PING を用いたクラスタ構成設定を追加
    2. コンテナビルド
    3. ECR でプライベートリポジトリを作成
    4. コンテナを ECR リポジトリへプッシュ
  2. Fatgate を用いた Keycloak 環境を AWS に構築する。
    1. VPC、ELBなどの作成
    2. Aurora(MySQL)上に作成および Keycloak 用データベースの作成
    3. S3_PING 用の S3 を作成
    4. ECS(クラスター、タスク定義、サービス)を作成
  3. 動作検証

前提条件

  • Keycloak バージョン:21.1.1
  • Dockerfile は公式ガイドに記載の内容をベース必要な部分のみ追加&削除する形で作成
  • JGroups によるノード間通信は「S3_PING」を利用

Keycloak の AWS 向けカスタム Docker イメージを ECR にプッシュする

公式ガイドのイメージでは、以下のような設定を想定したDockerイメージとなっている。

  • DBにpostgresを利用する設定
  • ノード間通信にUDPマルチキャストを利用
  • ヘルスチェックエンドポイントを有効にする環境変数を設定

しかし、AWSではUDPマルチキャスト使えない仕様であること、
コンテナの環境変数はできる限りECSタスク定義で設定するようにしたいということなど踏まえ、
以下のように変更したDockerfileを用いてAWS環境用にイメージをビルドする。

Dcokerfile

FROM quay.io/keycloak/keycloak:latest as builder

WORKDIR /opt/keycloak

# for demonstration purposes only, please make sure to use proper certificates in production instead
RUN keytool -genkeypair -storepass password -storetype PKCS12 -keyalg RSA -keysize 2048 -dname "CN=server" -alias server -ext "SAN:c=DNS:localhost,IP:127.0.0.1" -keystore conf/server.keystore
RUN /opt/keycloak/bin/kc.sh build

FROM quay.io/keycloak/keycloak:latest
COPY --from=builder /opt/keycloak/ /opt/keycloak/

#add jgroup-aws.jar and depencies
ADD --chown=keycloak:keycloak https://repo1.maven.org/maven2/org/jgroups/aws/jgroups-aws/2.0.1.Final/jgroups-aws-2.0.1.Final.jar /opt/keycloak/providers/jgroups-aws-2.0.1.Final.jar
ADD --chown=keycloak:keycloak https://repo1.maven.org/maven2/com/amazonaws/aws-java-sdk-core/1.12.410/aws-java-sdk-core-1.12.410.jar  /opt/keycloak/providers/aws-java-sdk-core-1.12.410.jar
ADD --chown=keycloak:keycloak https://repo1.maven.org/maven2/com/amazonaws/aws-java-sdk-s3/1.12.410/aws-java-sdk-s3-1.12.410.jar   /opt/keycloak/providers/aws-java-sdk-s3-1.12.410.jar
ADD --chown=keycloak:keycloak https://repo1.maven.org/maven2/joda-time/joda-time/2.12.2/joda-time-2.12.2.jar  /opt/keycloak/providers/joda-time-2.12.2.jar

ENTRYPOINT ["/opt/keycloak/bin/kc.sh"]

AWS 向けに S3_PING を用いたクラスタ構成設定を追加

Keycloakでは、JGroupsを使ってクラスタ構成する各ノードのディスカバリを行う仕組みとなっている。
デフォルト設定ではUDPを使う設定となっており、AWSでも利用可能な方式に変更する。
本記事ではS3_PINGという方法を設定してみる。

S3_PINGを利用するにあたり、JGroups AWSというライブラリをKeycloakに適用する必要がある。
下記のDockerfileへの追加内容は、Keycloakのprovidersフォルダへ必要なjarファイルをあらかじめ追加しておく設定である。これによりコンテナ起動時にprovidersに追加されているjarを取り込み、Keycloakを起動することができるようになっている。

Dcokerfile抜粋

#add jgroup-aws.jar and depencies
ADD --chown=keycloak:keycloak https://repo1.maven.org/maven2/org/jgroups/aws/jgroups-aws/2.0.1.Final/jgroups-aws-2.0.1.Final.jar /opt/keycloak/providers/jgroups-aws-2.0.1.Final.jar
ADD --chown=keycloak:keycloak https://repo1.maven.org/maven2/com/amazonaws/aws-java-sdk-core/1.12.410/aws-java-sdk-core-1.12.410.jar  /opt/keycloak/providers/aws-java-sdk-core-1.12.410.jar
ADD --chown=keycloak:keycloak https://repo1.maven.org/maven2/com/amazonaws/aws-java-sdk-s3/1.12.410/aws-java-sdk-s3-1.12.410.jar   /opt/keycloak/providers/aws-java-sdk-s3-1.12.410.jar
ADD --chown=keycloak:keycloak https://repo1.maven.org/maven2/joda-time/joda-time/2.12.2/joda-time-2.12.2.jar  /opt/keycloak/providers/joda-time-2.12.2.jar

本記事ではS3_PINGを利用しているがコンテナ環境だとDNS_PINGを使うケースが多い。
ECSの場合、ECS Service Discoveryを使うことでRoute 53に最大8ノードまで登録してくれるため、8ノードクラスタ内であればDNS_PINGが利用できる。
また、EKSの場合、k8sのDNSを利用することができます。

コンテナビルド

上記で作成したDockerfileをdocker buildでビルドを実行する。
正常にビルドされたか確認したい場合はdocker imagesでビルドしたイメージが表示されることを確認すると良い。

ECR でプライベートリポジトリを作成

AWS管理コンソールからECRのリポジトリを事前に作成する。
特に難しい要素はなく、可視性設定(今回はプライベート)とリポジトリ名を指定する程度でOK。

コンテナを ECR リポジトリへプッシュ

ビルド済コンテナイメージを作成したECRのリポジトリへdocker pushする。これでコンテナの準備は完了である。あとはコンテナ実行環境をAWSで構築していく。

ECRリポジトリ向けへのビルド~プッシュの方法については、作成したリポジトリのページにある「プッシュコマンドの表示」ボタンから参照可能

Fargate を用いた Keycloak 環境を AWS に構築する。

AWS環境についてはFargateを利用することも初めてだったこともあり、コンソールを利用してGUIベースで以下の構成を作成した。後々はCloudFormationでテンプレート化しておき、検証内容に応じてカスタマイズして爆速でKeycloakを構築できるようにしていきたい。

VPC、ELB などの作成

VPC関連

最近はVPCを作成する際に作成するリソースに「VPCなど」を選択すると、VPCサブネット、各種GWを一気に作成できるようになっている。今回はこちらを利用して上記の構成のVPC、サブネット、各種GWをサクッと作成。

ELB

各コンテナにリクエストを分散するため、ALBを設置して外向けのエンドポイントとした。
ECS FargateへALBからリクエストを転送する際の注意点として、ターゲットグループのタイプをIP addressで作成する必要がある。これはECS Fargate利用時のタスクネットワークモードがawsvpc固定となる。awsvpcモードの場合、各コンテナがENIが付与されて管理されるようになるため、ターゲットグループもIPでの指定が必要となる。

セキュリティグループ

セキュリティグループに関しては概ね以下辺りを開通できるように適宜設定が必要

  • コンテナ ⇔ RDS(3306)
  • コンテナ ⇔ ALB(8080)
  • コンテナ ⇔ コンテナ(7800)※S3_PING用
  • Any    ⇔ ALB(80,443)

IAMロール

ロールは以下のロールが最低限必要となる。

  • コンテナ(タスク)用のロール:S3への操作を可能とするポリシーを適用したロール
  • クラスタ用ロール:AmazonECSTaskExecutionRolePolicyを適用したロール

Aurora(MySQL)上に作成および Keycloak 用データベースの作成

次にKeycloakが利用するデータベースとしてAuroraを作成する。こちらもRDSサービスのコンソール上からポチポチして作成する。検証用のため、開発用かつマルチAZはなしで問題ない。
Auroraが立ち上がったらMysqlのクライアントソフトなどから接続し、以下のコマンドを実行してKeycloak用のDBを作成する。

msqlコマンド

# DBを作成(keycloakという名称ので作成した例)
create database keycloak character set utf8 collate utf8_unicode_ci;

# DB用のユーザーを作成(keycloak_user:keycloak-passで作成した例)
create user 'keycloak_user' identified by 'keycloak-pass'

# 作成したDBへ権限付与
grant all privileges on keycloak.* to 'keycloak_user';

MySQLデータベースへの接続はEC2のSSMやCloud9などでMysqlクライアントをインストールすると、AWS環境内で完結することができる。

S3_PING 用の S3 を作成

今回S3は各コンテナから一つのファイルへのRead/Write処理が発生するだけであるため、単純なプライベートバケットを一つ作成すればよい。

ECS(クラスター、タスク定義、サービス)を作成

最後にECS Fargateの各種設定する。

クラスター

以下の内容でクラスターを作成する。

パラメータ名 設定内容 補足
クラスタ名

任意でOK

VPC

作成済VPC

サブネット

プライベートサブネット2つ指定

クラスタを配置したいサブネットを指定

名前空間

デフォルト

デフォルトでクラスタ名が設定される

インフラストラクチャ

Fargare

ここをEC2に変更するクラスタようにEC2インスタンスが起動される

パラメータ名 設定内容 補足
クラスタ名

任意でOK

VPC

作成済VPC

サブネット

プライベートサブネット2つ指定

クラスタを配置したいサブネットを指定

名前空間

デフォルト

デフォルトでクラスタ名が設定される

インフラストラクチャ

Fargare

ここをEC2に変更するクラスタようにEC2インスタンスが起動される

タスク定義

以下の内容でタスク定義を作成する。

パラメータ名 設定内容 補足
タスク名義ファミリー

任意でOK

コンテナ

-

 名前

任意でOK

 イメージURI

ECRのリポジトリURI

 ポートマッピング-1

-

  コンテナポート

8080

Keycloakサービス用ポート

  プロトコル

TCP

  ポート名

適当でOK

  アプリケーションプロトコル

HTTP

 ポートマッピング-2

-

  コンテナポート

7800

S3_PING用ポート

  プロトコル

TCP

  ポート名

適当でOK

  アプリケーションプロトコル

None

 環境変数

-

KEYCLOAK_ADMIN

admin

管理コンソールAdminアカウント

KEYCLOAK_ADMIN_PASSWORD

admin

管理コンソールAdminパスワード

  KC_DB

mysql

DBの種類

  KC_DB_URL

jdbc:mysql://db_domain:3306/msqlデータベース名

jdbc形式でDBの接続先を指定

KC_DB_USERNAME

keycloak_user

msqlデータベースユーザー名

KC_DB_PASSWORD

keycloak-pass

msqlデータベースユーザーパスワード

KC_HTTP_ENABLED

true

HTTPリスナを有効化

KC_HOSTNAME_STRICT

false

ホスト名検証を無効化

  KC_HOSTNAME

ホスト名を設定

今回だとABLのドメイン

KC_ADMIN_HOSTNAME

ホスト名を設定

今回だとABLのドメイン

  KC_CACHE

ispn

クラスタ構成を有効化

KC_CACHE_STACK

ec2

S3_PINGを利用

KC_HEALTH_ENABLED

true

ヘルスチェックURLの有効化

JAVA_OPTS_APPEND

-Djgroups.s3.region_name=S3リージョン -
Djgroups.s3.bucket_name=S3バケット名

jgroupで利用するJAVAオプション

 Dockers設定-オプション

  コマンド

start

kc.shの実行オプション

環境

-

 タスクサイズ

-

  CPU

本検証では1 vCPU

  メモリ

本検証では2GB

 タスクロール

作成済ロールを指定

 タスク実行ロール

作成済ロールを指定

パラメータ名 設定内容 補足
タスク名義ファミリー

任意でOK

コンテナ

-

 名前

任意でOK

 イメージURI

ECRのリポジトリURI

 ポートマッピング-1

-

  コンテナポート

8080

Keycloakサービス用ポート

  プロトコル

TCP

  ポート名

適当でOK

  アプリケーションプロトコル

HTTP

 ポートマッピング-2

-

  コンテナポート

7800

S3_PING用ポート

  プロトコル

TCP

  ポート名

適当でOK

  アプリケーションプロトコル

None

 環境変数

-

KEYCLOAK_ADMIN

admin

管理コンソールAdminアカウント

KEYCLOAK_ADMIN_PASSWORD

admin

管理コンソールAdminパスワード

  KC_DB

mysql

DBの種類

  KC_DB_URL

jdbc:mysql://db_domain:3306/msqlデータベース名

jdbc形式でDBの接続先を指定

KC_DB_USERNAME

keycloak_user

msqlデータベースユーザー名

KC_DB_PASSWORD

keycloak-pass

msqlデータベースユーザーパスワード

KC_HTTP_ENABLED

true

HTTPリスナを有効化

KC_HOSTNAME_STRICT

false

ホスト名検証を無効化

  KC_HOSTNAME

ホスト名を設定

今回だとABLのドメイン

KC_ADMIN_HOSTNAME

ホスト名を設定

今回だとABLのドメイン

  KC_CACHE

ispn

クラスタ構成を有効化

KC_CACHE_STACK

ec2

S3_PINGを利用

KC_HEALTH_ENABLED

true

ヘルスチェックURLの有効化

JAVA_OPTS_APPEND

-Djgroups.s3.region_name=S3リージョン -
Djgroups.s3.bucket_name=S3バケット名

jgroupで利用するJAVAオプション

 Dockers設定-オプション

  コマンド

start

kc.shの実行オプション

環境

-

 タスクサイズ

-

  CPU

本検証では1 vCPU

  メモリ

本検証では2GB

 タスクロール

作成済ロールを指定

 タスク実行ロール

作成済ロールを指定

タスクサイズのCPUやメモリは任意の値でOKだが、小さくしすぎるとKeycloakが正常に起動できないため、正常に起動しない場合は十分な値を設定すること。

サービス

以下の内容でサービスを作成する。
サービスの作成が完了すると定義に沿ってコンテナが自動的に起動される。

パラメータ名 設定内容 補足
環境

-

 既存クラスター

作成済クラスターを指定

 コンピューティングオプション

キャパシティプロバイダー戦略

 キャパシティプロバイダ戦略

カスタムを使用(アドバンス)

 キャパシティプロバイダ

FARGATE

 ベース

1

 ウェイト

1

デプロイ設定

-

 アプリケーションタイプ

サービス

タスクにするとバッチジョブ的な起動となる

 タスク定義

-

  ファミリー

作成済タスク定義

  リビジョン

利用するリビジョン

基本はLatestでOK

 サービス名

任意

 サービスタイプ

レプリカ

 必要なタスク

2

タスクを削除するときはここを0にする

ネットワーキング

-

 VPC

既存のVPCを指定

 サブネット

プライベートサブネット2つを指定

 セキュリティグループ

作成済セキュリティグループ

 パブリックIP

OFF

今回はALB経由でのアクセスのため

ロードバランシング

-

 ロードバランサーの種類

Application Load Balancer

 Application Load Balancer

既存のロードバランサーを使用

 ロードバランサー

作成済ロードバランサーを選択

 ロードバランス用のコンテナの選択

8080ポートの方を指定

 リスナー

既存のリスナーを使用

 リスナー

作成済リスナーを指定

 ターゲットグループ

既存のターゲットグループを選択

 ターゲットグループ名

作成済ターゲットグループを選択

 ヘルスチェックパス

/health

 ヘルスチェック猶予期間

120

コンテナ起動時の初回ヘルスチェックまでの猶予時間

パラメータ名 設定内容 補足
環境

-

 既存クラスター

作成済クラスターを指定

 コンピューティングオプション

キャパシティプロバイダー戦略

 キャパシティプロバイダ戦略

カスタムを使用(アドバンス)

 キャパシティプロバイダ

FARGATE

 ベース

1

 ウェイト

1

デプロイ設定

-

 アプリケーションタイプ

サービス

タスクにするとバッチジョブ的な起動となる

 タスク定義

-

  ファミリー

作成済タスク定義

  リビジョン

利用するリビジョン

基本はLatestでOK

 サービス名

任意

 サービスタイプ

レプリカ

 必要なタスク

2

タスクを削除するときはここを0にする

ネットワーキング

-

 VPC

既存のVPCを指定

 サブネット

プライベートサブネット2つを指定

 セキュリティグループ

作成済セキュリティグループ

 パブリックIP

OFF

今回はALB経由でのアクセスのため

ロードバランシング

-

 ロードバランサーの種類

Application Load Balancer

 Application Load Balancer

既存のロードバランサーを使用

 ロードバランサー

作成済ロードバランサーを選択

 ロードバランス用のコンテナの選択

8080ポートの方を指定

 リスナー

既存のリスナーを使用

 リスナー

作成済リスナーを指定

 ターゲットグループ

既存のターゲットグループを選択

 ターゲットグループ名

作成済ターゲットグループを選択

 ヘルスチェックパス

/health

 ヘルスチェック猶予期間

120

コンテナ起動時の初回ヘルスチェックまでの猶予時間

ヘルスチェック猶予期間はコンテナの起動にかかる時間を確認して適宜変更すること。

動作検証

まず、コンテナ起動時に2つのコンテナが正しくクラスタを実現できているか見ていく。
以下起動時のコンテナログの抜粋であるが、メンバーとして2つのコンテナのIPが出力されている。

コンテナログ抜粋

2023-04-18 07:41:13,519 INFO [org.infinispan.LIFECYCLE] (jgroups-6,ip-192-168-141-217-53361) [Context=org.infinispan.COUNTER] ISPN100002: Starting rebalance with members [ip-192-168-146-54-55433, ip-192-168-141-217-53361], phase READ_OLD_WRITE_ALL, topology id 2
2023-04-18 07:41:13,526 INFO [org.infinispan.LIFECYCLE] (jgroups-6,ip-192-168-141-217-53361) [Context=org.infinispan.COUNTER] ISPN100010: Finished rebalance with members [ip-192-168-146-54-55433, ip-192-168-141-217-53361], topology id 2

また、S3に作成されたファイルを確認してみる。
こちらにも同様のIPで2つの情報が登録されており、正常にクラスタとして認識してS3_PINGにて通信ができていることが確認取れた。

.listファイル

ip-192-168-146-54-55433 	07f2e6ee-582f-454a-924d-13e3bc9b4d69 	192.168.146.54:7800 	T
ip-192-168-141-217-53361 	1c165785-b671-4c0a-9f3a-56643e9d69c1 	192.168.141.217:7800 	F

次に、Keycloakのアプリの動作確認を行う。
アプリの動作確認には、Keycloakに付属するログインユーザの個人情報画面を利用する。

  1. ALBのDNS名+/realms/master/account/#/personal-infoにアクセスし、ログイン画面を表示
  2. admin でログイン(パスワードも同じ)
  3. 個人情報画面が表示される
  4. ECSの実行中タスクを1つ強制ストップさせる
  5. 再ログインが発生しないで個人情報画面内を遷移できることを確認
  6. ECSにより自動的に再度コンテナが起動しきるのを待つ(CloudWatch logsでチェック)
  7. 最初からあったもう1つの実行中タスクを強制ストップさせる
  8. 再ログイン発生しないでプロフィール画面内を遷移できることを確認

上記のような操作をし、コンテナを1台ずつ落としても再ログイン操作が発生せずに操作可能なことが確認できたため、クラスタ構成が正常に動作していることが確認できた。

最後に

今回QuarkusベースのKeycloakでAWS上にクラスタ構成を構築してみたが、Keycloak自体の設定方法では特に難しい点はなく案外あっさり動いたという感想であった(その分AWSの設定でハマったけど。。。)

次の機会があれば、KubernetesやOpenShift利用してKeycloakを構築してECS比較しながら検証してみたい。

お気軽にお問い合わせください
オープンソースに関するさまざまな課題、OpenStandiaがまるごと解決します。
下記コンテンツも
あわせてご確認ください。