Keycloak从v16迁移至v21后无法读取Realm及用户,Postgres连接失败
我们已将Keycloak v16容器化,现计划迁移至v21版本。使用Postgres数据库,其中已创建好Realm和用户,但升级至v21后,Keycloak显示Realm和用户为空,数据库数据仍存在。尝试配置连接Postgres容器后出现连接拒绝错误,错误信息显示尝试连接localhost:5432被拒绝。
相关配置
新Dockerfile
FROM quay.io/keycloak/keycloak:21.0.2 COPY themes/ /opt/jboss/keycloak/themes/ ADD ./standalone-ha.xml /opt/jboss/keycloak/standalone/configuration/standalone-ha.xm
standalone-ha.xml配置片段
<spi name="connectionsJpa"> <provider name="default" enabled="true"> <properties> <property name="dataSource" value="java:jboss/datasources/KeycloakDS"/> <property name="initializeEmpty" value="true"/> <property name="migrationStrategy" value="update"/> <property name="migrationExport" value="${jboss.home.dir}/keycloak-database-update.sql"/> <property name="schema" value="${env.DB_SCHEMA:public}"/> </properties> </provider> </spi>
修改后的docker-compose配置
db: image: postgres:13.2 ports: - "5432:5432" environment: POSTGRES_PASSWORD: 'xxx' # volumes: # - /Volumes/DATA/docker_volume/pgdata:/var/lib/postgresql/data keycloak: depends_on: - db image: mykeycloak environment: KC_DB: postgres KC_DB_ADDR: db KC_DB_DATABASE: keycloak KC_DB_USER: postgres KC_DB_SCHEMA: public KC_DB_PASSWORD: xxx KEYCLOAK_ADMIN: keycloak_admin KEYCLOAK_ADMIN_PASSWORD: xxx KEYCLOAK_FRONTEND_URL: http://localhost:8080/auth/ volumes: - ./common/keycloak/disable-theme-cache.cli:/opt/jboss/startup-scripts/disable-theme-cache.cli command: start-dev --http-relative-path /auth ports: - 8080:8080
错误信息
2023-06-22 10:18:30 2023-06-22 08:18:30,593 WARN [org.hibernate.engine.jdbc.env.internal.JdbcEnvironmentInitiator] (JPA Startup Thread: keycloak-default) HHH000342: Could not obtain connection to query metadata: org.postgresql.util.PSQLException: Connection to localhost:5432 refused. Check that the hostname and port are correct and that the postmaster is accepting TCP/IP connections. 2023-06-22 10:18:30 at org.postgresql.core.v3.ConnectionFactoryImpl.openConnectionImpl(ConnectionFactoryImpl.java:319) 2023-06-22 10:18:30 at org.postgresql.core.ConnectionFactory.openConnection(ConnectionFactory.java:49) 2023-06-22 10:18:30 at org.postgresql.jdbc.PgConnection.<init>(PgConnection.java:247) 2023-06-22 10:18:30 at org.postgresql.Driver.makeConnection(Driver.java:434) 2023-06-22 10:18:30 at org.postgresql.Driver.connect(Driver.java:291) 2023-06-22 10:18:30 at java.sql/java.sql.DriverManager.getConnection(DriverManager.java:681) 2023-06-22 10:18:30 at java.sql/java.sql.DriverManager.getConnection(DriverManager.java:229) 2023-06-22 10:18:30 at org.postgresql.ds.common.BaseDataSource.getConnection(BaseDataSource.java:103) 2023-06-22 10:18:30 at org.postgresql.xa.PGXADataSource.getXAConnection(PGXADataSource.java:49) 2023-06-22 10:18:30 at org.postgresql.xa.PGXADataSource.getXAConnection(PGXADataSource.java:35) 2023-06-22 10:18:30 at io.agroal.pool.ConnectionFactory.createConnection(ConnectionFactory.java:232) 2023-06-22 10:18:30 at io.agroal.pool.ConnectionPool$CreateConnectionTask.call(ConnectionPool.java:536) 2023-06-22 10:18:30 at io.agroal.pool.ConnectionPool$CreateConnectionTask.call(ConnectionPool.java:517) 2023-06-22 10:18:30 at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:264) 2023-06-22 10:18:30 at io.agroal.pool.util.PriorityScheduledExecutor.beforeExecute(PriorityScheduledExecutor.java:75) 2023-06-22 10:18:30 at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1134) 2023-06-22 10:18:30 at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:635) 2023-06-22 10:18:30 at java.base/java.lang.Thread.run(Thread.java:833) 2023-06-22 10:18:30 Caused by: java.net.ConnectException: Connection refused 2023-06-22 10:18:30 at java.base/sun.nio.ch.Net.pollConnect(Native Method) 2023-06-22 10:18:30 at java.base/sun.nio.ch.Net.pollConnectNow(Net.java:672) 2023-06-22 10:18:30 at java.base/sun.nio.ch.NioSocketImpl.timedFinishConnect(NioSocketImpl.java:542) 2023-06-22 10:18:30 at java.base/sun.nio.ch.NioSocketImpl.connect(NioSocketImpl.java:597) 2023-06-22 10:18:30 at java.base/java.net.SocksSocketImpl.connect(SocksSocketImpl.java:327) 2023-06-22 10:18:30 at java.base/java.net.Socket.connect(Socket.java:633) 2023-06-22 10:18:30 at org.postgresql.core.PGStream.createSocket(PGStream.java:241) 2023-06-22 10:18:30 at org.postgresql.core.PGStream.<init>(PGStream.java:98) 2023-06-22 10:18:30 at org.postgresql.core.v3.ConnectionFactoryImpl.tryConnect(ConnectionFactoryImpl.java:109) 2023-06-22 10:18:30 at org.postgresql.core.v3.ConnectionFactoryImpl.openConnectionImpl(ConnectionFactoryImpl.java:235) 2023-06-22 10:18:30 ... 17 more 2023-06-22 10:18:30
1. 移除自定义standalone-ha.xml配置
Keycloak v17+ 基于Quarkus重构,不再使用Wildfly时代的standalone-ha.xml配置文件。当前Dockerfile中添加该文件会覆盖默认配置,导致环境变量中的数据库连接参数失效,Keycloak仍尝试连接默认的localhost数据库。
修改Dockerfile,删除添加standalone-ha.xml的行:
FROM quay.io/keycloak/keycloak:21.0.2 COPY themes/ /opt/jboss/keycloak/themes/
2. 确保Postgres服务就绪
depends_on仅保证容器启动顺序,不确保Postgres服务完全可用。可通过以下方式解决:
方式一:给Postgres容器添加健康检查
db: image: postgres:13.2 ports: - "5432:5432" environment: POSTGRES_PASSWORD: 'xxx' POSTGRES_DB: keycloak # 提前创建目标数据库,避免Keycloak启动时创建失败 healthcheck: test: ["CMD-SHELL", "pg_isready -U postgres -d keycloak"] interval: 5s timeout: 5s retries: 5 # volumes: # - /Volumes/DATA/docker_volume/pgdata:/var/lib/postgresql/data
方式二:添加等待脚本
创建wait-for-postgres.sh脚本:
#!/bin/bash set -e host="$1" shift cmd="$@" until pg_isready -h "$host" -U "postgres"; do >&2 echo "Postgres is unavailable - sleeping" sleep 1 done >&2 echo "Postgres is up - executing command" exec $cmd
在docker-compose中挂载并使用:
keycloak: depends_on: - db image: mykeycloak environment: KC_DB: postgres KC_DB_ADDR: db KC_DB_DATABASE: keycloak KC_DB_USER: postgres KC_DB_SCHEMA: public KC_DB_PASSWORD: xxx KEYCLOAK_ADMIN: keycloak_admin KEYCLOAK_ADMIN_PASSWORD: xxx KEYCLOAK_FRONTEND_URL: http://localhost:8080/auth/ volumes: - ./common/keycloak/disable-theme-cache.cli:/opt/jboss/startup-scripts/disable-theme-cache.cli - ./wait-for-postgres.sh:/opt/jboss/keycloak/wait-for-postgres.sh command: ["/opt/jboss/keycloak/wait-for-postgres.sh", "db", "start-dev", "--http-relative-path", "/auth"] ports: - 8080:8080
3. 添加数据库迁移参数
Keycloak v21需明确指定迁移策略,确保自动升级v16的数据库结构:
在Keycloak的command中添加--spi-connections-jpa-migration-strategy=update参数:
command: start-dev --http-relative-path /auth --spi-connections-jpa-migration-strategy=update
4. 验证数据库连接参数
确认所有KC_DB开头的环境变量配置正确:
KC_DB=postgres:指定数据库类型为PostgreSQLKC_DB_ADDR=db:对应Docker Compose中的Postgres服务名KC_DB_DATABASE=keycloak:目标数据库名,需确保Postgres中已存在KC_DB_USER=postgres:数据库用户名KC_DB_PASSWORD=xxx:数据库密码KC_DB_SCHEMA=public:数据库schema
5. 启动验证
执行docker-compose down -v清理旧容器和卷,再执行docker-compose up --build启动服务。查看Keycloak日志,确认数据库连接成功且迁移脚本执行完成,即可看到原有Realm和用户数据。
内容的提问来源于stack exchange,提问作者Marko Zadravec

