资讯动态

Harbor镜像仓库离线部署指南:从证书配置到运维实战

发布时间:2026/10/20 17:39:27 来源:尧图企业网站定制
如果你所在团队打算搭建自己的 Docker 镜像仓库又不想在权限管理、镜像复制、漏洞扫描这些环节上重复造轮子Harbor 基本是绕不开的名字。我见过不少内部项目最开始就是一台机器一个官方 Registry跑着跑着就发现想控制某个项目只有指定开发能推镜像连个像样的用户体系都没有想知道哪个镜像什么时候被谁改动过更是一问三不知。这篇就围绕 Harbor 的部署讲讲我自己的选型逻辑、离线安装过程、证书配置的坑以及上线之后你大概率会遇到的那几个运维问题。需要提前说明我按 Harbor 2.x 的部署方式来写代码和配置都以离线安装包为例因为生产环境出于安全和网络考虑多数还是走内网离线包。如果你手里是 1.x 版本的配置习惯注意 harbor.cfg 和 harbor.yml 差别很大网上那种老教程要谨慎参考。1. 为什么最终选 Harbor基础 Registry 的三处硬伤1.1 第一个硬伤权限模型几乎为零官方 Docker Registry 其实定位很简单一个带认证的镜像存储服务能跑、能存、能拉但对于企业内部使用处处捉襟见肘。最典型的问题就是对“项目”的隔离。官方 Registry 的镜像基本都堆在一个命名空间下面虽然有 token 认证但要么谁都能推要么配起来非常原始。开发团队多了以后谁也说不清这个镜像到底是哪个小组负责的更别说让某些人只能拉取、某些人才能推。Harbor 把“项目”当成最基础的权限边界一个项目可以公开也可以私有项目成员分成管理员、维护者、开发者、访客等角色。推到不同项目需要不同的授权这个模型虽然不复杂但在实际协作中特别有用。比如前端组和算法组共用一个仓库你只需要把两边成员分别加进各自项目互相不干扰。1.2 第二个硬伤缺少复制、审计和治理能力第二个让人头疼的问题是复制和审计。官方 Registry 基本没有跨机房同步的机制最多你手动把镜像导成 tar 包再导到另一台机器。碰到多机房、容灾场景就只能自己写脚本硬顶。审计日志也是企业环境的硬需求。Harbor 会对登录、推送、拉取、删除等关键操作做记录出问题的时候至少能追到是谁操作过哪个镜像。对不少开发团队来说能用日志回答“这个镜像为什么出现在 3 月 14 日凌晨两点”就已经值回部署成本了。镜像治理则是第三个隐藏痛点。镜像越堆越多占满磁盘只是时间问题。Harbor 自带清理策略可以按 tag 保留数量、按时间保留、按标签规则清理配合自带的 GC 垃圾回收能力能定期把不再被引用的镜像层清理掉。官方 Registry 想做到这种程度你得自己写脚本调 API运维成本一点都不低。1.3 为什么不用 Nexus 或其他制品库也有人问我Nexus 不也能管镜像吗确实能。Nexus 作为通用制品库对 Maven、npm、PyPI 这些组件的支持非常成熟如果你们团队已经长期用 Nexus 管理所有制品把镜像也放进去是可以的。我的判断标准是如果你们只是需要一个容器镜像的专用仓库没有那么多通用制品管理的存量包袱Harbor 更对症也更轻。Harbor 是专门围绕容器镜像这个场景做的登录页、项目模型、镜像复制、漏洞扫描这些功能都直接长在需求上。在上面三个痛点上它解决的效率比通用制品库高得多。能力点官方 Docker Registry通用制品库 NexusHarborWeb UI极简有但通用完整面向镜像多项目权限弱一般强角色划分清晰镜像复制无部分支持原生项目级规则漏洞扫描无部分内置 TrivyTag 保留与清理无一般专门规则配置审计日志有限一般完善如果你已经有一套成熟的镜像安全扫描链路或者深度绑定云厂商的制品库那么选型另说。但纯自建、要长期维护Harbor 通常是最省心的那个。2. 动手部署前要敲定的 5 个配置决策2.1 在线安装还是离线安装Harbor 提供在线和离线两种安装包。在线安装包很小但执行 install.sh 时会从公共镜像仓库拉取 Harbor 各组件的镜像。内网环境或者外网拉取不稳定的场景不建议碰它。离线安装包是一个几百 MB 的压缩包解压之后自带所有组件镜像安装时直接 docker load 导入全程不依赖外网。我的建议很直接能拿到离线包就用离线包减少一次不确定性也让后续升级时对“当前跑的是哪个版本”心里更有数。2.2 数据落盘位置和容量预估Harbor 默认把镜像数据和数据库文件放在 /data 下这个目录的容量和备份策略必须在部署前想好。镜像仓库的磁盘消耗比大多数人预估的快尤其是团队 CI 迭代频繁、每次构建都打新 tag 的场景两三个月吃掉几百 GB 很正常。容量规划上我会建议至少给 /data 单独挂一块数据盘并且预留 20% 以上的余量。如果条件允许直接把 /data 挂在分布式存储或共享文件存储上后续做高可用会方便很多这个后面专门讲。2.3 对外域名、端口和反向代理Harbor 对外暴露的端口默认是 80 和 443。如果你机器上已经有 Nginx 占用 80/443就需要提前改端口或者把 Harbor 放到 Nginx 后面做反向代理。另一个容易忽略的点是 hostname 配置。harbor.yml 里填的是客户端用来访问的地址建议填一个正式域名而不是 IP。虽然填 IP 也能跑但后续证书、复制、外部集成都会有坑尤其是一个 Harbor 实例要对接多台构建机时域名比 IP 更稳定。2.4 HTTPS 证书从第一天就上这是整个部署过程里最值得花时间的一件事。Harbor 虽然支持 HTTP 方式部署但 Docker 客户端默认用 HTTPS 访问仓库HTTP 模式下你得在每台构建机上配置 insecure-registries不仅麻烦而且会让所有客户端跳过 TLS 验证安全上很不舒服。证书来源有三个公共 CA 签的证书、企业内部 CA 签的证书、自签名证书。有企业 CA 就直接用企业 CA没有就搭一个内部 CA用它签发 Harbor 证书实在不想搞 CA才考虑自签单张证书。但无论哪种都比裸 HTTP 强。2.5 单机跑起来还是直接上高可用第一天部署不建议直接上高可用。Harbor 高可用涉及外部数据库、外部 Redis、共享存储、负载均衡还有一个多实例配置复杂度至少翻一倍如果你对 Harbor 的组件还不熟出问题根本排不过来。我的经验顺序是先用单机模式跑通流程摸清 registry、core、db、redis 这些组件的关系确认每日备份。等到业务量上来、对容灾有明确要求了再做高可用改造。反过来如果你明知道这个仓库上线就是几十个团队的硬依赖那就直接按高可用架构搭别回头再来一次迁移。3. 离线包安装的完整过程与每一步的意图3.1 准备环境两条命令确认 Docker 和 ComposeHarbor 对 Docker 版本比较宽松但 2.x 安装脚本依赖 Docker Compose v2 语法。部署前先确认两件事docker version --format {{.Server.Version}} docker compose version如果你的环境里没有 docker compose 插件只有旧的 docker-composeHarbor 安装脚本也能识别但我建议直接装好 compose 插件后续维护命令更统一。Harbor 2.x 的容器编排文件按 Compose v2 规范来写旧版 docker-compose 偶尔会在启动参数上报错。3.2 配置 harbor.yml不是照抄是逐项确认拿到离线包解压之后先别急着执行安装脚本先打开 harbor.yml 看一遍。这个文件是所有配置的核心改错一项后面排查几小时都是正常的。一个常见的最小配置项如下hostname: harbor.example.com http: port: 80 https: port: 443 certificate: /data/certs/server.crt private_key: /data/certs/server.key harbor_admin_password: YourStrongAdminPassw0rd database: password: YourStrongDBPassw0rd max_idle_conns: 50 max_open_conns: 100 data_volume: /data逐项说几个重点。hostname 填外部访问域名比如 harbor.example.com。如果这个域名客户端解析不到后面连镜像都拉不了而且这是客户端机器上的问题排查起来很容易绕圈子。harbor_admin_password 是初始管理员密码。很多人懒得改或者设成 123456 先跑起来我强烈建议第一次安装就设一个强密码并且立刻交给密码管理平台。管理员密码在外面裸奔镜像仓库基本等于门没锁。database 下面默认是 PostgreSQL 的密码。这个密码不仅在生产环境要改还要保证和 admin 密码不一样因为它会被写进 compose 环境变量很多安全问题就是从内部数据库弱口令开始的。data_volume 决定镜像和数据库文件的落盘位置。默认是 /data我一般会把它指到一个单独的大容量挂载点比如 /srv/harbor/data然后把这个目录单独做备份和扩容。3.3 执行 prepare 和 install看细节而不是等结果配置文件改好之后可以直接跑 ./install.sh但我的习惯是先手动跑一遍 ./prepare再跑安装脚本。prepare 的作用是预生成 Harbor 的 docker-compose.yml、Nginx 配置、证书挂载把 harbor.yml 的内容植入到各组件。如果 harbor.yml 写错了大部分错误会在 prepare 阶段暴露而不是等到启动容器时才翻车。先跑 prepare 能让你更快看到问题。install.sh 其实做了两件事加载离线包里的组件镜像然后通过 docker compose 启动全部容器。整个过程的输出很长不要看到进度条就干等注意几类关键日志镜像 load 是否完整有没有缺包prepare 是否报错通常和证书路径或端口冲突有关最后容器启动后docker compose ps 的 STATUS 是不是 healthy。启动完成后用下面三条命令快速验证基本状态docker compose ps curl -k https://harbor.example.com/ curl -k https://harbor.example.com/api/v2.0/pingcurl 返回一个正常页面或 ping 返回 pong说明 Web 层已经起来了。接下来才是完整的链路验证。3.4 用真实镜像走一遍登录到推送的完整链路页面能打开只是第一步真正要确认的是 Docker CLI 能正常登录、推送、拉取。找一台构建机执行docker login harbor.example.com docker tag nginx:latest harbor.example.com/demo/nginx:latest docker push harbor.example.com/demo/nginx:latest这里要注意第一次登录会碰到证书信任问题报错可能五花八门。下一章专门说这个坑。推送成功后去 Harbor 页面的 demo 项目里确认 tag 存在再顺手 docker pull 一次拉取链路也算是好的。从我的经验来看如果这三条命令能一次通过说明 harbor.yml、证书、网络解析、数据目录权限基本都对了如果卡住后面排查优先级就是证书信任 hostname 解析 端口放通 数据卷权限。4. HTTPS 证书与 Docker 客户端信任链最磨人的部署关卡4.1 “http: server gave HTTP response to HTTPS client”是怎么冒出来的这个报错我在不同项目里至少见过几十次基本上是 HTTPS 配置问题最经典的代表Error response from daemon: Get https://harbor.example.com/v2/: http: server gave HTTP response to HTTPS client原因是 Docker CLI 默认用 HTTPS 和仓库通信但 Harbor 那端实际跑的是 HTTP。两边协议不一致客户端一眼就认出来了。解决办法不是关掉 Docker 的 HTTPS而是让 Harbor 跑 HTTPS或者让 Docker 客户端知道这个地址可以走 HTTP。后者就是 insecure-registries虽然配置起来一行搞定但我真心不建议长期用。跳过 TLS 验证意味着仓库和客户端之间的通信是可窃听、可篡改的镜像在传输过程中被替换都不是没可能。4.2 自签证书从服务端到客户端的完整交付比较标准的做法是在服务端生成自签 CA然后用这个 CA 给 Harbor 签一张站点证书。Harbor 在启动时读 harbor.yml 里配置的 certificate 和 private_key对外提供 HTTPS。客户端的 Docker daemon 则需要信任这个 CA信任链建立之后就不会再报警。生成 CA 和服务器证书的命令参考# 生成 CA 私钥和证书 openssl genrsa -out ca.key 4096 openssl req -x509 -new -nodes -days 3650 -key ca.key -subj /CNharbor-ca -out ca.crt # 生成服务器私钥和证书请求 openssl genrsa -out server.key 2048 openssl req -new -key server.key -subj /CNharbor.example.com -out server.csr # 配置 SAN把域名和 IP 都加进去 cat ext.cnf EOF subjectAltName DNS:harbor.example.com, IP:192.0.2.10 EOF # 用 CA 签发服务器证书 openssl x509 -req -in server.csr -CA ca.crt -CAkey ca.key -CAcreateserial \ -days 730 -extfile ext.cnf -out server.crt生成之后把 server.crt 和 server.key 放到证书目录并在 harbor.yml 里指向它们。这里有个小细节容易被忽略SAN 一定要包含客户端实际访问的域名或 IP。如果客户端通过域名访问SAN 里就要有那个域名如果通过 IP 访问就要有 IP。我第一次配的时候只在 SAN 里写了域名结果有台机器硬是走 IP 访问TLS 校验直接不认账。客户端信任 CA 的做法是把 ca.crt 放到 Docker 证书目录下sudo mkdir -p /etc/docker/certs.d/harbor.example.com sudo cp ca.crt /etc/docker/certs.d/harbor.example.com/ca.crt sudo systemctl restart dockerDocker daemon 在访问 harbor.example.com 时会去 /etc/docker/certs.d/harbor.example.com/ 下找 ca.crt 做校验。这个目录路径确实是按域名命名的错一个字都不认。4.3 证书更新后为什么 restart 容器不管用还有一种非常容易踩的坑证书过期或换新证书后有人直接 docker restart harbor-core、docker restart nginx发现 HTTP 服务还是旧证书。原因是 Harbor 的 Nginx 配置和证书挂载是在 prepare 阶段生成的只 restart 容器不会重新走 prepare。正确的操作顺序是# 先停掉当前 Harbor 容器 docker compose down # 替换服务器证书和私钥 # 重新运行 prepare重新生成配置 ./prepare # 重新启动 docker compose up -d每次改 harbor.yml、换证书、调整端口都要记得重新执行 prepare。这个习惯养成了能省掉大量“我看着配置改对了为什么没生效”的排查时间。4.4 内网构建机批量配好 CA 的细节如果只有一两台构建机手动放 CA 证书还好如果有十几台 Jenkins 节点、K8s Worker就得把 CA 分发做成自动化。最常见的方式是用 Ansible 推送或者写个简单脚本把 ca.crt 分发到每台机器的 /etc/docker/certs.d/harbor.example.com/ 下分发后重启 docker 服务每台机器执行 docker login 验证。这里一定要做验证不要批量分发完就当完了。常见情况是部分机器的 Docker 版本比较旧需要重启才能重新加载证书目录还有机器通过代理访问仓库代理层证书又是另一套逻辑这种问题不验证根本发现不了。提示内网部署的 Harbor 如果未来要换 IP 或换域名一定要提前评估哪些客户端已经配好了 CA 信任和 Docker 登录。很多团队在这里吃过亏域名变更导致所有构建任务中断。5. 上线后反复遇到的高频问题与修复记录5.1 admin 密码丢失是一道应急题Harbor 的 admin 密码一旦忘了管理页面进不去很多操作都停摆。最直接的应急方法是通过数据库重置密码。Harbor 2.x 的用户信息存在 PostgreSQL 的 user 表中密码字段是 bcrypt 格式。重置思路是生成一个新的 bcrypt 哈希然后更新表中对应用户的密码字段。先生成哈希python3 -c import bcrypt; print(bcrypt.hashpw(bNewAdminPassw0rd, bcrypt.gensalt(rounds12)).decode())然后进入 harbor-db 容器连接 registry 数据库更新指定用户的密码docker exec -it harbor-db psql -U postgres -d registryUPDATE user SET password 生成的bcrypt哈希 WHERE username admin;执行完之后重新登录管理页面。这是应急手段平时不建议频繁用因为操作数据库容易造成配置和缓存不一致。真正该做的是把 admin 密码放进密码管理平台并定期验证一次登录。镜像仓库不像业务系统天天登录密码很容易被忘得干干净净。5.2 磁盘被镜像吃满以后镜像仓库的磁盘占用一直是增长型问题。很多团队初期没概念等发现 /data 满了前面推镜像的报错已经积累一大片。看磁盘占用先定位最大目录du -sh /data/*Harbor 的存储目录大致分两大部分registry 容器存储 blob 的目录、数据库目录。blob 目录占大头就是各类镜像的实际层数据。Harbor 页面里也自带项目占用统计可以看到哪个项目占了最多空间。处理思路是三分法先删掉已经确定没用的项目或 tag配置 tag 保留规则比如保留最近 30 个 tag 或者保留最近 90 天内的版本执行回收Harbor 的 GC 功能会把已经不被引用的 blob 层清理掉。结合“保留规则”自动跑比手动删省心很多。2.x 版本可以在系统管理里配置清理规则定期执行。需要注意GC 本身是给仓库做瘦身的但它只能回收“没有任何 tag 引用的 blob”。如果你删了 tag但还想手动保留某个中间层GC 不会理会这种需求。所以 GC 之前先确认项目里还有没有要抢救的镜像。5.3 升级跨版本导致的数据库迁移报错Harbor 升级是相对繁琐的环节。官方升级路径通常建议小版本逐步升跳太多版本很容易遇到数据库迁移失败。升级前最重要的就是备份这是老生常谈但我每次都要再加一句备份了没恢复过等于没备份。我的升级流程是这样的先 docker compose down 停掉服务备份 /data 目录尤其是 PostgreSQL 数据目录和 redis 数据目录解压新版本离线包保留原有 harbor.yml 配置必要时先对比新旧配置差异执行 ./install.sh脚本会触发数据库迁移启动后连续访问页面重点看项目、用户、复制规则是否完整。升级失败时最常见的现象是数据库容器反复重启。这时候别急着重试 install先把日志拉出来看迁移脚本报错在哪一步。老老实实恢复备份再查迁移脚本而不是反复重试因为每次启动都有可能执行一半的迁移把数据库状态搞得更乱。5.4 同一个 tag 被覆盖之后生产拉取到新镜像镜像 tag 本身是可以被覆盖的。这是镜像仓库和代码仓库一个很大的区别代码分支可以限制被覆盖但很多团队的 CI 每天都在往同一个 lateest 上推新镜像。某次推送后生产环境重新拉取就拿到新版本如果那个版本有问题事故半径可能很大。Harbor 提供了“不可变 tag”概念可以在项目里设置规则让某些 tag 不被重复覆盖。比如你可以把 v1.0.0、v2.0.0 这类正式版本设成 immutable也可以在 project 级别禁止最新 tag 被重复推。开启之后同名 tag 推送时会被拒绝从根上防止误覆盖。更上游一点的建议是CI 流程里不要把镜像统一打成 latest 推仓库用仓库地址加 commit SHA 或者构建号做 tag保证每次构建的镜像都可以追溯。镜像一旦可以追溯很多线上问题就只是去“查一下”而不是“猜一下”。6. 从单机到高可用复制、组件替换与备份策略6.1 最省事的容灾Harbor 之间的项目复制如果你想给镜像仓库做容灾但暂时没有精力上完整高可用Harbor 自带的复制功能是成本最低的方案。原理很简单在另一台机器再部署一个 Harbor然后在两个实例之间配置复制规则。按项目配置主 Harbor 上的镜像会自动推送到备份 Harbor也可以在做容灾切换时选择按需拉取。复制规则有两种模式。主动推送是把主实例的新镜像推到备份被动拉取是备份实例从主实例定期拉取。无论哪种都只需要两套单机 Harbor 加上正确的网络连通性就能达到“主挂了备份还在”的效果。这种方案的局限也很明显如果主实例彻底挂了新镜像推不进去备份实例上的镜像还是之前复制过去的。想要两个实例都能提供服务就需要更深入的高可用方案。6.2 官方高可用模式到底拆了什么组件Harbor 本身是一组容器组成的应用核心组件包括Harbor Core对外 API 和 Web UIRegistry镜像存储与分发PostgreSQL保存用户、项目、审计等元数据Redis缓存和任务队列Jobservice执行复制、清理、扫描等后台任务。单机部署把所有组件放在一台机器。高可用部署则是把 PostgreSQL、Redis、存储这三个有状态组件外部化然后跑两个到多个 Harbor 实例前端用负载均衡把流量分到多个实例上。由于实例之间共享同一套数据库和存储用户从哪个入口进来看到的都是同一份数据。这套架构能真正解决“实例单点故障”问题但复杂度也主要集中在这几个外部组件上外部数据库要做主备或集群Redis 要做高可用存储要支持多机同时挂载。这三个问题每一个单独拿出来都是独立的运维课题。如果只是想让服务不中断刚起步的项目直接上这套反而让运维负担翻倍。我更推荐先用复制方案扛住容灾等团队里有人能把数据库和存储的高可用真正维护起来再切换过去。6.3 系统备份的四个数据面与恢复演练不管哪种部署形态备份都是底线。Harbor 需要备份的内容我习惯分成四块harbor.yml 和证书这是“恢复身份”的关键丢了等于忘了你这套 Harbor 怎么搭的PostgreSQL 数据库用户、项目、权限、复制规则等元数据Redis缓存和任务状态重要性稍低但恢复时最好也带上 checkpointRegistry 存储所有镜像 blob容量最大备份策略要单独做。数据库可以按常规方式做逻辑备份比如定时 pg_dump存储部分如果量很大全量备份成本高可以配合主备复制或者异地复制来兜底。比较现实的目标是“数据不丢”而不是每次都在本地留一个完整快照。还有一点是恢复演练。我见过团队备份脚本写得很好真到演练时发现恢复流程里数据库版本不匹配备份文件也没法用。所以每季度至少做一次恢复演练把备份包恢复到一台闲置机器上确认能拉起一套可用的 Harbor再把这套系统视作有效备份。6.4 与 CI/CD 流水线的衔接经验Harbor 部署完之后一定要尽早和 CI/CD 流水线打通否则它只是个“能打开的网站”。打通的方式不复杂核心就是让构建机器能登录、能推送、能拉取。以某个内部流水线平台为例构建阶段的核心步骤通常是docker login harbor.example.com -u build-bot -p $REGISTRY_PASSWORD docker build -t harbor.example.com/team-app/backend:$CI_COMMIT_SHA . docker push harbor.example.com/team-app/backend:$CI_COMMIT_SHA这里有三个实践建议第一在 Harbor 里单独建一个专门的机器人账号只给指定项目的推送权限别拿 admin 去跑流水线。账户泄露的损失会被限制在单个项目内。第二tag 一定带上版本标识和来源信息。如果团队已经习惯用 latest至少在部署前重新 tag 一次带 SHA 的镜像保证回滚时有据可查。第三把 docker login 用的密码放进 CI 平台的密码管理不要硬编码在流水线文件里。镜像仓库作为内部基础设施它的凭据管理越规范后面出问题的概率就越低。最后分享一个我个人的习惯每次部署完 Harbor我会把 harbor.yml、证书目录、备份脚本、恢复文档放到同一个配置仓库里并写好一份半页纸的交接说明。镜像仓库属于那种“部署完可以三个月不用碰”的基础设施但真到了要改配置、要升级、要恢复的时候最怕的不是操作不会而是当初怎么部署的、改过哪些东西完全没有记录。这份文档就是给未来那个已经忘了细节的自己准备的。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