前回こちらの記事でECSを使ってKeycloakのクラスタ構成を検証してみたが、今回は同じAWSのコンテナ関連サービスであるEKSをつかってKeycloakのクラスタ構成を構築し、動作確認してみる。
本記事でやること
- EKS環境設定
- マニュフェスト作成
- デプロイ
- 動作検証
前提条件
- Keycloak バージョン:21.1.1
- コンテナイメージは公式のものをそのまま利用
- JGroups によるノード間通信は「DNS_PING」を利用
- Kubernetesのマニュフェストファイルは公式で案内されているものをベースにカスタマイズ
EKS環境設定
Cloud Shellの設定
今回はkubenetes操作用の端末としてCloud Shell
を利用する。
こちらの記事を参考にさせていただき、以下のコマンドを実行してkubectlをインストールする。
# ディレクトリ作成
mkdir -p $HOME/.local/bin
cd $HOME/.local/bin
# kubectl のインストール
curl -LO https://storage.googleapis.com/kubernetes-release/release/v1.18.13/bin/linux/amd64/kubectl
chmod +x kubectl
# ホームディレクトリに戻る
cd $HOME
これでKubernetesの操作に必要なツールをCloudShell上に準備することができた。
クラスターの作成
AWSマネージメントコンソール上からクラスタを作成していく。
EKSの画面でクラスター
を選択するとクラスター一覧が表示され、その画面上にあるクラスターを作成
ボタンを押下して作成を開始する。
各設定にて変更した部分を抜粋して以下に記載する。
クラスターを設定
- 名前:任意のクラスタ名
- kubernetesバージョン:1.27(デフォルトを選択)
- クラスターサービスロール:
AmazonEKSClusterPolicy
、AmazonEKSServicePolicy
をアタッチしたロールを作成して選択
ネットワーキングを指定
- VPC:事前に作成したVPCを指定
- サブネット:事前に作成したサブネットを2つ指定
- クラスターエンドポイントアクセス:パブリック
ログ記録の設定
今回はすべてOFFとした。
アドオンを選択
デフォルトでインストールとなっている以下のみ選択
- kube-puroxy
- CoreDNS
- Amazon VPC CNI
選択したアドオン設定を構成する
特に変更せずデフォルトのバージョンを選択
各設定を入力・選択後に確認画面で作成
ボタンを押下するとクラスターの作成が開始される。
クラスター作成には数分かかるため、しばらく完了するまで待つ。
正常にクラスターが作成されると以下のようにステータスがアクティブでクラスター一覧に表示される。
eksctlを利用してコマンドベースでクラスター作成することも可能。
eksctlを利用する場合はツールを操作環境へインストールする必要がある。
ノードグループの作成
ノードグループはノードのプロビジョニングとライフサイクル管理を自動化してくれるもので、EKSでノードを使うためにこちらを作成する。
先ほど作成したクラスターを選択して、クラスターの詳細画面でコンピューティング
タブを選択し、ノードグループを追加
ボタンを押下しノードグループの設定画面を開く。
ノードグループの各設定にて変更した部分を抜粋して以下に記載する。
ノードグループを設定
- 名前:任意の名前を設定
- ノードIAMロール:
mazonEKSWorkerNodePolicy
、AmazonEC2ContainerRegistryReadOnly
、AWSAppSyncPushToCloudWatchLogs
、AmazonEKS_CNI_Policy
をアタッチしたロールを作成して選択
ノードグループのコンピューティング設定
- AMIタイプ:Amazon Linux (AL2_x86_64)
- キャパシティタイプ:On-Demand
- インスタンスタイプ:t3.medium
- ディスクサイズ:20 GiB
- ノードグループのスケーリング設定
- 希望のサイズ:1 ノード
- 最小サイズ:1 ノード
- 最大サイズ:1 ノード
- ノードグループの更新設定:数値 1 ノード
- ノードグループのネットワーク設定:クラスター設定指定したものと同じサブネットを指定
各設定を入力・選択後に確認画面で作成
ボタンを押下するとノードグループおよび指定したサイズのノードの作成が開始される。
こちらに関しても作成には数分かかるため、しばらく完了するまで待つ。
正常に作成されると上記のように1つのノードと1つのノードグループが表示される。
クラスターへ接続
Cloud Shellから作成したクラスターの操作ができるように設定する。こちらを参考にして、Cloud Shell上で以下のコマンドを実行する。
aws eks --region example_region update-kubeconfig --name cluster_name
正常にコマンドが実行できるとAdded new context ~
のようなメッセージが表示され、クラスターの kubeconfig ファイルが作成または更新される。
また、以下のコマンドを実行してノードの情報がみれるか確認する。
kubectl get node
ここまでの設定がすべて正しくできていると以下のようにノードの情報を確認できる。
NAME STATUS ROLES AGE VERSION
ip-192-168-4-94.ap-northeast-1.compute.internal Ready <none> 57m v1.27.1-eks-2f008fe
マニュフェスト作成
最初にマニュフェストファイルの全容は以下の通りで、こちらをkubectl apply
すればALBからDBまですべて起動するようになっている。
apiVersion: v1
kind: Service
metadata:
name: mysql-server
spec:
type: ClusterIP
ports:
- name: mysql
port: 3306
targetPort: 3306
protocol: TCP
selector:
app: mysql-server
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: mysql-server
spec:
selector:
matchLabels:
app: mysql-server
strategy:
type: Recreate
template:
metadata:
labels:
app: mysql-server
spec:
containers:
- image: mysql:5.7
name: mysql
resources:
env:
- name: MYSQL_USER
value: keycloak_user
- name: MYSQL_PASSWORD
value: keycloak_password
- name: MYSQL_ROOT_PASSWORD
value: password
- name: LANG
value: C.UTF-8
ports:
- containerPort: 3306
name: mysql
volumeMounts:
- name: mysql-server-initdb
mountPath: /docker-entrypoint-initdb.d
- name: mysql-server-conf
mountPath: /etc/mysql/conf.d
volumes:
- name: mysql-server-initdb
configMap:
name: mysql-server-initdb-config
- name: mysql-server-conf
configMap:
name: mysql-server-conf-config
---
apiVersion: v1
kind: ConfigMap
metadata:
name: mysql-server-initdb-config
data:
createdb.sql: |
CREATE DATABASE keycloak_db;
GRANT ALL ON keycloak_db.* TO keycloak_user;
---
apiVersion: v1
kind: ConfigMap
metadata:
name: mysql-server-conf-config
data:
custom.cnf: |
[mysqld]
character-set-server=utf8
---
apiVersion: v1
kind: Service
metadata:
name: keycloak
labels:
app: keycloak
spec:
ports:
- name: http
port: 8080
targetPort: 8080
selector:
app: keycloak
type: NodePort
---
apiVersion: v1
kind: Service
metadata:
name: keycloak-discovery
spec:
clusterIP: None
ports:
- name: "http"
port: 8080
protocol: "TCP"
targetPort: 8080
selector:
app: keycloak
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: keycloak
labels:
app: keycloak
spec:
replicas: 2
selector:
matchLabels:
app: keycloak
template:
metadata:
labels:
app: keycloak
spec:
containers:
- name: keycloak
image: quay.io/keycloak/keycloak:21.1.1
args: ["start"]
env:
- name: KEYCLOAK_ADMIN
value: "admin"
- name: KEYCLOAK_ADMIN_PASSWORD
value: "admin"
- name: KC_PROXY
value: "edge"
- name: KC_HTTP_ENABLED
value: "true"
- name: KC_HOSTNAME_STRICT
value: "false"
- name: KC_DB
value: "mysql"
- name: KC_DB_URL
value: "jdbc:mysql://mysql-server:3306/keycloak_db"
- name: KC_DB_USERNAME
value: "keycloak_user"
- name: KC_DB_PASSWORD
value: "keycloak_password"
- name: KC_CACHE
value: "ispn"
- name: KC_CACHE_STACK
value: "kubernetes"
- name: KC_HEALTH_ENABLED
value: "true"
- name: JAVA_OPTS_APPEND
value: "-Djgroups.dns.query=keycloak-discovery.default.svc.cluster.local"
ports:
- name: http
containerPort: 8080
readinessProbe:
httpGet:
path: /realms/master
port: 8080
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: keycloak-ingress
annotations:
alb.ingress.kubernetes.io/scheme: internet-facing
alb.ingress.kubernetes.io/subnets: subnet-xxx, subnet-xxx #your subnets
alb.ingress.kubernetes.io/listen-ports: '[{"HTTPS" :443}]'
alb.ingress.kubernetes.io/certificate-arn: arn:aws:acm:ap-northeast-1:xxxxxx:certificate/xxxx #your cert arn
spec:
ingressClassName: alb
rules:
- http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: keycloak
port:
number: 8080
非常に長いので個別に分けて説明していく。
MySQL
MySQLのコンテナとサービスを作成に関する記載は以下の通り。
MySQLはクラスター内でのみサービスを提供できれば良いため、サービスのタイプをClusterIP
としている。
kind: Service
metadata:
name: mysql-server
spec:
type: ClusterIP
ports:
- name: mysql
port: 3306
targetPort: 3306
protocol: TCP
selector:
app: mysql-server
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: mysql-server
spec:
selector:
matchLabels:
app: mysql-server
strategy:
type: Recreate
template:
metadata:
labels:
app: mysql-server
spec:
containers:
- image: mysql:5.7
name: mysql
resources:
env:
- name: MYSQL_USER
value: keycloak_user
- name: MYSQL_PASSWORD
value: keycloak_password
- name: MYSQL_ROOT_PASSWORD
value: password
- name: LANG
value: C.UTF-8
ports:
- containerPort: 3306
name: mysql
volumeMounts:
- name: mysql-server-initdb
mountPath: /docker-entrypoint-initdb.d
- name: mysql-server-conf
mountPath: /etc/mysql/conf.d
volumes:
- name: mysql-server-initdb
configMap:
name: mysql-server-initdb-config
- name: mysql-server-conf
configMap:
name: mysql-server-conf-config
また、以下のようにConfigMap
上にDBの初期設定とクエリを登録して、MySQL起動時に初期DBと初期ユーザーを作成するように設定。
apiVersion: v1
kind: ConfigMap
metadata:
name: mysql-server-initdb-config
data:
createdb.sql: |
CREATE DATABASE keycloak_db;
GRANT ALL ON keycloak_db.* TO keycloak_user;
---
apiVersion: v1
kind: ConfigMap
metadata:
name: mysql-server-conf-config
data:
custom.cnf: |
[mysqld]
character-set-server=utf8
Keycloak
Keycloakのコンテナとサービスを作成に関する記載は以下の通り。
クラスター構成を確認するため、replicas
の設定を2
にしてKeycloakの2台起動するように設定。
また、Keycloakノード間通信は「DNS_PING」を利用するため、KC_CACHE_STACK
をkubernetes
とJAVA_OPTS_APPEND
でjgroups.dns.query
を設定。こちらに設定する内容は次に説明するHeadless servicesを利用する。
apiVersion: v1
kind: Service
metadata:
name: keycloak
labels:
app: keycloak
spec:
ports:
- name: http
port: 8080
targetPort: 8080
selector:
app: keycloak
type: NodePort
---yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: keycloak
labels:
app: keycloak
spec:
replicas: 2
selector:
matchLabels:
app: keycloak
template:
metadata:
labels:
app: keycloak
spec:
containers:
- name: keycloak
image: quay.io/keycloak/keycloak:21.1.1
args: ["start"]
env:
- name: KEYCLOAK_ADMIN
value: "admin"
- name: KEYCLOAK_ADMIN_PASSWORD
value: "admin"
- name: KC_PROXY
value: "edge"
- name: KC_HTTP_ENABLED
value: "true"
- name: KC_HOSTNAME_STRICT
value: "false"
- name: KC_DB
value: "mysql"
- name: KC_DB_URL
value: "jdbc:mysql://mysql-server:3306/keycloak_db"
- name: KC_DB_USERNAME
value: "keycloak_user"
- name: KC_DB_PASSWORD
value: "keycloak_password"
- name: KC_CACHE
value: "ispn"
- name: KC_CACHE_STACK
value: "kubernetes"
- name: KC_HEALTH_ENABLED
value: "true"
- name: JAVA_OPTS_APPEND
value: "-Djgroups.dns.query=keycloak-discovery.default.svc.cluster.local"
ports:
- name: http
containerPort: 8080
readinessProbe:
httpGet:
path: /realms/master
port: 8080
Headless services
Headless servicesでは、各Pod の IP アドレスを DNS ラウンドロビンで返却できるため、こちらをKeycloakノード間通信は「DNS_PING」に利用する。
Headless servicesはclusterIP: None
を設定することで利用可能となる。
apiVersion: v1
kind: Service
metadata:
name: keycloak-discovery
spec:
clusterIP: None
ports:
- name: "http"
port: 8080
protocol: "TCP"
targetPort: 8080
selector:
app: keycloak
Ingress
IngressはAWSのALBを利用するため、ingressClassName: alb
を設定。
また、ALBをHTTPSで利用可能とするため、事前に発行した証明書をACMへインポートしておきマニュフェストで指定している。
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: keycloak-ingress
annotations:
alb.ingress.kubernetes.io/scheme: internet-facing
alb.ingress.kubernetes.io/subnets: subnet-xxx, subnet-xxx #your subnets
alb.ingress.kubernetes.io/listen-ports: '[{"HTTPS" :443}]'
alb.ingress.kubernetes.io/certificate-arn: arn:aws:acm:ap-northeast-1:xxxxxx:certificate/xxxx #your cert arn
spec:
ingressClassName: alb
rules:
- http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: keycloak
port:
number: 8080
デプロイ
AWS Load Balancer Controller アドオンのインストール
ALBを使ってKubernetesのIngressを作成する場合にこちらのアドオンをインストールしておく必要があるため、AWS公式の手順に沿ってAWS Load Balancer Controller アドオンをインストールする。
デプロイ
作成したマニュフェストファイルをCloud Shell上にアップロード(もしくはShell上で作成)してデプロイする。
以下のコマンドを実行することでマニュフェストに記載した内容を順次デプロイされる。
kubectl apply -f keycloak.yaml
マニュフェストの内容が正しい形式であれば、以下のようにそれぞれが作成される旨の出力が返ってくる。
service/mysql-server created
deployment.apps/mysql-server created
configmap/mysql-server-initdb-config created
configmap/mysql-server-conf-config created
service/keycloak created
service/keycloak-discovery created
deployment.apps/keycloak created
ingress.networking.k8s.io/keycloak-ingress created
正常にデプロイされているか以下のコマンドを実行して確認する。
kubectl get all
以下のようにすべてのPODのSTATUSがRunningになっていればIngress(ALB)以外のデプロイはOK。
NAME READY STATUS RESTARTS AGE
pod/keycloak-7d8f8949df-67894 1/1 Running 0 2m6s
pod/keycloak-7d8f8949df-zcx2g 1/1 Running 0 2m6s
pod/mysql-server-6c8d866586-s42rb 1/1 Running 0 2m6s
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
service/keycloak NodePort 10.100.3.101 <none> 8080:32549/TCP 2m6s
service/keycloak-discovery ClusterIP None <none> 8080/TCP 2m6s
service/kubernetes ClusterIP 10.100.0.1 <none> 443/TCP 6h28m
service/mysql-server ClusterIP 10.100.106.59 <none> 3306/TCP 2m6s
NAME READY UP-TO-DATE AVAILABLE AGE
deployment.apps/keycloak 2/2 2 2 2m6s
deployment.apps/mysql-server 1/1 1 1 2m6s
NAME DESIRED CURRENT READY AGE
replicaset.apps/keycloak-7d8f8949df 2 2 2 2m6s
replicaset.apps/mysql-server-6c8d866586 1 1 1 2m6s
次にIngressの状況を以下のコマンドで確認する
kubectl get ingress
以下のように表示されればIngressの作成も成功。ただし、ALBのサービスがActiveになるまでには少し時間がかかるため、適宜コンソール上でALBがActiveになっているかは確認が必要。
NAME CLASS HOSTS ADDRESS PORTS AGE
keycloak-ingress alb * k8s-default-keycloak-xxxxxxxxxx-xxxxxxxxxx.ap-northeast-1.elb.amazonaws.com 80 5m29s
動作検証
前回のECSの時と同様に確認していく、
まず、コンテナ起動時に2つのコンテナが正しくクラスタを実現できているか確認。
以下のコマンドを実行してコンテナの起動時のログを確認していく。
kubectl log コンテナ名
以下が出力されたコンテナログの抜粋であり、メンバーとして2つのコンテナ名が出力されている。
2023-06-02 08:14:38,055 INFO [org.infinispan.LIFECYCLE] (jgroups-16,keycloak-7d8f8949df-67894-17810) [Context=offlineSessions] ISPN100002: Starting rebalance with members [keycloak-7d8f8949df-67894-17810, keycloak-7d8f8949df-zcx2g-58694], phase READ_OLD_WRITE_ALL, topology id 4
2023-06-02 08:14:38,057 INFO [org.infinispan.LIFECYCLE] (jgroups-16,keycloak-7d8f8949df-67894-17810) [Context=offlineSessions] ISPN100010: Finished rebalance with members [keycloak-7d8f8949df-67894-17810, keycloak-7d8f8949df-zcx2g-58694], topology id 4
次に、Keycloakのアプリの動作確認を行う。
こちらもECSの時と同様にアプリの動作確認には、Keycloakに付属するログインユーザの個人情報画面を利用する。
1. ALBのDNS名+/realms/master/account/#/personal-infoにアクセスし、ログイン画面を表示
2. admin でログイン(パスワードも同じ)
3. 個人情報画面が表示される
4. マニュフェストファイルのKeyclaokのreplicasの値を1に変更してkubectl apply
を実行する。
5. 再ログインが発生しないで個人情報画面内を遷移できることを確認
6. マニュフェストファイルのKeyclaokのreplicasの値を2に戻してkubectl apply
を実行する。
7. 再ログインが発生しないで個人情報画面内を遷移できることを確認
8. マニュフェストファイルのKeyclaokのreplicasの値を1に変更してkubectl apply
を実行する。
9. 再ログイン発生しないでプロフィール画面内を遷移できることを確認
上記のような操作をし、Podのreplicasを変更しても再ログイン操作が発生せずに操作可能であり、クラスタ構成が正常に動作していることが確認できた
参考資料
https://www.keycloak.org/getting-started/getting-started-kube
https://repost.aws/ja/knowledge-center/eks-cluster-connection
https://docs.aws.amazon.com/ja_jp/eks/latest/userguide/alb-ingress.html