人大金仓(KingbaseES)的容器部署和 MySQL、Redis 那类镜像不太一样。镜像由厂商提供,里面带着授权文件,启动校验严格,出问题时日志又经常误导人。这篇把部署、license 挂载和"库起不来"的排查顺序讲清楚,路径以厂商镜像的实际布局为准。
一、镜像
金仓的镜像不挂在公共仓库,来源一般是两个:
- 厂商给的镜像 tar 包,
docker load -i kingbase-v8r6.tar导入 - 内部私有仓库,
docker pull <仓库地址>/kingbase:v8r6
导入后确认:
docker images | grep -i kingbase
镜像名和 tag 按实际导入的写,后面的 compose 里统一用 kingbase:v8r6 举例。
二、目录规划
sudo mkdir -p /data/kingbase/{data,license}
| 宿主机路径 | 容器内路径 | 说明 |
|---|---|---|
/data/kingbase/data |
数据目录 | 必须挂出来,容器重建不丢库 |
/data/kingbase/license/license.dat |
.../bin/license.dat |
授权文件,建议单独挂载 |
容器内的安装目录不是统一的,先看一眼实际布局再写挂载路径:
docker run --rm --entrypoint ls kingbase:v8r6 /home/kingbase/install/kingbase
常见的几处:
- 安装目录:
/home/kingbase/install/kingbase - 授权文件:
/home/kingbase/install/kingbase/bin/license.dat - 服务端程序:同目录下的
bin/kingbase、bin/sys_ctl、bin/ksql - 数据目录:由镜像或 entrypoint 决定,通常在
kingbase用户主目录下
数据目录的属主要和容器里的 kingbase 用户对上:
docker run --rm --entrypoint id kingbase:v8r6 kingbase
sudo chown -R <输出的uid>:<输出的gid> /data/kingbase/data
三、启动
services:
kingbase:
image: kingbase:v8r6
container_name: kingbase8
restart: unless-stopped
privileged: true
environment:
TZ: Asia/Shanghai
DB_MODE: mysql
ports:
- "54321:54321"
volumes:
- /data/kingbase/data:/home/kingbase/data
- /data/kingbase/license/license.dat:/home/kingbase/install/kingbase/bin/license.dat
shm_size: 1gb
几点说明:
DB_MODE决定兼容模式(Oracle / MySQL / PG),建库前定好,后面改不了。用 MySQL 兼容模式就写mysql。privileged和command: /usr/sbin/init:有的厂商镜像把完整系统打包进来,用 systemd 启动,这种镜像必须开特权模式。如果镜像是单进程启动的,这两项可以去掉,以docker inspect看到的 entrypoint 为准。shm_size默认只有 64 MB,跑并发查询时容易报共享内存不足,给到 1 GB 比较稳。- 端口映射写成
127.0.0.1:54321:54321更安全,只给本机或反向代理访问。
四、license 的三个坑
金仓只在启动时校验授权,运行中的实例不会因为到期被踢出去,但下一次重启就起不来。这是它和普通数据库最不一样的地方。
坑一:license 在容器可写层里
不挂卷的时候,license 的实体在容器内的安装目录下。docker restart 能保住,docker rm 之后重建,就退回镜像里那份旧证。镜像里那份往往是打包时的临时授权,可能早就过期了。
所以 license 要单独挂出来:
volumes:
- /data/kingbase/license/license.dat:/home/kingbase/install/kingbase/bin/license.dat
坑二:挂载不能加 :ro
这个镜像的 entrypoint 启动时会对安装目录做 chown。挂载点设成只读,chown 直接失败:
chown: changing ownership of '/home/kingbase/install/kingbase/bin/license.dat': Read-only file system
随后服务起不来,报 sys_ctl: could not start server。所以 license 的挂载必须是可写的。
坑三:宿主机文件要先存在
如果宿主机上 license.dat 不存在,Docker 会把挂载点创建成一个目录,容器里的金仓读到目录,报错形态和"授权无效"完全不一样,很容易查错方向。
顺序必须是:
# 1. 先放文件
sudo cp license_xxx.dat /data/kingbase/license/license.dat
# 2. 改属主,和容器内 kingbase 用户一致
sudo chown <uid>:<gid> /data/kingbase/license/license.dat
chmod 640 /data/kingbase/license/license.dat
# 3. 再改 compose 挂载
改完挂载启动,容器内 entrypoint 那次 chown 会真的改掉宿主机文件的属主,这是 bind mount 共享 inode 的正常结果,不是事故。
五、初始化
多数厂商镜像的 entrypoint 会在数据目录为空时自动 initdb,已经有数据就跳过。手工初始化也可以,用 kingbase 用户执行:
docker exec -u kingbase kingbase8 \
/home/kingbase/install/kingbase/bin/initdb \
-D /home/kingbase/data -U SYSTEM --pwfile=/tmp/pw
-U 指定超级用户,后面所有连接都用这个用户名。--pwfile 里放初始密码,不要用 -W 交互输入,容器里没有终端。
注意 initdb 的字符集和兼容模式要和 DB_MODE 一致,建库之后再改,等于重建。
六、验证库到底起没起
这一步最容易被误导。sys_ctl 在 license 校验之前就把 pid 文件写出来了,所以日志里出现 server started,不代表库活着。判定存活只看这三条:
# 1. 进程状态,退出码为 0 才算
docker exec -u kingbase kingbase8 \
/home/kingbase/install/kingbase/bin/sys_ctl status -D /home/kingbase/data
# 2. 端口在监听。这个镜像里可能没有 ss/netstat,直接看 /proc
docker exec kingbase8 grep -i ':D431' /proc/net/tcp /proc/net/tcp6
# 3. 日志里出现 ready 字样
docker exec kingbase8 tail -50 /home/kingbase/data/sys_log/*.log
54321 的十六进制是 0xD431,这也是为什么要在 /proc/net/tcp 里搜这个字符串。容器里没有 ss 的时候,docker exec kingbase8 ss -lntp 会静默无输出,不能当成"端口没监听"。
三条都过了,再连库:
docker exec -u kingbase kingbase8 \
ksql -U SYSTEM -p 54321 -d test -c "select version();"
镜像的 entrypoint 在 initdb 时会把本地连接认证改成 trust,容器内连库不需要密码。外网连接受 kingbase.conf 里的 listen_addresses 和 sys_hba.conf 控制,默认只允许本地。
七、换 license 的标准动作
授权到期后,启动日志是这样的:
FATAL: XX000: License file expired.
LOCATION: KesMasterMain, master.c:961
换证按顺序做,少一步就会遇到"换了还是起不来":
# 1. 备份旧证和当前配置
sudo cp -a /data/kingbase/license/license.dat \
/data/kingbase/license/license.dat.bak-$(date +%F)
# 2. 覆盖新证,注意保住 inode 和属主
sudo cat new_license.dat > /data/kingbase/license/license.dat
sudo chown <uid>:<gid> /data/kingbase/license/license.dat
# 3. 清掉上次启动失败留下的 socket 和 pid 残留
docker exec kingbase8 sh -c '
ts=$(date +%s)
mkdir -p /tmp/kingbase-stale-$ts
mv /tmp/.s.KINGBASE.54321* /tmp/kingbase-stale-$ts/ 2>/dev/null || true'
# 4. 用 kingbase 用户启动,root 会被拒绝
docker exec -u kingbase kingbase8 \
/home/kingbase/install/kingbase/bin/sys_ctl start -D /home/kingbase/data
用 cat > 就地覆盖而不是删除重写,是为了保住文件 inode,已经挂载的路径不用重建容器。/tmp/.s.KINGBASE.54321 和对应的 .lock 文件是上次启动失败留下的,不清掉会报:
another server might be running; trying to start server anyway
lock file "/tmp/.s.KINGBASE.54321.lock" already exists
残留文件用 mv 挪走而不是 rm,出问题时还能看一眼时间戳,判断上一次启动尝试发生在什么时候。
还有一条:镜像里通常挂着一个 cron 看门狗,每分钟尝试拉起数据库。手工启动时如果撞上它,会看到 another server is running,等一分钟再试,或者先停掉看门狗。
八、备份
逻辑备份用 sys_dump,参数和 PostgreSQL 的 pg_dump 基本一致:
docker exec -u kingbase kingbase8 \
sys_dump -U SYSTEM -p 54321 -d app -F c -f /tmp/app.dump
docker cp kingbase8:/tmp/app.dump /backup/app-$(date +%F).dump
恢复:
docker cp /backup/app-2026-10-29.dump kingbase8:/tmp/app.dump
docker exec -u kingbase kingbase8 \
sys_restore -U SYSTEM -p 54321 -d app /tmp/app.dump
数据库本身可以直接停库后复制数据目录,但和 MySQL 一样,热复制没有意义。
九、常见报错
| 现象 | 原因 | 处理 |
|---|---|---|
FATAL: XX000: License file expired |
授权到期 | 按第七节换证 |
sys_ctl: could not start server |
原因很多,先看 sys_log 最后 50 行 |
优先查 license 和 socket 残留 |
another server might be running / lock file ... already exists |
上次启动失败留下的 socket 和 pid | 清 /tmp/.s.KINGBASE.54321* |
WARNING: max_connections should be less than or equal than N (restricted by license) |
授权带连接数上限,配置被压到上限值 | 核对应用连接池,别超上限 |
chown: ... Read-only file system |
license 挂载加了 :ro |
去掉只读 |
Is a directory 指向 license 路径 |
宿主机文件不存在,Docker 建了目录 | 先放文件再改 compose |
Permission denied 写数据目录 |
目录属主和容器内 kingbase 用户不一致 | chown 成对应 uid |
could not create shared memory segment |
容器 /dev/shm 太小 |
加大 shm_size |
| 进程在,端口连不上 | 监听地址、防火墙、端口映射 | 容器内看 /proc/net/tcp,宿主机看 ss -lntp |
docker logs 里 server started 但库连不上 |
sys_ctl 在 license 校验前就报成功 |
用第六节的三条标准重新判断 |
十、限制
- 授权是硬中断:到期不影响运行中的实例,但任何一次重启、宿主机重启、容器重建都会当场起不来。到期日要单独记,别等出事再查。
- 授权可能带并发连接数上限,配置里写再大也会被压下来,这个值只能问发证方。
- 容器重建会退回镜像内的旧授权,所以 license 必须挂卷,且换证后要检查挂载是否还在。
- 单实例部署没有高可用,做主备是另一套配置(
sys_hba.conf、复制槽、recovery.conf这一套),不在容器化本身的范围里。