Docker 部署 OceanBase 异常关机后恢复实战
Docker 部署 OceanBase 异常关机后恢复实战
一句话总结:OceanBase 内部系统表(
__all_ls_replica)持久化了容器 IP,Docker 网络重建后分配新 IP 导致 RPC 通信失败。解法是固定 Docker 网段与静态 IP,并绕过 obd 入口脚本直接启动 observer。
背景
生产环境使用 docker-compose 部署 OceanBase CE 4.2.1,服务器异常关机后容器启动失败。本文记录完整排查与修复过程。
环境信息
- OceanBase CE 4.2.1.10
- docker-compose 部署,数据通过 bind mount 持久化
- 单节点集群
obcluster
原始配置(有缺陷)
version: '3.0'
services:
oceanbase:
image: oceanbase/oceanbase-ce:latest
container_name: oceanbase
environment:
- OB_SYS_PASSWORD=123456
- OB_CLUSTER_NAME=obcluster
ports:
- "2881:2881"
- "2882:2882"
volumes:
- ./ob:/root/ob
networks:
- oceanbase_network
networks:
oceanbase_network:
driver: bridge
排查过程
第一步:docker-compose up -d 失败
[ERROR] OBD-1002: Fail to init 172.20.0.2 home path: /root/ob is not empty.
[ERROR] OBD-1027: run `obd cluster deploy -f obcluster` to perform forced deployment.
容器入口脚本检测到 /root/ob 已有旧数据,拒绝重新部署。
第二步:docker start oceanbase 同样失败
[ERROR] Deploy "obcluster" is configured. You could not start an configured cluster.
obd 内部状态卡在 configured,拒绝执行 start 命令。
第三步:进入容器手动排查
docker run -it --rm \
--network tszb-oceanbase_oceanbase_network \
-v "$(pwd)/ob:/root/ob" \
-p 2881:2881 -p 2882:2882 \
--entrypoint /bin/bash \
oceanbase/oceanbase-ce:latest
obd cluster list
# demo | stopped
# obcluster | configured ← 状态卡死
第四步:发现根因 —— IP 不匹配
# 容器当前 IP
hostname -I
# 172.20.0.2
# observer 内部配置记录的 IP
cat /root/ob/etc/observer.config.bin | strings | grep "172\."
# local_ip=172.28.0.2
# all_server_list=172.28.0.2:2882
# rootservice_list=172.28.0.2:2882:2881
容器 IP 是 172.20.0.2,但 observer.config.bin 内部记录的 IP 是 172.28.0.2。
这是因为 docker-compose 重建 network 时分配了新的子网段,而 OceanBase 内部表(__all_ls_replica 等)持久化存储了旧的 member_list IP。observer 启动后一直尝试向 172.28.0.2:2882 发 RPC,永远无法完成 Leader 选举。
第五步:错误尝试 —— 删除 config.bin
删除 observer.config.bin 后直接启动 observer,observer 认为这是全新集群尝试 bootstrap,与已有 datafile 冲突,直接崩溃:
ERROR ... errcode=-4018 ... info="observer start fail"
教训:不要删除已有集群的 observer.config.bin。
第六步:错误尝试 —— 用 -o local_ip 覆盖 IP
observer ... -o "local_ip=172.20.0.2"
虽然进程 IP 变成了 172.20.0.2,但 OceanBase 内部系统表 __all_ls_replica 的 member_list 字段里存的仍然是 172.28.0.2,RPC 通信依然失败,永远卡在 Server is initializing。
最终方案
思路
既然内部表中已经持久化了 172.28.0.2,最安全的做法是让 Docker 网络分配同样的 IP 段,而不是修改 OceanBase 内部数据。
修复后的 docker-compose.yml
version: '3.0'
services:
oceanbase:
image: oceanbase/oceanbase-ce:latest
container_name: oceanbase
environment:
- OB_SYS_PASSWORD=123456
- OB_CLUSTER_NAME=obcluster
ports:
- "2881:2881"
- "2882:2882"
volumes:
- ./ob:/root/ob
networks:
oceanbase_network:
ipv4_address: 172.28.0.2
networks:
oceanbase_network:
driver: bridge
ipam:
config:
- subnet: 172.28.0.0/16
操作步骤
# 1. 停止并清理
docker stop oceanbase 2>/dev/null
docker rm oceanbase 2>/dev/null
docker network rm tszb-oceanbase_oceanbase_network 2>/dev/null
# 2. 重建(注意:数据不丢,在 ./ob 目录里)
docker-compose up -d
# 3. 进入容器手动启动(obd 状态可能仍卡在 configured)
docker exec -it oceanbase bash
# 4. 清理运行时锁文件
rm -f /root/ob/run/*.pid /root/ob/run/*.lock
find /root/.obd -name "*.lock" -delete 2>/dev/null
# 5. 直接启动 observer
export LD_LIBRARY_PATH=/root/ob/lib:$LD_LIBRARY_PATH
cd /root/ob && /root/ob/bin/observer \
-p 2881 -P 2882 \
-z zone1 -n obcluster -c 1 \
-d /root/ob/store -i eth0 -l INFO \
-o "memory_limit=6G,system_memory=1G,datafile_size=5G,log_disk_size=5G,root_password='yunjin@9527'"
# 6. 等 3-5 分钟后验证
obclient -h127.0.0.1 -P2881 -uroot@sys -p'yunjin@9527'
验证成功的标志
# 进程存活
ps aux | grep observer | grep -v grep
# IP 一致
grep "self.*172.28.0.2" /root/ob/log/observer.log | tail -1
# 连接成功
obclient -h127.0.0.1 -P2881 -uroot@sys -p'yunjin@9527'
# 进入后执行:SELECT * FROM oceanbase.__all_tenant;
问题根因总结
| 问题 | 原因 | 修复 |
|---|---|---|
| OBD-1002: /root/ob is not empty | 入口脚本不允许重复部署已有数据目录 | 跳过 obd,直接启动 observer |
| Deploy is configured, cannot start | obd 内部状态持久化在 SQLite,异常关机后状态未更新 | 跳过 obd,直接启动 observer |
| observer 启动后 crashed (-4018) | 删除 config.bin 导致 observer 尝试重新 bootstrap 与已有数据冲突 | 保留 config.bin |
| 永远卡在 Server is initializing | 内部表 member_list 记录的旧 IP 与容器新 IP 不匹配,RPC 通信失败 | 固定 Docker 网段与静态 IP |
最终结果
经过上述排查与修复后,observer 进程成功启动,数据库连接正常,所有租户数据和业务表完好无损。
关键经验
- 不要把
/root/ob/store看作"能删"的数据——那是生产数据库的全部。 - observer.config.bin 不能删——它记录着集群元数据,删掉后 observer 会尝试重新 bootstrap。
- OceanBase 内部表会持久化 IP 地址——仅靠
-o local_ip无法覆盖所有记录。 - Docker 静态 IP + 固定子网是 OceanBase 容器化的最佳实践——避免容器重建后 IP 漂移。
- 30G+ 数据量的 crash recovery 预计 5-10 分钟,期间
Server is initializing是正常状态。
对 docker-compose.yml 的改进建议
version: '3.0'
services:
oceanbase:
image: oceanbase/oceanbase-ce:latest
container_name: oceanbase
environment:
- OB_SYS_PASSWORD=123456
- OB_CLUSTER_NAME=obcluster
ports:
- "2881:2881"
- "2882:2882"
volumes:
- ./ob:/root/ob
networks:
oceanbase_network:
ipv4_address: 172.28.0.2
restart: unless-stopped # 新增:宿主机重启后自动恢复
networks:
oceanbase_network:
driver: bridge
ipam:
config:
- subnet: 172.28.0.0/16
加了 restart: unless-stopped 后,容器崩溃或宿主机重启时 Docker 会自动尝试重启容器。不过注意:首次部署或 Docker 网络重建后,仍可能需要手动进入容器启动 observer,因为 obd 入口脚本对"已有数据目录"的检查逻辑比较严格。
更多推荐
所有评论(0)