设计双活 Harbor 并解决主键冲突问题
解读
国内金融、运营商及大型央企在信创与等保 2.0 背景下,对镜像仓库的RPO=0、RTO<30s有刚性要求,单实例 Harbor 已无法满足。双活(Active-Active)Harbor 指两个独立 Harbor 实例同时对外提供推送与拉取服务,任一机房整体掉线业务不中断。核心难点是:两实例共用一套底层存储时,PostgreSQL 主键自增序列重复导致 artifact、project、repository 表冲突,进而出现镜像索引覆盖或 500 错误。面试官希望候选人给出可落地的架构、数据层冲突避免策略、Docker 层配置及回滚方案,并能在 10 分钟内把思路讲清。
知识点
- Harbor 数据模型:artifact、project、repository、tag、blob、schedule 六张核心表均依赖序列自增 ID。
- PostgreSQL 序列冲突本质:两实例本地序列步长为 1,起始值相同,并发写入必冲突。
- 双活存储前提:国内主流采用共享型分布式存储(华为 OceanStor、XSKY、阿里云 NAS)或对象存储跨区域桶复制(MinIO 双活、腾讯云 COS 回源),保证 blob 层一致。
- 序列步长拆分法:PostgreSQL 序列通过
INCREMENT BY 2与START WITH 1/2把奇偶 ID 段分给两机房,零代码侵入,回滚只需改序列。 - 应用层 UUID 兜底:Harbor v2.5+ 已支持
UUID_GENERATE_V4()作为 artifact 唯一键,开启后可降级到 UUID 主键,序列仅做兼容。 - Docker Compose 层关键:
- 两实例
external_url必须分别指向本机房 SLB 域名,禁止交叉写流量; registry.relativeurls=true强制回源到本地域,避免跨机房拉取;jobservice.max_job_workers减半,防止并发任务重复。
- 两实例
- 脑裂与回切:借助Keepalived+VIP 或 DNS 权重实现流量切换;回切前需用
pg_rewind或rsync --checksum校验 blob 一致性,防止静默损坏。
答案
一、总体架构
- 两地三机房(A、B 同城双活,C 异地冷备),A、B 同时 Active。
- 存储层:
- 块存储:采用华为 OceanStor Dorado 双活 NAS,通过双活引擎保证 A-A 时延 <2 ms,blob 目录全局强一致。
- 对象存储:若已 MinIO,开启site replication(v2.1+),
mc admin replicate add双向同步,版本号由 MinIO 内部时钟向量保证。
- 数据库层:
- 两机房各部署独立 PostgreSQL 14 实例,不采用流复制,通过序列步长拆分实现逻辑双写。
- 序列脚本(以 harbor 库为例):
-- A 机房 ALTER SEQUENCE artifact_artifact_id_seq INCREMENT BY 2 START WITH 1; -- B 机房 ALTER SEQUENCE artifact_artifact_id_seq INCREMENT BY 2 START WITH 2; - 其余序列同理,一次性脚本由 Ansible 批量下发。
- 应用层:
- Harbor 版本锁定 v2.8.2 国产化麒麟 v10 镜像(
registry.cn-hangzhou.aliyuncs.com/goharbor/harbor-core:v2.8.2-ky10)。 harbor.yml关键配置:external_url: https://harbor-a.example.com database: host: 192.168.1.10 port: 5432 db: harbor sslmode: disable data_volume: /mnt/nas/harbor- 关闭
clair(已废弃),trivy.cache_dir指向本地 NVMe,减少跨机房锁等待。
- Harbor 版本锁定 v2.8.2 国产化麒麟 v10 镜像(
- 流量入口:
- 采用阿里云全球流量管理(GTM) 基于 EDNS-Client-Subnet 把用户解析到就近机房;
- 每个机房内部用 Keepalived+HAProxy 做 4 层负载,VIP 与 GTM 地址联动,实现秒级切换。
二、主键冲突解决验证
- 压测工具:使用官方 harbor-load 镜像(
goharbor/harbor-load:v1.1)并发 200 线程同时 push 相同busybox:latest到 A、B 两实例。 - 验证 SQL:
结果应为 0,证明无重复主键。SELECT COUNT(*) FROM artifact WHERE digest='sha256:xxx' GROUP BY digest HAVING COUNT(*)>1; - 回滚预案:
- 若序列拆分失败,30 秒内切换为单写模式:
- 关闭 B 机房入口,GTM 权重置 0;
- B 实例
docker-compose stop; - A 机房序列改回
INCREMENT BY 1,业务无感知。
- 若序列拆分失败,30 秒内切换为单写模式:
三、Docker Compose 片段(A 机房)
version: '2.4'
services:
portal:
image: registry.cn-hangzhou.aliyuncs.com/goharbor/harbor-portal:v2.8.2-ky10
networks:
- harbor
core:
image: registry.cn-hangzhou.aliyuncs.com/goharbor/harbor-core:v2.8.2-ky10
environment:
- _REDIS_URL=redis://redis:6379/0
- _POSTGRES_HOST=192.168.1.10
volumes:
- /mnt/nas/harbor:/data
networks:
- harbor
registry:
image: registry.cn-hangzhou.aliyuncs.com/goharbor/registry-photon:v2.8.2-ky10
environment:
- REGISTRY_STORAGE_REDIRECT_DISABLE=true
- REGISTRY_STORAGE_FILESYSTEM_ROOTDIRECTORY=/storage
volumes:
- /mnt/nas/harbor/registry:/storage
networks:
- harbor
networks:
harbor:
external: false
关键点:REGISTRY_STORAGE_REDIRECT_DISABLE=true 强制流量走 core,避免客户端直连存储出现跨机房 302 漂移。
拓展思考
- 三机房多活:若未来扩展为 3 活,序列步长改为 3,起始值 1/2/3;但 PostgreSQL 序列上限 2^63-1,提前评估 10 年增量。
- Operator 化:采用KubeSphere 开源的 harbor-operator,通过 CRD
HarborCluster把序列拆分逻辑写进 init-container,实现 GitOps 自动 rollout。 - 冲突监控:在 Prometheus 增加
pg_stat_user_tables_n_tup_ins指标,若两机房同一表插入速率长期接近,说明序列步长拆分不均,可动态setval调整。 - 国产化替代:华为 openGauss 2.1 已支持全局唯一索引(GUC
enable_global_unique_index=on),可彻底废弃序列,但需验证与 Harbor ORM 的兼容性。