有些年头没好好写基础设施相关的东西了。最近又替两三个团队折腾了一遍私有镜像仓库,聊下来发现不少人还是对“企业级”这三个字的理解停留在“能登录、能push、能pull”的阶段。真正落到生产环境,Harbor的部署从来不是改改hostname、配个密码就完事,里面涉及的高可用设计、存储选型、证书规划、权限模型、备份容灾,每一项都够写一整篇。这次把我自己上手踩过坑的经验完整整理出来,从架构思路到实操命令,再到问题排查,尽量一次讲透。
1. 先搞清楚企业级部署到底在解决什么问题
1.1 为什么不能直接用Docker Hub或者一个单机Registry
很多团队一开始图省事,直接用Docker Hub官方镜像往上一拉,内网里起一个单节点Registry就当私有仓库用了。小团队、几十个镜像、十来个人用,这么干确实没问题,但一旦到了几十人以上的研发团队、多个环境并行发布、CI/CD流水线频繁构建推送,问题就会集中爆发:镜像是谁推的、什么时间推的、哪个项目下的,完全没有记录;权限管理基本靠共享账号,有人误删一个镜像,整个团队都要跟着遭殃;节点一挂,所有依赖镜像拉取的环境全部停摆;更不用说镜像扫描、漏洞报告、清理策略这些需求,原生Registry压根不具备。
Harbor出现之后,基本成了私有镜像仓库的事实标准,原因很简单:它把镜像存储、权限控制、安全扫描、复制同步、回收策略、审计日志这些企业真正关心的能力都集成到了一套系统里,而且部署方式相对友好,社区活跃度也高,遇到问题有大量现成资料可查。
1.2 企业级部署和普通部署的本质差异
我自己理解,“企业级”这三个字,翻译成技术指标就是四点:高可用、可扩展、可审计、可容灾。高可用意味着不能有单点故障,任何一个组件挂了都不能影响核心链路;可扩展意味着存储和节点都能平滑扩容,不能每次扩容都要推倒重来;可审计意味着每个操作都有迹可循,谁在什么时间做了什么,出了问题能追溯;可容灾意味着数据得有多副本或者备份恢复机制,机器坏了、机房出问题了,业务不能因此丢失镜像。
带着这四点去看Harbor的部署方案,思路就会非常清晰:单机Docker Compose只是评估验证阶段的选择,生产环境必须走多节点高可用路线。
2. Harbor的整体架构设计与组件选型分析
2.1 核心组件全拆解
Harbor从功能上可以拆成存储层、服务层、数据库层、缓存层四块。存储层负责镜像的物理存放,本地目录、NFS、对象存储(比如S3、OSS、MinIO)都可以;服务层有核心组件(提供API和Web界面)、注册表组件(负责镜像上传下载)、任务服务(执行复制、扫描、清理等异步任务)、Nginx入口控制器(负责流量分发和TLS终结);数据库层默认用PostgreSQL存储元数据,比如项目信息、用户信息、镜像标签、审计日志;缓存层用Redis,主要给会话管理和一些热数据访问加速。
小规模部署时,这些组件可以全部跑在一台机器上,Docker Compose一键拉起,这也是官方默认的方式。但生产环境里,每个组件都应该独立考虑扩容和容灾。比如数据库,如果只是单机PostgreSQL,数据库宕机意味着整个Harbor的元数据不可用,镜像虽然还在存储里,但用户完全无法感知,所以至少要主从或者走托管数据库服务。
2.2 存储选型决定了整个系统的上限
镜像仓库是个典型的读多写少、大文件顺序读写的存储场景。镜像层是不可变的,写进去之后基本不会修改,读取时是流式传输。所以存储选型有自己的特殊考量:需要支持海量小文件和偶尔的大文件并发读写,需要高吞吐,还需要有快照或者跨区复制能力来保障容灾。
本地磁盘方案性能最好,但容量受限于单机,扩容需要迁移数据,节点故障时数据跟着丢失,只能算评估环境或者小规模内部使用的选择。NFS共享存储是最常见的方案,多个Harbor节点共享同一个存储池,配合负载均衡实现多活,但NFS本身是单点,如果NFS挂掉或者网络抖动,所有节点都会受到影响,建议至少用存储网关做高可用。对象存储是最推荐的方案,Harbor原生支持S3协议,把所有镜像数据放到MinIO、AWS S3或者OSS上,存储的高可用交给对象存储自己来解决,Harbor节点变成了无状态服务,扩容和故障恢复都非常轻量。
我自己实际部署过本地目录、NFS和MinIO三种方案,最终在多个生产环境都选了MinIO作为镜像存储,配合独立部署的多节点Harbor,整体架构非常清爽。
2.3 为什么多节点加共享存储是高可用架构的核心
Harbor的镜像数据存储天然支持共享存储模式,多个Harbor实例指向同一个对象存储后端,前端通过负载均衡分发请求,数据库和Redis也各自独立部署成高可用模式,这样就形成了一个无单点故障的架构。
这个架构的思路是:Harbor服务节点本身是无状态的,任何节点的请求都直接操作共享存储和共享数据库,所以节点可以横向扩展,任何一个节点宕机,负载均衡会自动把流量引到其他节点,用户几乎无感知。存储层的可用性交给对象存储来保障,数据库层走主从或者托管服务,Redis同理。
注意:Harbor官方文档在介绍高可用方案时,有时会提到基于P2P预热的多实例复制模式,但那个复制模式本质是异步复制两个独立站点,而不是多活共享存储模式。异步复制存在数据延迟,极端情况下两个站点数据不一致,更适合异地灾备场景,不适合在同一机房内做负载均衡高可用。
3. 环境准备与部署前的关键决策
3.1 服务器规划与资源评估
Harbor本身对服务器要求不算高,但企业级部署要预留足够的资源给数据库、Redis、对象存储和未来的镜像增长。我自己的一般标准是:Harbor应用节点至少4核8GB,生产环境建议8核16GB起步;数据库节点独立部署,至少4核8GB;Redis至少2核4GB;对象存储单独一套,容量按镜像存量的2倍再预留半年增长来估算。
有一个经常被低估的资源是磁盘IO。镜像分批上传并发高的时候,磁盘IO会成为主要瓶颈。如果用的是云主机,建议本地盘用SSD,共享存储走专线或者内网高速网络,避免因为网络延迟导致上传超时。
3.2 域名与证书规划是很多人忽略的坑点
域名规划最好一开始就定下来,不要用IP直接访问。企业级场景里,镜像地址是要写进每个开发者的Docker配置和CI/CD流水线里的,如果以后要迁移或者扩容,IP变了就意味着所有环境要重新配置,而域名可以随意切换后端。推荐用镜像仓库专用域名,比如registry.企业内部域名.com。
证书方面,最稳妥的做法是使用企业内已有的CA签发的证书,这样所有内部主机的Docker daemon都内置了根证书,不需要额外配置insecure-registries。如果暂时没有内网CA,也要规划好证书的自动轮换机制,证书过期导致Docker登录失败是生产环境最常见的故障之一。
有不少团队直接关闭了TLS验证或者配置insecure-registries,图省事,这在测试环境没什么问题,但生产环境非常不推荐。镜像仓库涉及公司核心资产,TLS终止应该在负载均衡层统一处理,证书规范管理,客户端侧不要绕过验证。
3.3 数据库、Redis、对象存储的部署形态
生产环境我一般不推荐用Harbor自带的内部数据库和Redis,而是独立部署,原因很简单:Harbor升级或者节点重启时,内部数据库跟着一起重启,容易引发数据一致性问题;数据库需要独立的备份策略和性能调优;数据库和Redis需要监控告警,独立部署更容易接入现有的监控体系。
对象存储如果选择MinIO,建议也独立部署一套,多节点单奇偶校验配置,至少4个盘以上才能发挥纠删码能力。如果公司已经有S3兼容的对象存储服务,建议直接复用,运维成本更低。
4. 实战:基于Docker Compose的多节点高可用部署
4.1 生成配置文件的方法与技巧
Harbor的安装包里自带一个harbor.yml.tmpl模板,部署时复制成harbor.yml然后做修改。但如果是多节点,建议每个节点单独生成配置,不直接共用配置文件,避免因为hostname、端口等信息不一致导致组件识别混乱。
下载离线安装包后解压,进入目录,第一步是复制模板并修改关键项:
cp harbor.yml.tmpl harbor.ymlharbor.yml里最关键的几个配置项:hostname填域名,不要填IP;http和https部分按证书方案设置,生产环境启用https并关闭http端口;harbor_admin_password设置强密码;database部分改为指向外部PostgreSQL;redis部分改为指向外部Redis;storage_service部分配置S3对象存储的endpoint、bucket、accesskey和secretkey。
4.2 外部组件准备过程中容易踩的坑
外部数据库创建时,要注意Harbor对数据库的用户权限要求。我自己一般创建一个专用用户,赋予harbor库的全部权限,而不要直接给超级管理员权限,降低风险。连接串里要正确配置sslmode,如果数据库启用了SSL,Harbor连接参数也要对应修改,否则服务启动时会反复报连接失败。
Redis如果启用了密码认证或者用了不同的db index,都要在harbor.yml里如实配置。有些团队习惯把所有缓存都放在db0,但多应用共用Redis时建议给Harbor单独分配一个db,避免key冲突。
MinIO端要注意bucket访问策略,Harbor会通过S3协议直接读写bucket,所以bucket的access key需要具备读写权限,region要配置正确,如果region不匹配,S3客户端会报错。
4.3 安装脚本执行与多节点配置同步
在第一个节点上执行:
sudo ./install.sh --with-trivy--with-trivy参数会同时安装镜像漏洞扫描组件,企业级安全合规要求基本都绕不开这个能力,建议直接启用。install.sh会拉取Harbor的所有镜像并启动服务,同时生成docker-compose.yml文件。
第一个节点启动完成后,在其他节点上重复同样的过程,注意harbor.yml里的配置保持一致,尤其是数据库和Redis的连接串。为了让操作可追溯、可回滚,建议把harbor.yml和安装包一起纳入配置管理,每次变更都走评审流程。
4.4 负载均衡配置踩坑记录
负载均衡是承担TLS终结的角色,证书放在LB上,后端Harbor节点可以只用HTTP协议。但这里有个很关键的细节:如果LB后端用HTTP协议转发,Harbor的Web界面里某些生成URL的逻辑会根据请求的Host和协议来判断,如果不做调整,可能出现界面加载正常但Docker推送时报错或者镜像地址拼接错误的情况。
我的做法是:LB上启用HTTP到HTTPS的重定向,后端直接以HTTPS协议转发到Harbor节点,Harbor节点上也保留TLS配置,这样客户端拿到的URL永远是统一的HTTPS格式。后端节点的证书可以不使用正式证书,因为LB到后端的链路是内网,但Harbor节点自身的证书如果不被Harbor信任,Docker推送时仍然会报证书错误,所以后端节点要么配置成insecure,要么也签一个内部CA证书。
负载均衡的健康检查也是一处经常出错的地方。Docker Registry API的健康检查路径是/,但Harbor更适合用 /api/v2.0/health 这个接口来判断节点是否真的可用,因为根路径返回的只是一个基础空响应,即使后端某个服务已经异常,根路径可能依然200。
4.5 验证部署是否真的高可用
部署完成后,一定要做故障演练验证,不然所谓高可用只是纸面架构。我常做的验证方式:从开发者机器上连续地push和pull镜像,同时手动停掉一个Harbor节点的Docker服务,观察连接是否自动切换到另一个节点,是否出现失败重试。再停掉共享存储里的某个数据节点,观察读写是否继续正常。再测试数据库主从切换,正常情况主库挂了,从库接管后Harbor服务应该自动恢复连接。
这些演练如果之前没做过,就趁部署初期业务流量小的时候补上。真正等到线上出故障再考虑高可用验证,一切都晚了。
5. 深入Harbor数据流:一次镜像推送的背后发生了什么
5.1 镜像上传过程的完整链路分析
理解了数据链路,排查问题时才能有的放矢。以Docker push为例,Docker客户端发起请求后,请求先到负载均衡,LB转发到某个Harbor节点,Harbor的Nginx组件拦截请求,做TLS终结后转发给核心服务;核心服务先做身份认证,确认用户有推送到目标项目的权限,然后把镜像的元数据写入数据库,把镜像层文件流式写入对象存储对应的bucket中;写入完成后,任务服务触发镜像扫描,扫描结果再回写数据库。
如果中间任何一环出问题,表象都是push失败,但根因可能完全不同。看不到完整链路,排查就会变成盲人摸象。
5.2 读写路径上的性能瓶颈定位
读路径在生产环境中最关键,因为每次部署应用时都要拉镜像。读链路的主要性能瓶颈通常集中在几个位置:负载均衡连接数是否打满;Harbor核心服务的内存GC是否频繁;对象存储的网络带宽是否被占满;Docker daemon到仓库之间的网络链路是否拥塞。
写路径上的瓶颈相对少见,因为CI/CD构建推送的频率通常远低于拉取频率。但如果同一时刻有大量构建任务同时推送,对象存储的小文件并发写入能力就可能跟不上,表现为上传超时或者客户端收到503。
5.3 自己的镜像仓库有多大,需要提前监控哪些指标
建议上线前就规划好监控指标:API请求的成功率、平均时延和五十九分位时延;活跃连接数和队列长度;数据库连接池用量和慢查询数量;Redis内存使用率和命中率;对象存储的容量使用率和增长率;镜像扫描任务积压数量。
这些指标不一定全部都要采集,但核心的容量增长和API成功率一定要有,否则镜像仓库一旦出问题,排查起来完全没方向。
6. 安全加固与权限模型的最佳实践
6.1 项目权限体系怎么建才合理
Harbor的权限模型是项目维度的,每个项目可以关联不同成员,成员角色分为访客、开发者和维护者。访客只能拉取,开发者可以推送和拉取,维护者可以管理项目成员和配置。企业里建议按照团队和项目的对应关系来建项目,不要所有人共用一个项目,也不要把项目划分得太细碎,否则权限配置维护成本很高。
管理员账号的使用要非常谨慎,系统管理员权限能绕过项目级别的权限控制,不适合给普通成员使用。建议管理员账号只用于全局配置和故障处理,日常操作全部用个人账号。
6.2 机器人账号在CI/CD中的正确用法
Harbor提供了机器人账号机制,专门给自动化流程用。CI/CD流水线里如果直接用个人账号推送镜像,一旦人员离职或者密码修改,所有流水线都会受到牵连,这是典型的管理混乱。
在Harbor的每个项目里创建独立的机器人账号,给到对应的项目权限,把账号密码配置到CI系统的Secret里。机器人账号的名称最好带语义,比如ci-robot-前端项目,方便审计追踪。
6.3 镜像扫描与漏洞治理要不要全量开启
Trivy扫描器集成在Harbor里,可以对镜像做CVE漏洞扫描。生产环境我比较推荐的做法是:对构建推送到仓库的镜像强制开启扫描,扫描结果和项目内嵌的漏洞策略联动,如果检测到高危或者严重漏洞,新镜像不能被标记为可信或者不能部署到生产环境。
需要提醒的是,扫描会消耗计算资源,任务服务需要独立设置并发数和超时时间,如果压得太狠,扫描任务会占用大量CPU和内存,反而影响核心的镜像推送和拉取。
7. 镜像清理、复制与备份容灾的落地经验
7.1 容量治理策略:什么时候清理镜像,哪些镜像不能清理
镜像仓库的存储会随着版本迭代持续膨胀,尤其是有自动构建的团队,一天可能生成几十甚至上百个镜像。如果不做清理策略,存储成本会直线上升,对象存储的段文件碎片也可能越来越多。
Harbor自带的回收策略支持两种模式:基于时间,比如保留最近30天的镜像;基于数量,比如每个repository保留最近50个镜像。还可以配合tag保留规则,比如只保留带版本号的release镜像,测试用的临时镜像用完即弃。
注意:镜像清理后,存储空间不会立刻释放,而是打上删除标记,需要定期执行GC任务(垃圾回收)才能真正把存储空间腾出来。GC任务会带来一定的IO压力,建议安排在业务低峰期执行。
清理策略有个容易忽略的点:被复制到其他站点或者被当前运行中的工作负载引用的镜像,清理时需要特别谨慎。生产环境建议先做一次镜像依赖盘点,梳理哪些镜像被哪些应用的Deployment引用,引用中的镜像不要纳入自动清理范围。
7.2 多机房复制与容灾的取舍
Harbor的复制能力可以把一个站点的镜像同步到另一个站点,支持基于项目级别和tag级别的过滤规则,可以提供缓存预热功能。多机房场景下,让每个机房都有一份完整镜像副本,避免跨机房拉取镜像,显著降低延迟。这是比较典型的单向或双向复制的用法。
但复制是异步的,意味着主站点已经接受了一个镜像推入,但还未完全同步到从站点前,从站点上的拉取请求可能会失败或者拿到过期数据。这个问题在容灾场景下是可以接受的,因为目标是从站点在主站点故障时有可用的回退,而不是追求强一致的实时同步。
7.3 数据库和对象存储的备份恢复实操
Harbor的元数据在数据库里,镜像层在对象存储里,备份恢复必须两头同时抓。只备份数据库,不备份对象存储,恢复了元数据却找不到镜像文件,等于白干;只备份对象存储,不备份数据库,镜像文件都在但数据模型串不起来,也没有意义。
数据库备份用PostgreSQL的逻辑备份或者物理备份都可以,建议每天全量加基于WAL的增量。对象存储的备份可以依赖对象存储自身的跨区复制或者版本控制能力,如果是自建的MinIO,可以配置站点复制把数据备份到另一套MinIO集群。
恢复演练也必须做,而且最好是定期做,模拟从零恢复出一个完整的Harbor环境。很多团队备份做了几年,真正要恢复时才发现备份文件是坏的,这种教训太常见了。
8. 升级、迁移与运维周期管理
8.1 升级前必须检查的事项清单
Harbor迭代速度不慢,但生产环境升级一定要谨慎,不要追新版本,也不要长期停在太旧的版本。升级前需要检查:当前版本到目标版本的官方升级路径,Harbor有时候不支持跨大版本直接跳级;数据库Schema变更是否需要额外步骤;目标版本与当前外部组件的兼容性;备份是否完整并且已多次验证可恢复。
升级时最稳妥的操作是先在一台测试机上跑完整流程,包括配置迁移、数据库升级、数据校验,确认无问题后在生产环境执行。
8.2 灰度升级多节点集群要注意的细节
多节点Harbor升级时,可以考虑逐个节点滚动升级,而不是所有节点一起停掉升级。滚动升级的策略是:先从负载均衡上摘掉一个节点,在该节点上执行升级操作,等它恢复健康后重新挂载,再处理下一个节点。这样升级窗口内依然有节点在服务,不会中断镜像推送和拉取。
但要注意:如果升级涉及数据库Schema变更,旧版本节点和数据库可能不兼容,所以数据库变更必须与第一个节点的升级绑定,升级过程中其他节点的旧版本临时不能正常工作也是允许的。实际情况里,为了规避风险,我通常仍然会选择短暂停机窗口完成升级,特别是数据库变更比较大时。用一个明确的运维窗口换数据安全,这笔账值得。
8.3 日常巡检和关键技术指标告警
运维常态化之后,巡检清单可以固定下来:配置了外部数据库主从切换后,Harbor能否自动重连;镜像清理GC任务是否正常按照计划执行;对象存储容量增长是否超出预期;扫描任务是否有大量积压;TLS证书还有多久过期。
告警一定要有,特别是证书过期告警和存储容量告警。证书过期不会立刻导致Harbor崩溃,但所有客户端登录和推送会同时失败,这种故障经常在大半夜爆发。容量告警则要在对象存储快满之前提前预警,留出扩容和清理的窗口。
9. 一次真实的部署复盘:从踩坑到稳定运行
9.1 背景与方案选型
之前帮某个业务团队搭建了一套企业级Harbor,原始需求就一句话:公司内部多个研发团队需要一个统一的镜像仓库,要支持按团队权限隔离,要保障可靠性,未来还要支持异地容灾。架构师给的方案是:Harbor应用节点两台,前面负载均衡;PostgreSQL主从一套;Redis主从一套;MinIO单集群多节点做对象存储。
这个方案看起来更像一个标准模板,但真正落地的过程中还是踩了不少坑。
9.2 部署过程中实际遇到的三个大坑
第一个坑是数据库连接串配置。Harbor的配置里数据库的sslmode默认是disable,但外部数据库启用了SSL,部署时没有同步修改这个参数,结果Harbor核心服务启动后反复报数据库连接失败,日志里提示是SSL错误。排查了很久才发现是sslmode不匹配,改成require之后立刻恢复。
第二个坑是负载均衡健康检查路径问题。最开始配置的检查路径是根路径,负载均衡判断所有节点都是健康的,但实际两个节点都正常时看起来没事,后来一个节点的核心服务异常后,根路径依然返回200,流量继续往异常节点打,导致一批镜像推送全部失败。这个问题直接暴露了健康检查路径选择的重要性。
第三个坑是MinIO的bucket region和服务端不一致。Harbor写入对象存储时报错S3 region错误,检查MinIO控制台才发现创建bucket时用了默认region,而Harbor配置里指定的region和这个不一致。虽然MinIO的S3兼容层大部分场景下对region不敏感,但一旦Harbor的S3客户端校验了region,就会直接报错。
9.3 稳定运行后的体会
这套架构上线运行一年多,走了无数个版本的迭代,整体非常稳定。后来团队做容灾演练,模拟一个Harbor节点完全不可用,负载均衡自动切流,整个过程没有一条推送或拉取请求失败;模拟数据库主库宕机,从库自动接管,Harbor短暂抖动后恢复,没有出现数据丢失。
我个人在实际操作中最深刻的体会是:Harbor部署本身不难,难的是把周边生态一起规划好,存储、数据库、证书、监控、备份,这些环节缺一个都可能在某个深夜给你当头一棒。如果你正打算做企业级部署,建议从架构设计开始就多想几步,先想清楚未来半年到一年的容量需求和使用场景,再动手不迟。这套架构跑顺之后,团队从“镜像仓库是个黑盒”变成了“一切都在掌控之中”,这种安全感,才是企业级这三个字的真正含义。