18
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Keycloak by OpenStandiaAdvent Calendar 2023

Day 15

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

Last updated at Posted at 2023-12-14

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_image.PNG

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
コンテナ -
  名前 任意で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 コンテナ起動時の初回ヘルスチェックまでの猶予時間

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

動作検証

まず、コンテナ起動時に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比較しながら検証してみたい。

参考資料

 https://www.keycloak.org/server/containers
 https://www.keycloak.org/server/all-config
 https://github.com/jgroups-extras/jgroups-aws
 https://infinispan.org/docs/stable/titles/embedding/embedding.html#jgroups-extras-properties_cluster-transport
 https://qiita.com/wadahiro/items/0837729e7c57becbfd06
 https://qiita.com/yoonis/items/4f4a9df0f6f8e858bd4a

18
4
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
18
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?