👨🏻‍⚕️

KeycloakのDockerコンテナにヘルスチェックを設定する

2024/07/08に公開

シチュエーション

  • KeycloakのDockerイメージでコンテナの状態がHealthyか確認したい
  • Keycloakをdockerイメージで利用
    • versionは21.1.2
  • docker composeを利用

躓きポイントと調査

ファーストミート

keycloakのイメージにヘルスチェックが定義されていない

compose.yaml
services:
  app:
    # 省略
    depends_on:
      keycloak:
        condition: service_healty
  keycloak:
    image: quay.io/keycloak/keycloak:21.1.2
    # 省略
$ docker compose up
# 省略
Gracefully stopping... (press Ctrl+C again to force)
dependency failed to start: container keycloakのコンテナ名 has no healthcheck configured

調査1

https://www.keycloak.org/server/health

ヘルスチェックを有効化

  • Relevant optionsにEnv: KC_HEALTH_ENABLEDとあるので有効化
  • composeファイルでヘルスチェックを定義
  • ホスト側でエンドポイントを確認できるようにポートフォワーディング
compose.yaml
services:
  # 省略
  keycloak:
    image: quay.io/keycloak/keycloak:21.1.2
    environment:
      - KC_HEALTH_ENABLED=true
    ports:
      - "58080:8080"
      - "59000:9000"
    healthcheck:
      test: ["CMD", "curl", "-fs", "localhost:9000/health/ready", "||" , "exit 1"]
      interval: 3s
      retries: 30
    # 省略

コンテナがHealthyにならないので確認

$ curl localhost:59000/health/ready
curl: (56) Recv failure: Connection reset by peer

ポート9000が繋がらないので8080で確認

$ curl localhost:58080/health/ready
{
    "status": "UP",
    "checks": [
    ]
}%                              

Enable Keycloak Healthcheckのドキュメントはバージョン指定のドキュメントから飛んだが、最新しかリンクにない可能性あるのでバージョンによる差異はあるかもしれない

port: 8080へ変更

compose.yaml
services:
  # 省略
  keycloak:
    image: quay.io/keycloak/keycloak:21.1.2
    environment:
      - KC_HEALTH_ENABLED=true
    ports:
      - "58080:8080"
    healthcheck:
      test: ["CMD", "curl", "-fs", "localhost:8080/health/ready", "||" , "exit 1"]
      interval: 3s
      retries: 30
    # 省略

しかしUnhealthy

ヘルスチェックのログを確認

$ docker inspect keycloakのコンテナ名

State -> Health -> Logを確認

{
  "Start": "",
  "End": "",
  "ExitCode": -1,
  "Output": "OCI runtime exec failed: exec failed: unable to start container process: exec: \"curl\": executable file not found in $PATH: unknown"
}

curlが入っていなかった😇

調査2

https://www.keycloak.org/server/containers

If you try to install new software in a stage FROM quay.io/keycloak/keycloak, you will notice that microdnf, dnf, and even rpm are not installed. Also, very few packages are available, only enough for a bash shell, and to run Keycloak itself. This is due to security hardening measures, which reduce the attack surface of the Keycloak container.

rpmが入っていないのでインストールも単純ではない

解決策

  1. ドキュメントに従い中間イメージでcurlをインストールしてkeycloakのイメージで利用

    It is possible to install new RPMs if absolutely required, following this two-stage pattern established by ubi-micro:

  2. bashの/dev/tcpを利用

解決策1

https://www.keycloak.org/server/containers#_installing_additional_rpm_packages

ドキュメントに従いDockerfileを作成し、ついでにヘルスチェックも定義

Dockerfile
FROM registry.access.redhat.com/ubi9 AS ubi-micro-build
RUN mkdir -p /mnt/rootfs
RUN dnf install --installroot /mnt/rootfs curl --releasever 9 --setopt install_weak_deps=false --nodocs -y && \
    dnf --installroot /mnt/rootfs clean all && \
    rpm --root /mnt/rootfs -e --nodeps setup

FROM quay.io/keycloak/keycloak:21.1.1
COPY --from=ubi-micro-build /mnt/rootfs /

HEALTHCHECK --interval=3s --retries=30 CMD curl -fs localhost:8080/health/ready || exit 1

composeで実行

compose.yaml
services:
  # 省略
  keycloak:
    build:
      context: Dockerfileが置いてあるディレクトリ
    environment:
      - KC_HEALTH_ENABLED=true
    ports:
      - "58080:8080"
    # 省略

コンテナがHealthyになりました🎉

解決策2

途中からDockerfile作るの面倒だなと思っていたら別解のgistを見つけました

https://gist.github.com/sarath-soman/5d9aec06953bbd0990c648605d4dba07

使ったことはなかったがbashにnetcatに近いことができる機能があることを発見
コマンドを調べながら使い方を確認し、composeファイルにhealthcheckを定義します

compose.yaml
services:
  # 省略
  keycloak:
    image: quay.io/keycloak/keycloak:21.1.2
    environment:
      - KC_HEALTH_ENABLED=true
    ports:
      - "58080:8080"
    healthcheck:
      test: ["CMD", "/bin/bash", "-c", "exec 3<>/dev/tcp/localhost/8080; echo -e 'GET /health/ready HTTP/1.1\nhost: localhost\nConnection: close\n' >&3; grep '200 OK' <&3 || exit 1"]
      interval: 3s
      retries: 30
    # 省略

こちらでもコンテナがHealthyになりました🎉

どちらの案を利用したか

結局解決策1も2も少しトリッキーなので、記述量・ファイルが少ない2の方を採用しました

Discussion