1. 为什么说自建镜像仓库是容器化改造绕不开的一步
做容器化落地两三年,我越来越觉得镜像管理这件事,比想象中重要得多。早期阶段团队规模小,镜像直接往公共仓库一推,开发拉下来就能跑,谁也没觉得有什么问题。可当项目从两三个服务扩展到二三十个服务,环境从一台测试机变成开发、测试、预发、生产多套环境之后,镜像分发立刻成了瓶颈。公共仓库的拉取速度不稳定,时不时限额报错,私有代码和内部镜像更是不能随便往外放。也就是在那段时间,我被逼着认真调研了一轮自建镜像仓库的方案,最后选定了Harbor,一直用到现在。
Harbor是个开源的容器镜像仓库管理平台,基于Docker Registry做了大量扩展,核心能力包括:基于角色的访问控制、镜像同步复制、漏洞扫描、审计日志和垃圾回收。它不像裸的Registry那么好用,后者只有最基础的存储和拉取推送功能,权限管理几乎为零,多人协作时谁都能删项目、推覆盖同名镜像,安全性和规范性都跟不上。
对需要内网交付、离线部署、多环境同步的团队来说,Harbor基本是标配选择。这篇文章我会把从环境准备、安装部署、核心配置到日常运维、问题排查的完整过程都理一遍,中间穿插我实际踩过的坑和调优思路。正在做镜像仓库选型的人、准备在一台服务器上搭Harbor的开发者,或者已经装上但用得不顺畅的运维同学,都可以参考这份经验。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 部署前的设计与选型思路
2.1 先想清楚部署架构,再动手下载安装包
我见过不少人上来就clone压缩包、改配置、跑脚本,结果装到一半发现端口冲突、证书没配、存储路径不对,只能推倒重来。部署Harbor前,有几件事必须盘清楚。
第一,机器的角色。Harbor是典型的服务端应用,它的核心职责是把镜像文件高效地托管在磁盘上。CPU和内存要求都不高,但是磁盘IO和存储容量极其关键。我目前用的是6个共享存储挂载点对应不同项目域,跑了大半年,实测下来稳定可靠。
第二,访问入口的规划。Harbor默认支持HTTP和HTTPS两种方式,生产环境我强烈建议直接上HTTPS。后面配置镜像拉取时就要明白,Docker在非安全模式下对私有仓库的处理比较麻烦,需要在每台客户端上做额外配置,如果服务端能直接提供有效证书,客户端那边几乎零成本接入。
第三,存储的归属。Harbor的数据包含两块:一块是数据库,存放用户、项目、镜像元数据、审计日志;另一块是registry存储层,存放实际的镜像图层文件。数据库建议放在本地SSD上,镜像数据可以根据规模考虑本地大容量磁盘或挂载NAS、对象存储。这个设计要提前定好,因为部署后改存储路径比较折腾,涉及数据搬迁。
2.2 数据库选型与高可用预案
Harbor的元数据默认使用PostgreSQL存储,安装包内嵌的数据库适合单机或小规模场景。我最初图省事用了内嵌数据库,后来在升级和维护时遇到一些限制,才开始考虑外置数据库。
外置数据库的好处是便于统一备份管理,多个应用可以复用一套高可用数据库基础设施。如果你对可靠性要求比较高,建议在规划阶段就把外置PostgreSQL准备好,版本建议12以上,配置一个大一点的链接池。要注意的是,Harbor迁移到外置数据库时,需要导出和导入整个数据库数据,新旧版本结构不一致时容易出问题,最好在测试环境先验证。
2.3 部署方式对比:在线安装、离线安装还是Helm
Harbor配了三种常见安装方式,它们的使用场景差别很大:
- 在线安装:官方提供一个在线installer包,体积小,安装时临时拉取所需的镜像。适合网络环境通畅的场景。
- 离线安装:把安装包和所有依赖镜像打包成一个约几十GB的压缩包,一次下载后拷贝到目标机器上直接部署。适合内网隔离、离线交付的场景。我实际做交付时绝大多数都是走离线安装。
- Helm Chart安装:适合已经有Kubernetes集群的团队,把Harbor作为集群内应用托管,通过Helm管理版本升级、配置变更和扩缩容。
我的建议是:如果目标机器网络能访问Docker Hub和GitHub,在线安装足够,省去大体积传输;如果目标机是数据隔离区或客户现场,必须离线安装;如果团队已经以Kubernetes为底座,那么用Helm部署更顺滑,可以利用集群能力做负载均衡和存储编排。
3. 一步步把Harbor装起来:完整实操记录
3.1 基础环境准备
我以CentOS 7.9虚拟机为例,这个版本在企业里还是大量存在,做法上有一定代表性。先确认内核、Docker和Docker Compose的版本。Harbor不同版本对Docker版本有要求,总体Docker 20.10以上基本没问题。Docker Compose建议使用v2以上版本,独立二进制文件就行。
在没有Docker的环境里先装Docker。可以用yum源或官方脚本,装完后执行docker version确认server端和client端都能正常工作。然后安装Docker Compose插件,下载二进制放到/usr/local/bin目录,赋上执行权限即可。这一步最容易被忽略的是没有把普通用户加入docker组,后续执行命令总是要加sudo,操作体验不好,我习惯在安装初始就处理好这个。
3.2 下载Harbor离线包并校验完整性
到Harbor的GitHub Releases页面下载离线安装包时,注意选择版本分支和架构。x86服务器选amd64,ARM服务器选arm64,踩过选错架构的坑,装完nginx起来但是registry服务一直异常,后来发现是镜像架构不匹配。
离线包下载完成后,先做SHA256校验,和官方提供的校验值比对,确保文件完整。然后解压到/opt/harbor目录,这个路径可以按自己习惯调整,但要注意目录权限和后续升级时的规范性。
bash复制mkdir -p /opt/harbor
tar -zxvf harbor-offline-installer-v2.9.5.tgz -C /opt/harbor --strip-components=1
cd /opt/harbor
3.3 配置harbor.yml:这些参数不能随便填
解压后的目录里有harbor.yml.tmpl模板文件,把它复制成harbor.yml再编辑。这个文件几乎决定了Harbor的所有核心配置,我逐项说下关键点。
hostname必须设置为服务器的实际访问地址,可以是域名,也可以是IP。它会被写进镜像的tag前缀和客户端登录地址里。如果后面改了hostname,所有已推送镜像的地址信息都会变化,客户端需要重新打tag再推送,所以开始就要定好。
harbor_admin_password设置了admin超级管理员密码。默认值是Harbor12345,生产环境必须改掉,建议至少16位混合大小写和特符。这个密码会在安装脚本执行时写入数据库,后面可以在界面里再改,但初始化时就定一个强密码更稳妥。
data_volume是镜像数据存储的根目录,默认/data。这个目录建议放到独立的数据盘分区下,别和系统盘混在一起,因为镜像仓库的磁盘增长速度比较快,一旦系统盘灌满,整个服务都会出问题。
https节里配置证书和私钥路径。证书和私钥必须在执行安装前就生成好,并且要正确配置权限,Harbor服务进程需要能读取这些文件。
database节里配置的是内嵌PostgreSQL的密码,如果你不打算用外置数据库,这里建议改掉默认密码,防止安全弱口令。
我把典型的harbor.yml配置写出来,大家可以直接对照:
yaml复制hostname: registry.example.com
http:
port: 8080
https:
port: 443
certificate: /data/certs/registry.crt
private_key: /data/certs/registry.key
harbor_admin_password: Astr0ngP@ssw0rd!
database:
password: D@tabaseP@ss
max_idle_conns: 100
max_open_conns: 900
data_volume: /data
trivy:
ignore_unfixed: true
skip_update: true
insecure: false
jobservice:
worker_num: 10
notification:
webhook_job_max_workers: 5
3.4 执行安装脚本的完整过程
配置好harbor.yml后,运行install.sh就能开始安装。这个脚本会先加载镜像,然后执行prepare准备配置文件,最后用Docker Compose拉起服务。
加载镜像这一阶段耗时最长,离线包里的镜像文件比较多,用docker load逐个导入。这一步的瓶颈在磁盘IO,如果机器配置比较差,可能要等十几分钟甚至更久。等所有镜像加载完,脚本进入prepare阶段,会校验配置文件生成所有容器的环境变量和挂载参数。然后是安装阶段,执行docker compose相关命令启动服务。
bash复制sudo ./install.sh --with-trivy
注意安装脚本后面带的一些选项:--with-trivy内置漏洞扫描器,--with-notary用于镜像签名,--with-chartmuseum用于管理HelmChart。如果暂时用不到,可以不加,后续也可以通过重新执行prepare和install来启用。
安装完成后,执行docker compose ps查看容器状态。正常情况下会有harbor-core、harbor-registry、harbor-db、harbor-jobservice、nginx等容器处于Up状态。然后用浏览器访问配置的hostname和端口,看到Harbor的登录页面就说明部署成功了。
3.5 客户端接入验证
服务端起来后,第一时间在客户端上验证登录、推送、拉取整个流程。先创建测试项目,然后用docker login登录仓库。
bash复制docker login registry.example.com
登录成功后给本地镜像打上仓库前缀的标签再推送:
bash复制docker tag nginx:latest registry.example.com/test/nginx:1.24
docker push registry.example.com/test/nginx:1.24
推送完成后在Harbor界面的对应项目里能看到镜像列表和标签信息。拉取就不用多说了,docker pull后面跟同样的地址。如果客户端报证书不受信任的提示,要么把服务端证书加到客户端的信任列表,要么在客户端的docker配置里把该地址加入insecure-registries。对内部环境来说,如果暂时没有正规CA签发的证书,用insecure-registries是最省事的做法,但安全要求高的环境不推荐。
4. 核心功能配置:安全、同步与扫描
4.1 HTTPS证书方案
内部环境搞HTTPS证书有几个思路:自建内部CA颁发证书,用现有的企业CA,或者购商业证书。我建议走自建内部CA的方式,一次性把CA根证书下发到所有服务器和开发机,后续签发新证书都在这个CA体系里,客户端只需要信任一次根证书。
自签证书可以用openssl生成:
bash复制openssl genrsa -out ca.key 2048
openssl req -x509 -new -nodes -key ca.key -days 3650 -subj "/CN=Internal CA" -out ca.crt
openssl genrsa -out registry.key 2048
openssl req -new -key registry.key -subj "/CN=registry.example.com" -out registry.csr
生成CSR时,注意把镜像仓库的访问域名配到Common Name和SAN里,否则浏览器会报域名不匹配。在openssl配置中加上subjectAltName=DNS:registry.example.com、IP:192.168.1.10这些信息,然后用CA证书签署:
bash复制openssl x509 -req -in registry.csr -CA ca.crt -CAkey ca.key -CAcreateserial -out registry.crt -days 825 -extfile ext.cnf
签出来的registry.crt和registry.key放到harbor.yml里配置的路径下,然后重跑./install.sh让配置生效。把ca.crt分发到各客户端的/etc/docker/certs.d/registry.example.com/ca.crt之后,docker pull和push就不会再报证书错误了。
4.2 认证与权限体系
Harbor的认证模式有本地数据库、LDAP/AD、OIDC三种,在系统管理的外部认证设置里切换。
本地数据库模式下,管理员在界面手动创建用户,然后把用户加到项目成员里并分配角色。角色分项目管理员、开发者、访客等,权限粒度能精细到查看、推送、拉取。这种方式管理成本低,适合团队规模不大、没有统一认证体系的场景。
如果有公司统一的LDAP或AD域,可以切LDAP模式,用户在Harbor登录时走域账号验证。配置时需要填LDAP地址、BaseDN和UID属性等参数。我实际配置时遇到过属性映射不对导致用户登录成功但看不到项目的情况,建议小规模验证时先把LDAP搜索用户的功能调试通。
项目级别还有一项很实用的权限控制:机器人账户。它不是给人用的,而是给CI/CD流水线用的。创建机器人账户时可以精确指定它只能访问某个项目、只能推送或只能拉取。我把流水线的发布阶段配置成使用机器人账户,避免了把个人账号密码写到流水线配置文件里的隐患。
4.3 镜像复制同步,解决多环境分发问题
镜像复制是Harbor比较吸引人的能力。它能把一个仓库里的镜像自动同步到另一个Harbor实例或标准Registry。
复制模式有两种:推送模式和拉取模式。推送模式是源Harbor主动往目标仓库推;拉取模式是目标Harbor从源仓库拉。我在多数据中心的场景下,常用推送模式从中心仓库往边缘节点同步精选镜像。
配置复制规则时,要定义源、目标实例类型并填认证信息。如果两个Harbor实例之间走HTTPS,目标证书要配置正确,否则同步任务会一直报错。同步成功后,源镜像有任何更新,符合条件的新tag会自动推过去。这个功能我强烈建议用起来,它解决了很多环境之间镜像版本不一致的扯皮问题。
4.4 漏洞扫描器Trivy的接入
Harbor从2.0开始,默认集成了Trivy扫描器。在install.sh里加--with-trivy参数后,系统管理里会多出漏洞扫描的配置。
需要明确一点:Trivy扫描依赖漏洞数据库,第一次运行时会尝试下载数据库文件。内网环境访问不了公共网络时,需要手动更新漏洞数据库,或者在harbor.yml里把trivy部分的skip_update设为true,然后用离线方式导入数据库。数据库文件的版本要和Trivy对应上,否则扫描插件会异常。
扫描策略可以手动执行,也可以配置成定时扫描。项目里有新的镜像推入时,手动点扫描按钮通常就够了。扫描结果按严重程度分级展示,能看到漏洞所在层和修复版本。实际运维时,我会重点关注Critical和High级别的漏洞,联系开发修复后重新构建基础镜像。
5. 日常运维踩坑实录与排查手段
5.1 高频故障和解决清单
把这段时间遇到的典型问题整理成速查表,方便直接套用:
| 问题现象 | 可能原因 | 处理方式 |
|---|---|---|
| Harbor页面打不开 | 部署时http.port被占用 | 检查端口占用,改harbor.yml里的端口后重跑prepare和install |
| docker login报证书错误 | 客户端不信任服务端证书 | 导入CA证书到客户端或配置insecure-registries |
| docker push提示413 | Nginx上传体积限制 | 深入检查nginx的client_max_body_size配置,大镜像时需要调大 |
| 镜像推送成功但页面不显示 | 数据库和registry不同步 | 查看harbor-core日志,确认项目空间和仓库状态,必要时重启harbor服务 |
| 磁盘空间疯涨 | 历史镜像和垃圾层堆积 | 配置回收策略,定期执行垃圾回收 |
| 扫描器一直提示未就绪 | Trivy数据库未初始化 | 手动导入数据库或放开网络更新限制 |
| 复制任务失败 | 目标仓库认证失败或网络不通 | 检查目标实例类型配置和认证凭据 |
5.2 垃圾回收机制:软删除和硬清理的区别
Harbor的垃圾回收功能是个重点,它解决的是镜像删除后磁盘空间不释放的问题。在Harbor里删除镜像或者覆盖镜像标签,实际上只是删除了元数据索引,底层图层仍然留在存储目录里。真正释放空间需要执行垃圾回收。
执行垃圾回收有两条路径:界面上直接在项目或系统管理里操作,或者调用API。执行GC时,Harbor会启动一个jobservice任务,扫描所有未被引用的镜像层,并把它们列出来。清理前会先做软删除,把可清理的层标记出来,确认无误后再做硬删除。
我踩过的坑是:在业务高峰时段执行GC,导致拉取镜像的请求变慢。GC期间jobservice会扫描大量图层文件,磁盘IO明显增多,建议在维护窗口操作。另外,GC不会一次把空间全部释放,要看有多少镜像层真正未被引用,删除所有镜像后执行GC,空间才会大片掉下来。
5.3 备份和恢复:不要只备份数据库
Harbor的数据分成两部分,备份时缺一不可。数据库里存的是所有项目、用户、镜像元数据;registry存储目录里存的是实际镜像层文件。只备份数据库不备份存储目录,恢复后镜像列表还在,但拉取时大概率失败,因为图层文件丢了一半。
我的备份方案分两步走。数据库走pg_dump导出SQL文件,可以放在本机定时执行。存储目录通过rsync同步到备份服务器,或用文件系统快照。恢复流程也不复杂:先恢复数据库,再恢复目录数据,然后重启Harbor服务。
bash复制docker exec harbor-db pg_dump -U postgres -d registry > harbor_db_backup.sql
rsync -av --delete /data/registry/ backup-server:/backup/harbor/registry/
5.4 升级迁移的几个注意点
Harbor版本升级不能直接跳版本,官方文档会说明每个版本之间的升级路径。升级前先备份数据库和存储目录,再下载对应版本离线包,执行./upgrade.sh升级脚本。它会自动检测当前版本和旧配置,做数据库迁移。
我在升级时遇到最多的问题是config配置参数变化导致prepare阶段失败。建议升级前对比新旧harbor.yml模板,把废弃的字段清理掉。还有一点:升级后所有容器会重建,IP和端口需要注意别和现有服务冲突。
6. 生产环境下的使用技巧与效率工具
6.1 项目规划和命名规范
Harbor里的项目建议按团队或业务域划分。我用的是两层结构,第一层是业务域,第二层是镜像用途(基础、发布、试验版本)。项目的公开/私有属性要严格控制,内部项目一律设为私有,避免误拉取。
镜像tag的命名规范也应该定下来,一般用三段式:服务名-环境-版本号。比如gateway-prod-1.2.0。别图省事用latest打天下,多环境指向同一个latest,后面排查问题时很难定位线上到底跑的是哪个镜像。
6.2 镜像代理缓存功能
Harbor内置了和Docker Hub等公共仓库对接的代理缓存能力。配置一个代理项目,把远程仓库指到Docker Hub,团队拉公共镜像时统一走这个代理项目。内网拉取公共镜像的速度会快很多,同时也方便审计到底拉过哪些镜像。
这个功能适合开发阶段大量使用公共镜像的场景。配置好后,拉取的方式稍微变化,比如原来拉nginx,走了代理项目后,拉取命令变成registry.example.com/proxy-dockerhub/library/nginx:latest。缩短镜像tag层级,可以自己写个脚本做映射方便使用。
6.3 通过API做自动化运维
Harbor提供完整的REST API,日常运维可以脚本化。我经常用的两个调用场景:批量创建项目,还有批量清理长期不活跃的镜像。
查询项目下所有仓库和tag的API接口是/api/v2.0/projects/{project_name}/repositories,再对每个repo查一下tag列表和下载量。下载量很低的tag基本上可以考虑清除。写个Python脚本定期跑一遍,把超过N天未拉取的镜像删除或归档,能有效控制仓库膨胀。
bash复制curl -u admin:yourpass https://registry.example.com/api/v2.0/projects
6.4 日常监控需要盯住的几个指标
Harbor的监控主要包括三方面:服务健康、磁盘空间、同步任务状态。服务健康可以通过/api/v2.0/health接口拿到组件状态。磁盘空间最直接是df -h看data_volume挂载点,建议磁盘使用率超过80%就开始清理。
同步任务状态可以从界面或API查看,正常情况下复制任务都是Success状态,出现Failed要尽快处理,否则边缘节点会一直停留在旧版本镜像。有条件的话可以把这些指标接入现有的监控体系,Harbor本身暴露了一些Prometheus指标,方便从中间件角度监控。
7. 最后聊几点实在的体会
用过Harbor这近两年,最大的体会是它很容易上手,但要用得好并不简单。初始部署只是开始,后续的权限规划、镜像生命周期管理、同步策略和备份机制,才是真正影响团队效率的地方。我一度因为图省事跳过HTTPS配置直接HTTP裸奔,后来客户端不断报证书错误,重新补证书时又发现所有镜像的访问地址已经写死在编译配置里,改造成本成倍增加。所以,该规范的步骤别省。
如果团队规模不大、镜像总量可控,一台4核8G的服务器加上一个简单外置数据库就能把Harbor跑得很顺。如果后续要支撑几十个团队和数百个仓库,那就得好好考虑对象存储、多实例和高可用架构。我现在倾向的演进路径是:单机起步、外置数据库、存储层挂NAS、多个Harbor实例做复制,逐步过渡到Kubernetes上的Helm部署。这套思路大家可以根据实际情况调整,起点不一定要高,但是每一层都要有清晰的规划。
