【keycloak on Fargate 1】keycloakをFargateに構築してみる

# やること
- ECS on Fargateにkeycloakをクラスタ構成でデプロイする

# 構成図
本記事で構築するリソースは以下の通り。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/992414/94c78772-7677-51d9-f747-36fddb7ce38d.png)


# keycloakのDockerイメージをECRにPush
1. 以下のDockerfileをECRにPushする。
（参照：https://qiita.com/s-takino/items/c666c0a1fce10dd30843）
    ```Dockerfile
    # ビルド環境の準備
    FROM quay.io/keycloak/keycloak:latest as builder
    
    WORKDIR /opt/keycloak
    # 検証環境のため簡易的な証明書を作成
    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
    # Keycloakのビルドスクリプトを実行して、Keycloakをビルド
    RUN /opt/keycloak/bin/kc.sh build
    
    # 本番環境の設定
    FROM quay.io/keycloak/keycloak:latest
    # ビルドステージから生成されたファイルをコピー
    COPY --from=builder /opt/keycloak/ /opt/keycloak/
    
    # JARファイルの追加
    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
    
    # エントリーポイント(kc.sh)の設定
    ENTRYPOINT ["/opt/keycloak/bin/kc.sh"]
    ```
    - 検証目的のため簡易的な自己証明書を利用（実運用環境では適切な証明書を使用すること）
    - 環境変数はFargate側で設定するためDockerfileでは未定義
    - keycloakのクラスタリングに必要なjarファイルをダウンロードして追加
        - UDPの使用がデフォルト設定だが、AWSでは対応していないため、「S3_ping」を使用する

2. ECRにPush
    - ECRの作成手順は本記事では割愛
    ```
    aws ecr get-login-password --region ap-northeast-1 | docker login --username AWS --password-stdin <アカウント名>.dkr.ecr.ap-northeast-1.amazonaws.com
    docker build -t <任意のイメージ名> .
    docker tag <任意のイメージ名>:latest <アカウント名>.dkr.ecr.ap-northeast-1.amazonaws.com/<リポジトリ名>:latest
    docker push <アカウント名>.dkr.ecr.ap-northeast-1.amazonaws.com/<リポジトリ名>:latest
    ```
    
# 周辺リソースの環境構築
1. VPCの作成
    VPCを一つと、パブリックサブネット、プライベートサブネットを二つずつ作成する。
    
2. セキュリティグループの作成
    ▷ ALB用SG
    |ルール|タイプ|プロトコル|ポート|ソース|
    |:-:|:-:|:-:|:-:|:-:|
    |インバウンド|HTTP|TCP|80|0.0.0.0/0|
    |   |HTTPS|TCP|443|0.0.0.0/0|
    |アウトバウンド|すべてのトラフィック|すべて|すべて|0.0.0.0/0|

    ▷ コンテナ用SG
    |ルール|タイプ|プロトコル|ポート|ソース|
    |:-:|:-:|:-:|:-:|:-:|
    |インバウンド|MYSQL/Aurora|TCP|7800|コンテナ用SGのID|
    |   |MYSQL/Aurora|TCP|8080|ALB用SGのID|
    |アウトバウンド|すべてのトラフィック|すべて|すべて|0.0.0.0/0|

    ▷ DB用SG
    |ルール|タイプ|プロトコル|ポート|ソース|
    |:-:|:-:|:-:|:-:|:-:|
    |インバウンド|MYSQL/Aurora|TCP|3306|コンテナ用SGのID|
    |アウトバウンド|すべてのトラフィック|すべて|すべて|0.0.0.0/0|
    
    
3. DB(RDS)の構築
    Cloud9や踏み台サーバをRDSと同一VPC内にデプロイし、以下のコマンドを実行
    ```
    # mysqlログイン
    mysql --user=admin --password -h <RDSのエンドポイント>

    # DB作成
    MySQL [(none)]> create database keycloak character set utf8 collate utf8_unicode_ci;

    # ユーザ作成
    MySQL [(none)]> create user 'dbuser' identified by 'dbpass';

    # ユーザにDBへの権限付与
    MySQL [(none)]> grant all privileges on keycloak.* to 'dbuser';
    ```
4. S3バケットの作成
    keycloakクラスタメンバーのリストの格納先となるS3バケットを作成しておく
    
5. ドメイン名、証明書の取得
    keycloakサーバ用へのアクセスをHTTPS化するためドメインと証明書を取得する。
    証明書は自己証明書を使うと500エラーが発生するためACM経由で発行する。

