Open3

Docker で動いている Keycloak の H2 データベースを Postgres に移行する

hrkohrko

TL;DR

  • Keycloak コンテナのエントリーポイントを一時的に /bin/sh -c "while true; do sleep 10000; done" に変える
  • コンテナ内のシェルで kc.sh exportkc.sh import を実行する

本編

うっかり H2 データベースで本番運用してしまった Keycloak のデータベースを Postgres に切り替えたい。

そのためには kc.sh export コマンドでレルムをエクスポートし、データベースを切り替えた上で kc.sh import を実行する必要がある。しかし、これらのコマンドは Keycloak を停止しないと実行できないので、Docker で動かしている Keycloak でこの作業を行うには少し工夫が必要になる。
(同じような問題で困ってる人もいるっぽい)

hrkohrko

参考までに、自分の環境では以下のような誤った docker-compose.yml で Keycloak を実行していた(ドメイン名と初期パスワードは実際は違う値)。

Postgres を使用するようにしていたはずが、設定する環境変数が間違っており、実際には H2 データベースが利用されていた[1]

docker-compose.yml
volumes:
  postgres_data:
services:
  postgres:
    image: postgres
    volumes:
      - postgres_data:/var/lib/postgresql/data
    environment:
      POSTGRES_DB: keycloak
      POSTGRES_USER: keycloak
      POSTGRES_PASSWORD: password
    restart: unless-stopped
  keycloak:
    image: quay.io/keycloak/keycloak:20.0
    entrypoint: /opt/keycloak/bin/kc.sh start --auto-build
    environment:
      DB_VENDOR: POSTGRES
      DB_ADDR: postgres
      DB_DATABASE: keycloak
      DB_USER: keycloak
      DB_SCHEMA: public
      DB_PASSWORD: password
      KEYCLOAK_ADMIN: admin
      KEYCLOAK_ADMIN_PASSWORD: changeme
      KC_HOSTNAME: "kc.example.com"
      KC_HOSTNAME_ADMIN: "kcadm.example.com"
      KC_PROXY: "edge"
    ports:
      - 8080:8080
    depends_on:
      - postgres
    restart: unless-stopped
脚注
  1. しかも H2 データベースのディレクトリは永続化されていないので、docker compose down でデータが消える仕様になっていた… (とはいえ、docker ホストマシン自体のバックアップはあるので完全にデータを失うことはない) ↩︎

hrkohrko

作業メモ

これは手順書ではなく自分が行った作業の記録なので、参考にする場合は十分に注意を払うこと。(できるだけ環境依存の手順を書かないようにはしていますが。)

エクスポート

コンテナを止めて、H2 データベースのデータをホストにコピーした。

# docker stop keycloak_keycloak_1
# docker cp keycloak_keycloak_1:/opt/keycloak/data/h2 /root/h2
# chmod 777 /root/h2
# chmod 666 /root/h2/*

docker-compose.yml を以下のように書き換えた:

15c15,19
<     entrypoint: /opt/keycloak/bin/kc.sh start --auto-build
---
>     volumes:
>       - /root/h2:/opt/keycloak/data/h2
>     # entrypoint: /opt/keycloak/bin/kc.sh start --auto-build
>     # dummy entrypoint to prevent keycloak from starting
>     entrypoint: /bin/sh -c "while true; do sleep 10000; done"

これで、H2 データベースの永続化ができた。さらに、エントリーポイントを sleep コマンドで上書きすることで、コンテナの起動時に Keycloak が開始されるのを防ぐようにした。

コンテナを作り直した。

(host)# docker compose down
(host)# docker compose up -d

レルムをエクスポートした。

(host)# docker exec -it keycloak_keycloak_1 bash
(keycloak)# cd /opt/keycloak/
(keycloak)# bin/kc.sh export --file kc_export.json
(keycloak)# exit
(host)# docker cp keycloak_keycloak_1:/opt/keycloak/kc_export.json /root/kc_export.json

インポート

docker-compose.yml を以下のように書き換えた:

16c16
<       - /root/h2:/opt/keycloak/data/h2
---
>       - /root/kc_export.json:/opt/keycloak/kc_export.json:ro
21,26c21,24
<       DB_VENDOR: POSTGRES
<       DB_ADDR: postgres
<       DB_DATABASE: keycloak
<       DB_USER: keycloak
<       DB_SCHEMA: public
<       DB_PASSWORD: password
---
>       KC_DB: postgres
>       KC_DB_URL: jdbc:postgresql://postgres/keycloak
>       KC_DB_USERNAME: keycloak
>       KC_DB_PASSWORD: password

ボリュームを書き換えて、エクスポートしたレルムのファイル kc_export.json をコンテナ内に読み込み専用でマウントした。また、データベースの指定を正しい環境変数に書き換え、データの格納先が Postgres になるようにした。

コンテナを作り直した。

(host)# docker compose down
(host)# docker compose up -d

レルムをインポートした。

(host)# docker exec -it keycloak_keycloak_1 bash
(keycloak)# cd /opt/keycloak/
(keycloak)# bin/kc.sh build  # 環境変数で指定した設定を反映するのに必要
(keycloak)# bin/kc.sh import --file kc_export.json 
(keycloak)# exit

動作確認

docker-compose.yml を以下のように書き換えた:

15,19c15
<     volumes:
<       - /root/kc_export.json:/opt/keycloak/kc_export.json:ro
<     # entrypoint: /opt/keycloak/bin/kc.sh start --auto-build
<     # dummy entrypoint to prevent keycloak from starting
<     entrypoint: /bin/sh -c "while true; do sleep 10000; done"
---
>     entrypoint: /opt/keycloak/bin/kc.sh start --auto-build

kc_export.json のマウントはもう不要なので削除した。また、ダミーのエントリーポイントを削除し、元のエントリーポイントに戻した。これで通常通り Keycloak が起動するようになる。

コンテナを作り直した。

(host)# docker compose down
(host)# docker compose up -d

ブラウザで Keycloak の管理画面や Keycloak で認証するサービスにアクセスできることが確認できた。