Apache Syncope 4.1.3 Docker Compose 部署指南
使用官方镜像与 Docker Compose 部署 Apache Syncope 4.1.3 的完整指南,覆盖环境变量清单、编排文件编写、栈启动验证、安全密钥强制配置、数据持久化与常见故障排查,全部步骤与回显均来自真实环境实测。
Apache Syncope 4.1.3 Docker Compose 部署指南
一、架构说明
使用官方容器镜像编排部署 Apache Syncope 4.1.3,包含四个服务:
| 服务 | 镜像 | 实测镜像大小 | 容器内端口 | 宿主映射(实测) |
|---|---|---|---|---|
| db | postgres:16-alpine | 420 MB | 5432 | 不对外 |
| syncope(Core) | apache/syncope:4.1.3 | 1.64 GB | 8080 | 18080 |
| syncope-console(管理控制台) | apache/syncope-console:4.1.3 | 936 MB | 8080 | 28080 |
| syncope-enduser(用户自助界面) | apache/syncope-enduser:4.1.3 | 839 MB | 8080 | 38080 |
组件协作方式(实测确认):数据库使用外部 PostgreSQL(数据持久化,容器重启不丢数据,这是与 standalone 发行版内嵌临时库的关键差异);各组件通过 Keymaster 机制互相发现,本方案中 Keymaster 由 Core 自身的 REST 端点(/syncope/rest/keymaster)提供,无需额外组件。
官方容器镜像运行环境为 Java 25(实测容器内 Java 25.0.4),无需宿主机安装 JDK。
二、前置条件
宿主机需安装 Docker(实测 Docker 29.8.0)与 Compose 插件(实测 Compose v5.5.1):
docker version --format 'Client {{.Client.Version}} / Server {{.Server.Version}}'
docker compose version
Client 29.8.0 / Server 29.8.0
Docker Compose version v5.5.1
确认端口未被占用:18080、28080、38080。
ss -ltnp | grep -E ':18080|:28080|:38080'
无输出即端口可用。
三、获取官方镜像
逐一拉取(实测回显为镜像 ID 行):
docker pull apache/syncope:4.1.3
4.1.3: Pulling from apache/syncope
Digest: sha256:e6a5326d5e3a4a2b93d76c52a334797d3fa0ba4d22e6d18b16d68a5b54a1e0db
Status: Downloaded newer image for apache/syncope:4.1.3
docker.io/apache/syncope:4.1.3
docker pull apache/syncope-console:4.1.3
docker pull apache/syncope-enduser:4.1.3
docker pull postgres:16-alpine
官方还提供 apache/syncope-wa:4.1.3(认证中心)与 apache/syncope-sra:4.1.3(反向代理网关),本指南的基础架构不涉及;需要 SSO 认证中心时可按同一模式加入编排。
四、部署步骤
4.1 创建部署目录与环境变量文件
mkdir -p /opt/syncope
cd /opt/syncope
创建 .env 文件(Compose 变量来源,实测必须给 KEYMASTER_USERNAME/KEYMASTER_PASSWORD 非空值,否则 Core 启动崩溃,见 7.1):
cat > .env <<'EOF'
SYNCOPE_VERSION=4.1.3
KEYMASTER_USERNAME=anonymous
KEYMASTER_PASSWORD=anonymousKey
ANONYMOUS_USER=anonymous
ANONYMOUS_KEY=anonymousKey
EOF
4.2 编写 docker-compose.yml
创建 docker-compose.yml(基于官方 docker-compose-postgresql.yml 模板,加入实测必需的安全密钥环境变量与堆限制):
services:
db:
image: postgres:16-alpine
restart: always
environment:
POSTGRES_DB: syncope
POSTGRES_USER: syncope
POSTGRES_PASSWORD: syncope
syncope:
depends_on:
- db
image: apache/syncope:4.1.3
ports:
- "18080:8080"
restart: always
environment:
SPRING_PROFILES_ACTIVE: docker,postgresql,saml2
DB_URL: jdbc:postgresql://db:5432/syncope?stringtype=unspecified
DB_USER: syncope
DB_PASSWORD: syncope
DB_POOL_MAX: 20
DB_POOL_MIN: 5
OPENJPA_REMOTE_COMMIT: sjvm
KEYMASTER_ADDRESS: http://localhost:8080/syncope/rest/keymaster
KEYMASTER_USERNAME: ${KEYMASTER_USERNAME:-}
KEYMASTER_PASSWORD: ${KEYMASTER_PASSWORD:-}
SERVICE_DISCOVERY_ADDRESS: http://syncope:8080/syncope/rest/
ANONYMOUS_USER: ${ANONYMOUS_USER}
ANONYMOUS_KEY: ${ANONYMOUS_KEY}
SECURITY_ADMINPASSWORD: e553465e3d1ad816a8a8c33176a2ff00fb58f6f55502c89e7593242accc1b0d3c2a2b5fc300a085b5051ecbdd2195b05a53a198ec98d9f75d323cc3c563592f9
SECURITY_ADMINPASSWORDALGORITHM: SHA512
SECURITY_AESSECRETKEY: Qf7kP2mX9vLw4nR8tY6uJ3hB5dC1aZ0e
SECURITY_JWSKEY: Xk4tP8wR2mY6uL3vJ9qF5hD1aZ7cN0bS4gE8iK2oT6yUQzA5rW7eM1nV9xC3fG8Hd
JAVA_OPTS: "-Xms256m -Xmx512m"
syncope-console:
depends_on:
- syncope
image: apache/syncope-console:4.1.3
ports:
- "28080:8080"
restart: always
environment:
SPRING_PROFILES_ACTIVE: docker
KEYMASTER_ADDRESS: http://syncope:8080/syncope/rest/keymaster
KEYMASTER_USERNAME: ${KEYMASTER_USERNAME:-}
KEYMASTER_PASSWORD: ${KEYMASTER_PASSWORD:-}
SERVICE_DISCOVERY_ADDRESS: http://syncope-console:8080/syncope-console/
ANONYMOUS_USER: ${ANONYMOUS_USER}
ANONYMOUS_KEY: ${ANONYMOUS_KEY}
syncope-enduser:
depends_on:
- syncope
image: apache/syncope-enduser:4.1.3
ports:
- "38080:8080"
restart: always
environment:
SPRING_PROFILES_ACTIVE: docker
KEYMASTER_ADDRESS: http://syncope:8080/syncope/rest/keymaster
KEYMASTER_USERNAME: ${KEYMASTER_USERNAME:-}
KEYMASTER_PASSWORD: ${KEYMASTER_PASSWORD:-}
SERVICE_DISCOVERY_ADDRESS: http://syncope-enduser:8080/syncope-enduser/
ANONYMOUS_USER: ${ANONYMOUS_USER}
ANONYMOUS_KEY: ${ANONYMOUS_KEY}
说明:
- SECURITY_ADMINPASSWORD 的值为 q1w2e3r4 的 SHA512 十六进制摘要(生成方法见 7.2),容器默认密码为 password 且被强制检测,不修改则 REST 全部返回 500。
- SECURITY_AESSECRETKEY 固定 32 字符(AES-256);SECURITY_JWSKEY 在 HS512 算法下必须不少于 64 字符,否则启动失败(见 7.3、7.4)。生产环境请替换为自己生成的随机值。
- JAVA_OPTS 限制 Core 的 JVM 堆在 512 MB 内;四个服务相互独立,Console/Enduser 如需限制可各自添加。
4.3 校验并启动
校验编排文件语法:
docker compose config --quiet && echo CONFIG_OK
CONFIG_OK
启动全部服务:
docker compose up -d
[+] Running 5/5
✔ Network syncope_default Created
✔ Container syncope-db-1 Started
✔ Container syncope-syncope-1 Started
✔ Container syncope-syncope-console-1 Started
✔ Container syncope-syncope-enduser-1 Started
查看运行状态:
docker compose ps --format 'table {{.Name}}\t{{.Image}}\t{{.Status}}'
NAME IMAGE STATUS
syncope-db-1 postgres:16-alpine Up 20 seconds
syncope-syncope-1 apache/syncope:4.1.3 Up 20 seconds
syncope-syncope-console-1 apache/syncope-console:4.1.3 Up 20 seconds
syncope-syncope-enduser-1 apache/syncope-enduser:4.1.3 Up 20 seconds
4.4 等待 Core 就绪
Core 完全启动需 30-90 秒(首次启动含建表与数据初始化)。观察日志直到出现 Started:
docker compose logs syncope 2>&1 | grep 'Started SyncopeCoreApplication'
syncope-1 | 2026-10-12 02:26:08.364 INFO [ main] org.apache.syncope.core.starter.SyncopeCoreApplication : Started SyncopeCoreApplication in 26.605 seconds (process running for 28.527)
注意:Console 在 Core 完全启动(服务注册完成)前访问会短暂返回 500(No registered services for type CORE),Core 就绪后自愈,属启动时序现象(见 7.5)。
五、部署验证
5.1 REST 接口验证
使用 admin 与新密码调用自助接口(实测回显):
curl -s -u admin:q1w2e3r4 http://localhost:18080/syncope/rest/users/self | grep -o '"username":"[^"]*"'
"username":"admin"
5.2 界面可达性验证
curl -s -o /dev/null -w 'console=%{http_code}\n' http://localhost:28080/syncope-console/
curl -s -o /dev/null -w 'enduser=%{http_code}\n' http://localhost:38080/syncope-enduser/
console=302
enduser=302
302 为跳转登录页,表示服务正常。浏览器访问 http://<服务器IP>:28080/syncope-console/,以 admin/q1w2e3r4 登录管理控制台。
5.3 数据持久化验证(与 standalone 的关键差异)
创建测试用户(实测返回 201):
curl -s -X POST -u admin:q1w2e3r4 -H 'Content-Type: application/json' \
-d '{"_class":"org.apache.syncope.common.lib.request.UserCR","username":"appuser01","password":"Q1w2E3r4!@#5","realm":"/","plainAttrs":[{"schema":"fullname","values":["App User 01"]},{"schema":"surname","values":["User"]},{"schema":"userId","values":["appuser01@syncope.apache.org"]}]}' \
'http://localhost:18080/syncope/rest/users?storePasswordOnSyncope=true' -w '\ncode=%{http_code}\n' -o /dev/null
code=201
重启 Core 容器后再查询(实测用户仍在):
docker compose restart syncope
curl -s -u admin:q1w2e3r4 'http://localhost:18080/syncope/rest/users?fiql=username%3D%3Dappuser01' | grep -o '"username":"appuser01"'
"username":"appuser01"
数据保存在独立 PostgreSQL 服务中,Core 容器重启不丢失,适合承载长期数据。
六、环境变量与镜像内配置详解
6.1 Core(apache/syncope)环境变量
| 环境变量 | 示例值 | 说明 |
|---|---|---|
| SPRING_PROFILES_ACTIVE | docker,postgresql,saml2 | 必须包含 docker 与对应数据库 profile,saml2 可选 |
| DB_URL | jdbc:postgresql://db:5432/syncope?stringtype=unspecified | 数据库连接串;stringtype=unspecified 为 PostgreSQL 必备 |
| DB_USER / DB_PASSWORD | syncope / syncope | 数据库账号,对应 db 服务的初始化值 |
| DB_POOL_MAX / DB_POOL_MIN | 20 / 5 | 连接池上下限 |
| OPENJPA_REMOTE_COMMIT | sjvm | OpenJPA 远程提交提供者,单实例用 sjvm |
| KEYMASTER_ADDRESS | http://localhost:8080/syncope/rest/keymaster | Core 用自身内置 Keymaster(localhost 为容器内自身);也可指向 zookeeper:2181 |
| KEYMASTER_USERNAME / KEYMASTER_PASSWORD | anonymous / anonymousKey | 访问 Keymaster 的凭据,必须非空 |
| SERVICE_DISCOVERY_ADDRESS | http://syncope:8080/syncope/rest/ | 对外登记的服务地址(服务名:8080) |
| ANONYMOUS_USER / ANONYMOUS_KEY | anonymous / anonymousKey | 匿名内部调用账号,生产环境必须修改 |
| SECURITY_ADMINPASSWORD | (SHA512 摘要) | 覆盖默认 admin 密码摘要,强制要求 |
| SECURITY_ADMINPASSWORDALGORITHM | SHA512 | 与上一项配套的算法 |
| SECURITY_AESSECRETKEY | 32 字符随机串 | AES 密钥,16/24/32 字符对应 AES-128/192/256 |
| SECURITY_JWSKEY | 不少于 64 字符随机串 | JWT 签名密钥(HS512 算法要求 ≥64 字符) |
| JAVA_OPTS | "-Xms256m -Xmx512m" | JVM 参数,容器入口脚本原样传给 java |
SECURITY_* 环境变量生效机制:镜像内配置文件使用 Spring Boot 宽松绑定,security.adminPassword 等属性可直接由对应大写环境变量覆盖(实测验证有效)。
Console 与 Enduser 的环境变量结构相同(KEYMASTER_、SERVICE_DISCOVERY_ADDRESS、ANONYMOUS_),无数据库相关项。
6.2 镜像内部结构(排障用)
Core 镜像内目录(实测):
/opt/syncope/bin/startup.sh # 入口脚本:exec java $JAVA_OPTS -jar /opt/syncope/lib/syncope.jar
/opt/syncope/lib/syncope.jar # Spring Boot 可执行包
/opt/syncope/conf/core-docker.properties # 环境变量映射(keymaster.*、security.anonymous* 等)
/opt/syncope/conf/core-postgresql.properties # 数据库参数映射(persistence.domain[0].* ← DB_*)
/opt/syncope/conf/log4j2.xml
/opt/syncope/bundles/ # ConnId 连接器包
/opt/syncope/log/ # 容器内日志目录
进入容器核对 JVM 参数的实测方法:
docker exec syncope-syncope-1 sh -c 'tr "\0" " " < /proc/1/cmdline'
java -Xms256m -Xmx512m -jar /opt/syncope/lib/syncope.jar
七、常见启动失败与解决方法(全部实测复现)
7.1 Keymaster 凭据为空导致崩溃
日志特征:
WARN org.springframework.boot.SpringApplication : Exception encountered during context initialization - cancelling refresh attempt: java.lang.IllegalArgumentException: Cannot pass null or empty values to constructor
ERROR org.springframework.boot.SpringApplication : Application run failed
原因:官方 postgresql 模板的 KEYMASTER_USERNAME/KEYMASTER_PASSWORD 引用环境变量但无默认值,.env 缺失时为空。解决:在 .env 中写入 anonymous/anonymousKey(或自定义强凭据并同步修改 ANONYMOUS_*)。
7.2 默认管理员密码被强制拒绝
日志特征:
ERROR org.apache.catalina.core.StandardWrapperValve : Servlet.service() for servlet [cxfServletRegistration] threw exception
java.lang.IllegalStateException: The default adminPassword property is being used. This must be changed to avoid a security breach!
原因:容器内 security.adminPassword 仍为官方默认值。解决:生成新密码摘要并通过 SECURITY_ADMINPASSWORD/SECURITY_ADMINPASSWORDALGORITHM 环境变量传入。摘要生成方法(在任意装有 JDK 的机器上,将下述内容保存为 Sha512.java 并编译执行):
import java.security.MessageDigest;
import java.util.HexFormat;
public class Sha512 {
public static void main(String[] a) throws Exception {
System.out.println(HexFormat.of().formatHex(
MessageDigest.getInstance("SHA-512").digest(a[0].getBytes())));
}
}
java Sha512.java q1w2e3r4
e553465e3d1ad816a8a8c33176a2ff00fb58f6f55502c89e7593242accc1b0d3c2a2b5fc300a085b5051ecbdd2195b05a53a198ec98d9f75d323cc3c563592f9
7.3 默认 AES 密钥被强制拒绝
日志特征:
java.lang.IllegalStateException: The default AES key property is being used. This must be changed to avoid a security breach!
解决:设置 SECURITY_AESSECRETKEY 环境变量,长度 16/24/32 字符。
7.4 JWS 密钥长度不足导致启动失败
日志特征:
Failed to instantiate [org.apache.syncope.core.spring.security.DefaultCredentialChecker]:
Factory method 'credentialChecker' threw exception with message: The configured key for HS512 must be at least 64 characters
解决:SECURITY_JWSKEY 使用 64 字符以上的随机串(实测 66 字符通过)。
7.5 Console 短暂 500(No registered services for type CORE)
日志特征(Console 容器):
Caused by: org.apache.syncope.common.keymaster.client.api.KeymasterException:
jakarta.ws.rs.NotFoundException: {"status":404,"type":"NotFound","elements":["NotFoundException: No registered services for type CORE"]}
原因:Console 启动早于 Core 完成向 Keymaster 注册(Core 的注册发生在 Started SyncopeCoreApplication 之后)。解决:等待 Core 日志出现 Started 且再过数秒后访问即可,无需处理;编排层面可增大 depends_on 之外的重试容忍(Compose 的 depends_on 不等待应用就绪,属正常现象)。
八、日常运维命令
查看实时日志:
docker compose logs -f syncope
重启单个服务:
docker compose restart syncope
修改 docker-compose.yml 或 .env 后应用变更(自动重建受影响容器):
docker compose up -d
查看各服务状态:
docker compose ps --format 'table {{.Name}}\t{{.Image}}\t{{.Status}}'
升级版本流程:修改 .env 中 SYNCOPE_VERSION 与各 image 标签为新的 4.1.x 补丁版本,执行 docker pull 拉取新镜像后 docker compose up -d 重建;数据库结构由 Core 启动时自动校验,跨大版本升级需按官方迁移文档另行处理。
九、卸载方式
停止并移除全部容器与网络(实测回显):
docker compose down
Container syncope-syncope-enduser-1 Stopped
Container syncope-db-1 Stopping
Container syncope-db-1 Stopped
Container syncope-db-1 Removing
Container syncope-db-1 Removed
Network syncope_default Removing
Network syncope_default Removed
如需连同数据一并清除,在 down 时加 -v 参数移除数据卷;再删除部署目录与本地镜像:
rm -rf /opt/syncope
docker rmi apache/syncope:4.1.3 apache/syncope-console:4.1.3 apache/syncope-enduser:4.1.3 postgres:16-alpine
十、常见问题
Q1:与 standalone 发行版部署怎么选?
standalone 解压即用、内置嵌入式数据库,适合快速验证,但数据重启即重置;Compose 部署使用外部 PostgreSQL,数据持久,适合长期运行与生产起步。生产环境还建议为 db 服务配置卷挂载持久化数据(本指南模板默认卷由 Compose 管理)。
Q2:REST 访问返回 500 且日志提示 default adminPassword property is being used?
容器安全策略强制修改三类默认凭据:adminPassword、AES 密钥、(部分场景)JWS 密钥。按第七节 7.2/7.3/7.4 的方法通过环境变量覆盖后重建容器。
Q3:修改了 SECURITY_ 环境变量后不生效?*
docker compose up -d 检测到环境变量变化会自动重建容器;若使用 docker compose restart 则不会重建,需执行 up -d。确认容器为重建而非重启:docker compose ps 查看 Created/Up 时间。
Q4:端口可以改成 80/443 吗?
可以。ports 映射左侧宿主端口可自由调整(如 "80:8080"),组件间发现地址 SERVICE_DISCOVERY_ADDRESS 使用容器网络内部端口,不受宿主映射影响。
Q5:如何确认 Core 使用的 JVM 堆限制?
执行 docker exec syncope-syncope-1 sh -c 'tr "\0" " " < /proc/1/cmdline',实测回显 java -Xms256m -Xmx512m -jar /opt/syncope/lib/syncope.jar 即生效值。
十一、参考链接
- Apache Syncope Getting Started(Docker 章节):https://syncope.apache.org/docs/getting-started.html
- 官方 compose 模板(随源码包发布):https://downloads.apache.org/syncope/
- Docker Hub 官方镜像页:https://hub.docker.com/r/apache/syncope
- Apache Syncope Reference Guide:https://syncope.apache.org/docs/reference-guide.html