6. ALBの作成
    コンテナの前段に配置するALBを作成する。
    ▷ターゲットグループ
    | # | ## |  |
    |:-:|:-:|:-:|
    | 基本的な設定 | ターゲットタイプの選択 | IP |
    |   | ターゲットグループ名 | 任意の名称  |
    |   |プロトコル : ポート| HTTP: 8080 |
    |   |IP アドレスタイプ| IPv4|
    |   |VPC| 作成済みのVPCを選択 |
    |   |プロトコルバージョン|HTTP1|
    |ヘルスチェック|ヘルスチェックプロトコル| HTTP|
    |   |ヘルスチェックパス|/auth/admin|

    ▷ロードバランサー
    | # | ## |  |
    |:-:|:-:|:-:|
    |ロードバランサータイプ| |Application Load Balancer |
    |基本的な設定|ロードバランサー名| 任意の名称  |
    |   |スキーム|インターネット向け|
    |ネットワークマッピング |VPC|作成済みのVPCを選択|
    |   |VPC| 作成済みのVPCを選択 |
    |   |マッピング|パブリックサブネットを二つ選択する|
    |セキュリティグループ ||作成済みのSGを選択|
    |リスナーとルーティング|リスナー|プロトコル：HTTPS／ポート：443／ターゲットグループ：作成済みのターゲットグループを選択|
    |セキュアリスナーの設定 |セキュリティポリシー|ポリシー名：ELBSecurityPolicy-TLS13-1-2-2021-06|
    |   |デフォルト SSL/TLS サーバー証明書|作成済みのACM証明書を選択|

7. レコードセット登録
    Route53にてエイリアスレコードを使用して、作成したドメインとALBを紐づける。

# Fargateの作成
1. クラスター作成
    インフラストラクチャにAWS Fargateを選択して作成。
    ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/992414/1575431c-c1ff-8650-0fdb-5cacd3a69368.png)

    | # | ## |  |
    |:-:|:-:|:-:|
    | 基本的な設定 | ターゲットタイプの選択 | IP |
    |   | デフォルトの名前空間  | 任意の名称  |
    | インフラストラクチャ  |   | AWS Fargate  |


2. タスク定義作成
    - タスクロールは以下のポリシーを付与して作成する
        - AmazonEC2ContainerServiceRole
        - AmazonS3FullAccess
    - タスク実行ロールは以下のポリシーを付与して作成する
        - AmazonECSTaskExecutionRolePolicy

    | # | ## |  |
    |:-:|:-:|:-:|
    | タスク定義の設定  | タスク定義ファミリー名  | 任意の名称 |
    | インフラストラクチャの要件  | 起動タイプ  | AWS Fargate  |
    |   | OS、アーキテクチャ、ネットワークモード | Linux/X86_64  |
    |   | ネットワークモード  | awsvpc（既定）  |
    |   | タスクサイズ  | CPU：.5vCPU／メモリ:3GB  |
    |   | タスクロール  | 作製したロールを選択  |
    |   | タスク実行ロール  | 作製したロールを選択  |
    | コンテナ - 1 | コンテナの詳細  | 名前：任意の名称／イメージ URI：PushしたECRイメージURI／必須コンテナ：はい  |
    |   | プライベートレジストリ認証  | いいえ  |
    |   | ポートマッピング  | コンテナポート：8080／プロトコル：TCP／ポート名：http／アプリケーションプロトコル：HTTP   |
    |   |   | コンテナポート：7800／プロトコル：TCP／ポート名：none／アプリケーションプロトコル：None   |
    |   | 読み取り専用  | いいえ  |
    |   | 環境変数  | 下記表を参照 |
    |   | ログ収集の使用  | はい（値はデフォルト）  |
    | ストレージ- オプション  | 量  | 30 |

    環境変数

    | キー | 値 | 説明 |
    |:-:|:-:|:-:|
    | JAVA_OPTS_APPEND  | -Djgroups.s3.region_name=ap-northeast-1 -Djgroups.s3.bucket_name=<バケット名> | JGroups S3_PINGプロトコル用の追加Javaオプション |
    | KC_HOSTNAME | < keycloak サーバのドメイン名> | Keycloakのホスト名 |
    | KC_ADMIN_HOSTNAME  | < keycloak サーバのドメイン名> | 管理コンソール用のホスト名 |
    | KC_DB_URL  | jdbc:mysql://<RDSのエンドポイント>:3306/<作成したDB名> | MySQLデータベースのJDBC接続URL |
    | KC_DB_USERNAME  | <作成したDBユーザ名> | DB接続に使用するユーザ名 |
    | KC_DB_PASSWORD  | <作成したDBユーザのパスワード> | DB接続に使用するパスワード |
    | KC_CACHE  | ispn | KeycloakのキャッシュプロバイダーとしてInfinispanを使用 |
    | KC_CACHE_STACK  | ec2 | Infinispanキャッシュの設定スタックをEC2用に設定 |
    | KC_DB  | mysql | 使用するDBの種類 |
    | KC_HEALTH_ENABLED  | true | ヘルスチェックエンドポイントを有効化 |
    | KC_HOSTNAME_STRICT | false | リクエストのホスト名検証を無効化 |
    | KC_HTTP_ENABLED | true | HTTPプロトコルを有効化 |
    | KC_HTTP_PORT | 8080 | HTTP接続のポートを指定 |
    | KC_PROXY | edge | 前段にALBを配置するためEdgeプロキシ設定を有効化 |
    | KEYCLOAK_ADMIN | admin | 初期管理者ユーザ名 |
    | KEYCLOAK_ADMIN_PASSWORD | admin | 初期管理者ユーザのパスワード |

