背景
按 deploy/docker-compose-ha/VERIFY.md 在两台跨机房主机(181=primary / 182=standby)上验证 HA 主备部署,过程中发现 6 个代码 bug(其中 4 个直接阻断部署)+ 1 个待深入的 NATS mirror 问题 + 2 处 VERIFY.md 文档不准 。本次验证采用「保留 181 现有数据原地转 HA primary」的方式(复用 compose_* 数据卷 + 现有 common.env,COMPOSE_PROJECT_NAME=compose),181 数据全程保留,182 通过复制获得全量数据。
验证结论:修复下列问题后,VERIFY.md 阶段 1–6(V1–V11 + ha-status + 写入一致性)全部通过 。
环境:Docker 29.5 / Compose v5.1.3,镜像源 bk-lite.tencentcloudcr.com/bklite/weopsx。
阻断级(不修无法完成部署)
1. telegraf 悬空依赖导致备节点(及中间 config)启动失败
现象 :bootstrap.sh 在 docker compose pull 报 service "telegraf" depends on undefined service "server": invalid compose project。
根因 :compose/monitor.yaml 中 telegraf depends_on: server,但 server 是 profiles:[active]、telegraf 不是。生成 docker-compose.yaml 的 config 不带 --profile active 时 server 被排除 → telegraf 悬空。备节点永不激活 profile,此问题会让 standby 彻底起不来 。
修复 :compose/ha.yaml 给 telegraf 也加 profiles:[active](与功能同类的 fusion-collector 一致)。
2. falkordb entrypoint 路径写死且错误
现象 :compose-falkordb-1 反复重启,日志 exec: /entrypoint.sh: not found,容器 unhealthy,bootstrap 失败。
根因 :compose/ha.yaml 的 falkordb override 末尾 exec /entrypoint.sh,但该镜像真实入口是 /var/lib/falkordb/bin/run.sh(消费 REDIS_ARGS/FALKORDB_ARGS)。
修复 :改为 exec /var/lib/falkordb/bin/run.sh。
3. 基础服务启动列表显式点名业务服务,导致备节点误启动
现象 :standby 上 webhookd、nats-executor 被启动(VERIFY V4 要求它们缺失)。
根因 :bootstrap.sh start_services() 的 docker compose up -d traefik redis ... nats-executor vector webhookd 显式点名了 nats-executor/webhookd。docker compose up 显式点名会无视 profile 强行启动 。
修复 :从该列表移除 nats-executor、webhookd(它们只应由主节点 --profile active up 拉起)。
4. 自签证书缺 clientAuth,NATS leafnode 双向 TLS 永久握手失败
现象 :leafnode 一直连不上;181 nats 日志 TLS leafnode handshake error: x509: certificate specifies an incompatible key usage → Leafnode connection closed: TLS Handshake Failure。连锁导致 V8 失败、mirror 无数据、NATS 透传失败。
根因 :leafnodes.tls.verify: true(双向),备节点作为 leaf 用同一证书做客户端认证;但 bin/ha-gen-certs.sh 的 extendedKeyUsage = serverAuth 缺 clientAuth。
修复 :extendedKeyUsage = serverAuth, clientAuth,重签证书下发两端后 leafnode 稳定。
5. 主备 JetStream domain 相同 + mirror 未用 external-api,mirror 建成空的普通流
现象 :ha-init-mirror-streams.sh 建出来的是带本名 subject 的普通空流 而非 mirror;stream info 无 Mirror: 段,消息恒为 0。
根因 :① bootstrap.sh generate_nats_config 给主备都写死 domain=bklite;跨 leafnode 做 mirror 时 leaf 必须用不同 domain 并通过 mirror.external.api = "$JS.<主domain>.API" 引用源流,否则 mirror 自引用拉不到数据。② ha-init-mirror-streams.sh 用 nats stream add --mirror $s --defaults,nats CLI 0.3.0 无 --mirror-domain flag、--defaults 跳过外部域设置。
修复 :① domain 按角色区分(primary bklite / standby bklite_standby);② mirror 脚本改用 --config JSON 指定 mirror.external.api。修复后 metrics/METRICS_ALL 同步正常(见下方 feat(middleware): 添加umami跟踪功能并移除冗余的install.run文件 #7 关于 OBJ_bklite 的遗留)。
遗留设计点 :domain 当前按「角色」命名,failover 角色互换后 mirror 的 $JS.bklite.API 引用会与新主 domain 不一致,failover/failback 的 domain 对称性需进一步设计。
6. DOCKER_IMAGE_NATS_CLI 在脚本独立运行时为空
现象 :独立执行 ha-status.sh 时 NATS 段误报所有流「不存在」(虽 [ALL OK],但 NATS 检查实际失败);ha-init-mirror-streams.sh 独立运行同样受影响。
根因 :镜像变量由 bootstrap.sh 运行时按 REGISTRY_BASE 拼出,脚本只 source common.env 时该变量为空,docker run "" ... 失败。
修复 :两脚本加兜底 DOCKER_IMAGE_NATS_CLI="${DOCKER_IMAGE_NATS_CLI:-${REGISTRY_BASE}/natsio/nats-box:latest}"。
待深入(非阻断)
7. OBJ_bklite mirror 回填在大流 + 内部删除空洞上确定性停滞
现象 :修复 fix: 修复 FloatingOrbs 组件渲染时动画闪烁问题 #4 /Refactor/bootstrap cleanup #5 后,metrics(3/3)、METRICS_ALL(0/0) mirror 正常;但 OBJ_bklite mirror 确定性卡在 2,487/10,482 (删除重建复现),182 nats 日志反复 JetStream error response for create mirror consumer: stream not found (10059)。
源流特征 :OBJ_bklite 10,482 条 / 1.3 GiB / 已删除 11,298 条 / seq 1→21,780(大量内部删除空洞)。拉完首批 2,487 条(连续段)后 consumer 在源端重建失败。
影响评估 :对象数据本身 经 MinIO 站点复制已安全同步(V11 通过),OBJ_bklite 仅为对象事件流,影响有限;但 failover 后该事件流不完整。
怀疑 :跨 leafnode 的 external-API JetStream mirror 在需要多轮 pull / 跨删除空洞时,consumer 续建的 JS API 子主题未被正确路由。需要 NATS 拓扑层面进一步排查(leafnode + 跨域 mirror 是已知棘手场景)。
VERIFY.md 文档修正
V3 :curl -k https://127.0.0.1:443/ 因 web 路由规则是 Host(\<HOST_IP>`),用 127.0.0.1作 Host 会返回 **404**。应改为curl -k https://<HOST_IP>/或带-H "Host: <HOST_IP>"`。
V8 :nats ... server report connections 用 admin 账号会报 ensure the account used has system privileges。admin 无 SYS 权限,需用 system 账号,或改用「检查两端 nats 日志 leafnode 连接 + 无 TLS handshake error」来验证。
本 issue 中 6 个代码 bug 的修复已在本地验证可用(实测两台机器 V1–V11 + ha-status + 一致性全部通过)。可按需拆分为独立 PR。
背景
按
deploy/docker-compose-ha/VERIFY.md在两台跨机房主机(181=primary / 182=standby)上验证 HA 主备部署,过程中发现 6 个代码 bug(其中 4 个直接阻断部署)+ 1 个待深入的 NATS mirror 问题 + 2 处 VERIFY.md 文档不准。本次验证采用「保留 181 现有数据原地转 HA primary」的方式(复用compose_*数据卷 + 现有common.env,COMPOSE_PROJECT_NAME=compose),181 数据全程保留,182 通过复制获得全量数据。验证结论:修复下列问题后,VERIFY.md 阶段 1–6(V1–V11 + ha-status + 写入一致性)全部通过。
环境:Docker 29.5 / Compose v5.1.3,镜像源
bk-lite.tencentcloudcr.com/bklite/weopsx。阻断级(不修无法完成部署)
1.
telegraf悬空依赖导致备节点(及中间 config)启动失败bootstrap.sh在docker compose pull报service "telegraf" depends on undefined service "server": invalid compose project。compose/monitor.yaml中telegrafdepends_on: server,但server是profiles:[active]、telegraf不是。生成docker-compose.yaml的config不带--profile active时server被排除 → telegraf 悬空。备节点永不激活 profile,此问题会让 standby 彻底起不来。compose/ha.yaml给telegraf也加profiles:[active](与功能同类的fusion-collector一致)。2.
falkordbentrypoint 路径写死且错误compose-falkordb-1反复重启,日志exec: /entrypoint.sh: not found,容器 unhealthy,bootstrap 失败。compose/ha.yaml的 falkordb override 末尾exec /entrypoint.sh,但该镜像真实入口是/var/lib/falkordb/bin/run.sh(消费REDIS_ARGS/FALKORDB_ARGS)。exec /var/lib/falkordb/bin/run.sh。3. 基础服务启动列表显式点名业务服务,导致备节点误启动
webhookd、nats-executor被启动(VERIFY V4 要求它们缺失)。bootstrap.shstart_services()的docker compose up -d traefik redis ... nats-executor vector webhookd显式点名了nats-executor/webhookd。docker compose up显式点名会无视 profile 强行启动。nats-executor、webhookd(它们只应由主节点--profile active up拉起)。4. 自签证书缺
clientAuth,NATS leafnode 双向 TLS 永久握手失败TLS leafnode handshake error: x509: certificate specifies an incompatible key usage→Leafnode connection closed: TLS Handshake Failure。连锁导致 V8 失败、mirror 无数据、NATS 透传失败。leafnodes.tls.verify: true(双向),备节点作为 leaf 用同一证书做客户端认证;但bin/ha-gen-certs.sh的extendedKeyUsage = serverAuth缺clientAuth。extendedKeyUsage = serverAuth, clientAuth,重签证书下发两端后 leafnode 稳定。5. 主备 JetStream domain 相同 + mirror 未用 external-api,mirror 建成空的普通流
ha-init-mirror-streams.sh建出来的是带本名 subject 的普通空流而非 mirror;stream info无Mirror:段,消息恒为 0。bootstrap.sh generate_nats_config给主备都写死domain=bklite;跨 leafnode 做 mirror 时 leaf 必须用不同 domain 并通过mirror.external.api = "$JS.<主domain>.API"引用源流,否则 mirror 自引用拉不到数据。②ha-init-mirror-streams.sh用nats stream add --mirror $s --defaults,nats CLI 0.3.0 无--mirror-domainflag、--defaults跳过外部域设置。bklite/ standbybklite_standby);② mirror 脚本改用--configJSON 指定mirror.external.api。修复后metrics/METRICS_ALL同步正常(见下方 feat(middleware): 添加umami跟踪功能并移除冗余的install.run文件 #7 关于 OBJ_bklite 的遗留)。$JS.bklite.API引用会与新主 domain 不一致,failover/failback 的 domain 对称性需进一步设计。6.
DOCKER_IMAGE_NATS_CLI在脚本独立运行时为空ha-status.sh时 NATS 段误报所有流「不存在」(虽[ALL OK],但 NATS 检查实际失败);ha-init-mirror-streams.sh独立运行同样受影响。bootstrap.sh运行时按REGISTRY_BASE拼出,脚本只source common.env时该变量为空,docker run "" ...失败。DOCKER_IMAGE_NATS_CLI="${DOCKER_IMAGE_NATS_CLI:-${REGISTRY_BASE}/natsio/nats-box:latest}"。待深入(非阻断)
7. OBJ_bklite mirror 回填在大流 + 内部删除空洞上确定性停滞
metrics(3/3)、METRICS_ALL(0/0) mirror 正常;但OBJ_bklitemirror 确定性卡在 2,487/10,482(删除重建复现),182 nats 日志反复JetStream error response for create mirror consumer: stream not found (10059)。OBJ_bklite10,482 条 / 1.3 GiB / 已删除 11,298 条 / seq 1→21,780(大量内部删除空洞)。拉完首批 2,487 条(连续段)后 consumer 在源端重建失败。VERIFY.md 文档修正
curl -k https://127.0.0.1:443/因 web 路由规则是Host(\<HOST_IP>`),用127.0.0.1作 Host 会返回 **404**。应改为curl -k https://<HOST_IP>/或带-H "Host: <HOST_IP>"`。nats ... server report connections用admin账号会报ensure the account used has system privileges。admin无 SYS 权限,需用 system 账号,或改用「检查两端 nats 日志 leafnode 连接 + 无 TLS handshake error」来验证。