Dựng Keycloak bằng Docker, export/import realm
Tiếp đi: chạy Keycloak bằng Docker cho cả team dùng.
Bài trước đã thiết kế realm shop. Giờ dựng server thật, sao cho cả team chạy được cùng một cấu hình.
1. Bài toán
Team shop-app có 5 dev. Mỗi người cần một Keycloak local có sẵn realm shop, client, user test để chạy order-service.
2. Vấn đề
docker runtrơn dùng database H2 trong container: xóa container là mất hết realm.- Mỗi người tự click trong admin console: cấu hình lệch nhau, bug "máy mình chạy được".
- Không biết admin password đặt ở đâu, dễ commit nhầm.
3. Giải pháp
- Docker Compose: Keycloak + Postgres, volume giữ dữ liệu.
- Admin tạm qua biến môi trường
KC_BOOTSTRAP_ADMIN_USERNAME/KC_BOOTSTRAP_ADMIN_PASSWORD(Keycloak 26; bản cũ làKEYCLOAK_ADMIN). - Realm export ra JSON, commit vào git, container khởi động với
--import-realm.
start-dev | start | |
|---|---|---|
| Mục đích | Dev local | Production |
| HTTP | Cho phép HTTP | Bắt buộc HTTPS hoặc cấu hình proxy |
| Hostname | Không bắt buộc | Bắt buộc KC_HOSTNAME (hoặc tắt strict có chủ đích) |
| Theme/cache | Không cache, sửa theme thấy ngay | Cache bật |
| Build | Tự build mỗi lần | Nên kc.sh build trước (image tối ưu, --optimized) |
4. Code từng bước
shop-app/
├── infra/
│ ├── docker-compose.yml
│ ├── .env.example
│ └── keycloak/import/shop-realm.json
└── ...
Bước 1: .env.example (commit file mẫu, không commit .env):
# Chỉ dùng cho dev local. Production lấy từ secret manager.
KC_ADMIN_USER=admin
KC_ADMIN_PASSWORD=change-me
KC_DB_PASSWORD=change-me
Bước 2: docker-compose.yml
services:
postgres:
image: postgres:16
environment:
POSTGRES_DB: keycloak
POSTGRES_USER: keycloak
POSTGRES_PASSWORD: ${KC_DB_PASSWORD}
volumes:
- kc-db:/var/lib/postgresql/data
keycloak:
image: quay.io/keycloak/keycloak:26.0
command: ["start-dev", "--import-realm"]
environment:
KC_DB: postgres
KC_DB_URL: jdbc:postgresql://postgres:5432/keycloak
KC_DB_USERNAME: keycloak
KC_DB_PASSWORD: ${KC_DB_PASSWORD}
KC_BOOTSTRAP_ADMIN_USERNAME: ${KC_ADMIN_USER}
KC_BOOTSTRAP_ADMIN_PASSWORD: ${KC_ADMIN_PASSWORD}
KC_HEALTH_ENABLED: "true"
ports:
- "8080:8080"
volumes:
- ./keycloak/import:/opt/keycloak/data/import:ro
depends_on:
- postgres
volumes:
kc-db:
Bước 3: Tạo realm, client, user lần đầu (admin console http://localhost:8080/admin hoặc CLI):
docker compose exec keycloak bash -c '
/opt/keycloak/bin/kcadm.sh config credentials --server http://localhost:8080 \
--realm master --user "$KC_BOOTSTRAP_ADMIN_USERNAME" --password "$KC_BOOTSTRAP_ADMIN_PASSWORD"
/opt/keycloak/bin/kcadm.sh create users -r shop -s username=alice -s enabled=true \
-s email=alice@example.test -s emailVerified=true
/opt/keycloak/bin/kcadm.sh set-password -r shop --username alice --new-password "$TEST_USER_PASSWORD"
/opt/keycloak/bin/kcadm.sh add-roles -r shop --uusername alice --rolename customer'
Admin bootstrap chỉ là tạm: đăng nhập xong hãy tạo admin thật (hoặc nối IdP công ty) rồi xóa user bootstrap.
Bước 4: Export realm cho team
# Export khi server đã dừng phục vụ (chạy container tạm cùng DB)
docker compose run --rm keycloak export \
--dir /opt/keycloak/data/export --realm shop --users realm_file
# hoặc: Admin console → Realm settings → Action → Partial export (không có user, secret bị che)
Copy shop-realm.json vào infra/keycloak/import/ và commit. Dev mới chỉ cần:
cp .env.example .env
docker compose up -d
curl -s http://localhost:8080/realms/shop | jq .realm # "shop"
5. Cách hoạt động
docker compose up
|
v
Keycloak khởi động ── KC_DB=postgres ──> bảng realm, user, client...
|
+-- --import-realm: đọc /opt/keycloak/data/import/*.json
| realm "shop" CHƯA có → import
| realm "shop" ĐÃ có → bỏ qua (không ghi đè)
|
+-- master chưa có admin → tạo admin bootstrap từ KC_BOOTSTRAP_ADMIN_*
v
:8080/admin :8080/realms/shop :9000/health (management port)
6. So sánh cách đưa cấu hình realm
| Cách | Ưu | Nhược |
|---|---|---|
| Click admin console | Nhanh khi học | Không tái lập, không review |
--import-realm JSON | Đơn giản, hợp dev local | Chỉ import lần đầu, không cập nhật realm đã có |
| keycloak-config-cli / Terraform provider | Idempotent, cập nhật được, hợp CI/CD | Thêm công cụ |
7. Khi nào nên dùng / không nên dùng
- Nên:
start-dev+ import JSON cho local và integration test (Testcontainers có module Keycloak). - Không nên: dùng
start-dev, H2, hay admin bootstrap trên production; dùng import JSON để "sửa" realm production.
File realm export có cả client secret của order-service và password user test. Commit lên git như vậy có sao không?
Bạn lo đúng chỗ. Với dev local thì chấp nhận được nếu secret và user là giá trị test, không dùng ở môi trường khác. Nhưng nên làm sạch:
- Password user được lưu dạng hash trong export, nhưng vẫn nên chỉ có user test.
- Thay secret bằng placeholder. Keycloak hỗ trợ thay biến môi trường khi import:
"secret": "${ORDER_SERVICE_SECRET}". - Không bao giờ export realm production rồi commit.
{
"clientId": "order-service",
"publicClient": false,
"serviceAccountsEnabled": true,
"secret": "${ORDER_SERVICE_SECRET}"
}
Cách quản lý secret chung xem bài 11.7.
Mình thêm client mới vào shop-realm.json, restart container mà không thấy client đâu. Sao vậy?
Đúng hành vi: --import-realm bỏ qua realm đã tồn tại trong DB. Volume Postgres vẫn giữ realm cũ.
# Cách 1: dev local, xóa dữ liệu rồi import lại
docker compose down -v && docker compose up -d
# Cách 2: thêm thủ công bằng kcadm (không mất user đang test)
# Cách 3: dùng keycloak-config-cli để apply thay đổi idempotent
Trên môi trường dùng chung (staging, prod), thay đổi realm nên đi qua công cụ idempotent trong pipeline, không xóa DB.
Phỏng vấn hỏi cách dựng Keycloak và chia sẻ cấu hình realm thì trả lời sao?
Q1. start-dev và start khác nhau thế nào?
🇻🇳 Tiếng Việt:
start-devcho dev: cho HTTP, không cần hostname, tắt cache theme.startcho production: bắt buộc HTTPS hoặc proxy, cấu hình hostname, nên build image tối ưu trước rồi chạy với database ngoài như Postgres.
🇬🇧 English:
start-devis for development: plain HTTP, no hostname required, theme caching off.startis for production: it requires HTTPS or proxy settings and a hostname, and we should build an optimized image first and use an external database like Postgres.
Q2. Team chia sẻ cấu hình realm thế nào?
🇻🇳 Tiếng Việt:
Export realm ra JSON, thay secret bằng placeholder biến môi trường, commit vào repo, container chạy với
--import-realm. Import chỉ áp dụng khi realm chưa có, nên môi trường dùng chung em dùng keycloak-config-cli hoặc Terraform để apply thay đổi idempotent.
🇬🇧 English:
We export the realm to JSON, replace secrets with environment placeholders, commit it and start the container with
--import-realm. Import only applies when the realm does not exist yet, so for shared environments I use keycloak-config-cli or Terraform to apply changes idempotently.
Cách nhớ ngắn gọn
- Compose = Keycloak + Postgres + volume.
- Admin bootstrap qua
KC_BOOTSTRAP_ADMIN_*, dùng xong thì thay. --import-realm: chỉ import khi chưa có.- Secret trong JSON →
${ENV}.
Tự kiểm tra
Không khai báo KC_DB thì Keycloak dùng DB gì?
H2 file trong container (chỉ hợp dev). Xóa container là mất dữ liệu.
Thư mục import mặc định là gì?
/opt/keycloak/data/import.
Sửa JSON rồi restart không thấy thay đổi, vì sao?
Realm đã tồn tại trong DB, import bỏ qua. Xóa volume (local) hoặc dùng công cụ idempotent.
Biến môi trường admin bootstrap ở Keycloak 26 là gì?
KC_BOOTSTRAP_ADMIN_USERNAME và KC_BOOTSTRAP_ADMIN_PASSWORD.