3. サービス作成
    - タスクロールは以下のポリシーを付与して作成する
    - タスク実行ロールは以下のポリシーを付与して作成する

    | # | ## |  |
    |:-:|:-:|:-:|
    | 環境 | 既存のクラスター | 作成済みのクラスターを選択 |
    |  | コンピューティングオプション | キャパシティープロバイダー戦略 |
    |   | キャパシティープロバイダー戦略 | カスタムを使用 (アドバンスト) |
    |   |  | キャパシティープロバイダー：FARGATE／ベース：1／ウェイト：1 |
    |   | プラットフォームバージョン | LATEST |
    | デプロイ設定 | アプリケーションタイプ | サービス |
    |   | タスク定義 | 作成済みのタスク定義を選択 |
    |   | サービス名 | 任意の名称  |
    | | サービスタイプ | レプリカ |
    | ネットワーキング | VPC | 作成済みのVPCを選択 |
    |   | サブネット | 作成済みのプライベートサブネットを選択 |
    |   | セキュリティグループ | 作成済みのSGを選択   |
    |   | パブリックIP | いいえ |
    | ロードバランシング | ロードバランサーの種類 | Application Load Balancer |
    |   | コンテナ | 8080:8080 |
    |  | ロードバランサー | 作成済みのALBを選択 |
    |  | ヘルスチェックの猶予期間 | 60 |
    |  | リスナー | 作成済みのリスナーを選択 |
    |  | ターゲットグループ | ターゲットグループ名：／作成済みのターゲットグループを選択／ヘルスチェックパス：/auth/admin／ヘルスチェックプロトコル：HTTPS |


# 動作確認
実際にアクセスしてみる。    
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/992414/ed789c31-2833-b195-bfe4-3c5d985470ee.png)
コンテナの環境変数で指定した初期管理者ユーザ名／パスワードにてログインする。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/992414/99540aa5-6420-b8d5-b7f9-e9c098eb8c02.png)
realmを作成してみる。画像赤枠の「Create realm」を押下。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/992414/a8b32c30-b564-1f53-ab08-f730a22eff79.png)
Realm nameに任意の名前を記入して作成。
作製したrealmは先程の選択タブより切り替え可能
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/992414/f905e613-00a8-87b1-dff5-55ff5f31f92d.png)
続けてUserを作成する。
Usersメニューより「Add User」ボタンを押下。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/992414/43d5c876-05de-e86a-167e-9f1340af1291.png)
任意の名前を記載し、Create。

最後にkeycloakが正常にクラスタ構成で動作しているか確認する。
ECSのサービスを確認するとタスクが二つ作成されていることが分かる。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/992414/0ee19d0d-e465-a118-f26b-5ee4ebf3d4f6.png)
S3に格納されたlistファイルも確認する。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/992414/a9b12b47-9b3c-5175-bf67-70532b20e023.png)
中身を見るとタスクのプライベートIPが表示されているため、正常にクラスタ構成にて動作していることが確認できる。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/992414/4b5ad187-0260-03e0-60d3-1578d19585b3.png)

DBでも確認。(見づらいのでメモ帳経由で添付)
▷MySQL [keycloak]> select * from USER_CONSENT;
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/992414/24f24daa-87e8-7897-f25b-3f10319a311b.png)
▷MySQL [keycloak]> select * from USER_ENTITY;
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/992414/09347e65-5980-6874-fc42-a844e40bbb0b.png)


# 最後に
Fargateは非常にやさしくアプリケーションをデプロイできるので助かりますね。

# 参考資料
https://qiita.com/s-takino/items/c666c0a1fce10dd30843
https://qiita.com/wadahiro/items/0837729e7c57becbfd06