Harbor私有镜像仓库实战:从部署、HTTPS证书到故障排查

📅 发布时间:2026/10/20 21:14:36
Harbor私有镜像仓库实战:从部署、HTTPS证书到故障排查
不少团队走到私有镜像仓库这一步其实是环境倒逼出来的。项目从两三个服务膨胀到十几二十个镜像开始散落在各个开发机和构建机上版本一多就乱了有人推上去再也不管有人面对一堆旧 tag 不知道该拉哪个。想用官方 Docker Registry 临时顶一顶用两周就会发现它只解决了存储问题没解决协作问题。Harbor 正是在这个位置补位的——一套开源的企业级镜像仓库底层依旧是成熟的 Registry 存储引擎外面则包上了 Web 界面、项目级权限、镜像复制、漏洞扫描、审计日志这些真正让团队能一起干活的能力。这篇文章结合我自己从零部署到日常维护的完整经验把在 Linux 上搭建 Harbor 私有仓库、配好 HTTPS、跑通推送拉取以及后面遇到的三类典型故障一次讲清楚。适合准备自建镜像仓库的运维同学也适合已经装了 Harbor 但还没把证书、权限、清理这些细节理顺的开发同学。1. 为什么不用裸RegistryHarbor补上的四块短板1.1 权限管理裸Registry没有人的概念Docker 官方开源的 Registry 镜像本质上就是一套存储加 API 服务默认部署完没有任何账号体系谁能 push、谁能 pull 基本没法按人控制。你可以拿 Nginx 在前面做一层 basic auth但那是团队共用一个密码的模式日志里只看到一个账号出了事根本说不清是谁推的。更麻烦的是没法做项目隔离开发环境、测试环境、生产环境的镜像堆在同一个库里误操作成本极高。Harbor 的权限模型是推到项目维度的。一个用户可以关联多个项目在每个项目里单独分配角色项目管理员能管成员和配置开发人员能推送镜像访客只能拉取还有更严格的限制访客。实际用的时候我给测试组只开测试项目的访客权限给构建机只开对应项目的推送权限生产项目就留给两个核心同学。这套模型直接对应团队分工不用额外设计一套权限体系。1.2 删除不是真的删除镜像生命周期管理裸 Registry 支持通过 API 删除镜像但实际操作比较绕先拿到 digest再调接口而且删了之后底层存储未必立刻释放。多人反复推送同一个 tag、或者中断的推送都会在 registry 里留下大量无引用的层时间一长 /var/lib/registry 只涨不跌。Harbor 把这件事做成了界面操作。项目管理员可以设置保留策略比如保留最近 20 个 tag、只保留被某条流水线标记过的镜像到时间自动清理同时提供手动垃圾回收按钮把无主的 blob 真正从磁盘上清掉。对要长期运维的人来说这个功能比在服务器上敲一堆 curl 值钱得多。1.3 跨机房复制给镜像搬家当你有第二个机房或者想在开发环境同步一份生产镜像时裸 Registry 基本没有现成方案。Harbor 内置复制管理可以配置基于拉取模式的复制规则由目标实例按计划从源实例拉镜像而且保留项目结构、权限和签名校验。我当时把构建放在西边机房东边机房通过复制规则定时同步业务容器部署时直接从本地仓库拉速度和稳定性都好很多。这个能力不只是多机房需要从生产同步到测试、从主仓库同步到灾备仓库思路都是一样的。1.4 审计日志与漏洞扫描Harbor 的审计日志会记录谁在什么时间登录、push、pull 了哪个镜像配合 UI 的访问日志排查这个镜像到底谁推上去的这类问题非常快。它默认还提供漏洞扫描能力镜像入库时基于 CVE 库做检查扫描结果直接显示在镜像列表上。生产拉镜像前先扫一遍很多低版本基础镜像的明显漏洞能在源头拦下来。简单总结一下裸 Registry 和 Harbor 的差距维度裸 RegistryHarbor账户体系无仅基础认证完整 RBAC按项目分配角色镜像清理API 删除不自动释放空间保留策略 手动垃圾回收跨实例同步需要自己写脚本内置复制规则审计与扫描无审计日志 漏洞扫描Web 管理界面无完整 UI管理和运维都在浏览器里完成2. 动手前先定四件事版本、依赖、证书、存储2.1 版本选型与硬件底线Harbor 目前主力分支是 2.x。官方文档给的最小配置是 2 核 CPU、4G 内存、40G 磁盘但那是能跑起来的标准。我个人经验是开了漏洞扫描之后扫描任务会把 CPU 和内存顶上去尤其是第一次全量扫描的时候所以生产环境直接按 4 核 8G 起步更稳妥系统盘和数据盘也必须分开。版本选择上没有特殊要求就用当前最新稳定版拿到离线安装包。离线包的好处是整个安装过程不需要从公网拉取任何镜像所有组件镜像都打包在安装包内内网环境部署和后面的离线升级都很方便。2.2 Docker 与 Compose 依赖检查Harbor 的安装机需要 Docker 和 Docker Compose。2.x 官方要求 Docker 17.06 以上、Compose 1.18 以上实际建议直接用当前 Docker 稳定版。装完先确认版本省得后面报一些莫名其妙的兼容问题docker --version docker compose version如果机器上只有老的 docker-compose 命令没有 docker compose 插件install.sh 也能识别但注意后面所有日常管理命令要和安装时用的命令保持一致避免一会儿 docker compose、一会儿 docker-compose 把环境搞乱。2.3 域名与证书方案越早定越省事Harbor 的对外地址会写进镜像的 tag 里比如 harbor.example.com/app-a/web:v1一旦上线之后再改 hostname所有旧 tag 都要重新打一遍。所以第一步就是把规划的地址定下来别用 127.0.0.1也别拿临时的内网 IP 当正式地址。证书我建议直接走内部 CA 服务端证书的方案自己维护一个根 CA用它给 Harbor 签一张证书后面所有要拉镜像的节点只要信任这个根 CA 就行。这个方法比每台机器都配 insecure-registries 要规范、安全得多。第 4 节我会把整条生成链路完整写出来照着做就行。2.4 数据目录与备份规划harbor.yml 里的 data_volume 参数决定所有数据落在哪里。Harbor 的数据库、Redis、镜像存储、日志都在这一个目录下面默认是 /data。最怕的是把它装在系统盘上跑几个月镜像把根分区占满整个机器跟着出问题。我建议把 data_volume 指向独立数据盘的挂载点比如 /data/harbor同时在部署前就想好备份策略。后面不管是升级还是出故障这个目录就是最重要的恢复源。3. 部署全过程离线包、harbor.yml、install.sh3.1 下载解压与目录规划去 Harbor 项目官方发布页面下载最新稳定版的离线安装包文件名形如 harbor-offline-installer-v2.11.0.tgz。传到服务器后解压tar -xzvf harbor-offline-installer-v2.11.0.tgz cd harbor解压出来的目录里有 harbor.yml.tmpl 模板、install.sh、prepare、common.sh还有一个打包了全部组件镜像的压缩文件。建议把目录固定放在 /opt/harbor以后升级时解开新版本替换进来路径保持一致能减少很多环境变量上的烦恼。3.2 harbor.yml 逐项配置先复制模板再修改cp harbor.yml.tmpl harbor.yml核心配置项如下完整字段可以参考模板文件里的注释hostname: harbor.example.com http: port: 80 https: port: 443 certificate: /data/certs/harbor.example.com.crt private_key: /data/certs/harbor.example.com.key harbor_admin_password: YourStrongPassword database: password: YourDbPassword data_volume: /data/harbor trivy: enabled: true逐项说hostname 必改别留模板默认值如果纯走 HTTPS可以把 http 段整个注释掉避免 80 端口被机器上其他服务占用harbor_admin_password 是 admin 的初始密码至少 8 位不要用模板里的默认弱密码database.password 会写入内置数据库的初始化配置记好别丢trivy.enabled 打开漏洞扫描能力如果你暂时不看扫描结果可以先关掉减少资源占用。3.3 执行 install.sh 与启动校验配置文件改好之后直接执行./install.shinstall.sh 会依次做三件事加载离线镜像包调用 prepare 根据 harbor.yml 生成最终的 docker-compose.yml 和 nginx 配置最后 docker compose up -d 启动全套组件。加载离线包可能需要几分钟耐心等它跑完。启动后用下面两条命令确认状态docker compose ps docker compose logs -f看到 core、nginx、registry、数据库、Redis 这些容器都是 running 状态就可以在浏览器里访问 https://harbor.example.com用 admin 登录了。到这里 Harbor 就能正常提供镜像仓库服务但还没完后面几步才是让它真正好用的关键。4. HTTPS 证书链路私有仓库第一大坑的完整解法4.1 用 OpenSSL 搭一个内部 CA如果你已经有企业内部的 CA 平台直接跳到下一步签服务端证书就行。没有的话用 OpenSSL 建一个根 CA 成本非常低。先准备工作目录mkdir -p /data/certs cd /data/certs openssl genrsa -out ca.key 4096 openssl req -x509 -new -nodes -sha512 -days 3650 \ -subj /CNHarbor Internal CA \ -key ca.key -out ca.crt这一步会得到 ca.key 和 ca.crt。ca.key 一定要收好后续给其他服务签发证书时才用得到ca.crt 则是可以放心分发给所有节点的信任凭证。4.2 给 Harbor 签发服务端证书服务端证书有两个容易出错的地方有效期和 SAN。先生成私钥和 CSR再通过扩展文件把域名和可能用到的 IP 都写进 SANopenssl genrsa -out harbor.example.com.key 4096 openssl req -new -sha512 \ -subj /CNharbor.example.com \ -key harbor.example.com.key \ -out harbor.example.com.csr cat v3.ext EOF subjectAltName DNS:harbor.example.com, IP:192.168.100.10 EOF openssl x509 -req -sha512 -days 3650 \ -extfile v3.ext \ -CA ca.crt -CAkey ca.key -CAcreateserial \ -in harbor.example.com.csr \ -out harbor.example.com.crtCAcreateserial 会在当前目录生成 ca.srl 序列号文件不要删后面再签其他服务证书时还要用它。把生成的 crt 和 key 路径填到 harbor.yml 的 https 段证书部分就完成了。SAN 里的 IP 和 DNS 都要覆盖否则你用 IP 访问时会出现证书校验失败。4.3 让每台 Docker 守护进程信任 CAHarbor 所在机器以及所有要访问它的节点Docker 守护进程都必须信任刚才的根 CA。正规做法是建 Docker 的证书目录把 ca.crt 放进去目录名必须和访问地址完全一致mkdir -p /etc/docker/certs.d/harbor.example.com cp ca.crt /etc/docker/certs.d/harbor.example.com/ca.crt systemctl restart docker这里有个特别容易忽略的点Harbor 所在机器自己也要 push/pull所以这台机器同样要执行这一步否则你在本机登录自己的仓库照样报证书错误。分发 ca.crt 可以用一条 scp 推送或者配合配置管理工具批量下发规则就是每台需要访问的节点都得有。如果实在不想分发证书还有一个短平快的方案在 /etc/docker/daemon.json 里声明{ insecure-registries: [harbor.example.com] }insecure 方案能跑但镜像和登录信息都走明文 HTTP内网临时测试可以接受正经环境我不推荐后面会越来越难改。4.4 配置变更后必须重新 prepareHarbor 不会自动重读 harbor.yml。不管是补了证书还是改了数据库密码都要按这个顺序来一遍docker compose down ./prepare docker compose up -dprepare 会重新生成 docker-compose.yml、nginx 和各个组件的配置文件然后再用 up -d 把服务拉起来。我习惯每次变更之前先把当前 data_volume 备份一下再动文件成本很低但保险系数非常高。5. 上线后的日常项目、用户、推送、拉取5.1 项目的权限模型怎么落地登录 UI 后第一件事是建项目。项目名会成为镜像仓库路径的第一段比如建一个项目 app-a之后镜像地址就是 harbor.example.com/app-a/xxx:v1。项目分公开、私有两种公开项目默认允许所有人拉取私有项目必须登录并获得权限。用户管理里除了普通用户还有机器人账户这是给 CI/CD 用的。比如构建机上不想存管理员密码可以给一条流水线单独建一个机器人账户只给某个项目的推送权限拿到的 token 直接当登录密码用。这个功能实际用下来比把管理员密码交给流水线安全太多而且随时可以撤销。5.2 推送镜像的完整命令推送前先登录再打 tag最后推送docker login harbor.example.com docker tag myapp:latest harbor.example.com/app-a/myapp:latest docker push harbor.example.com/app-a/myapp:latesttag 的仓库名部分必须和项目对应推错项目名会直接报权限错误。docker login 的凭证默认缓存在 ~/.docker/config.json 里CI 机器上可以先手动登录一次或者把 user 和 auth 字段直接写进 config.json这样流水线不用每次弹登录交互。拉取端只要提前做过 4.3 里的 CA 信任配置直接拉就行docker pull harbor.example.com/app-a/myapp:v15.3 复制规则与垃圾回收跨机房或者多环境同步在 UI 的复制管理里配置规则。选基于 pull 模式的复制源实例填本 Harbor目标实例填对端地址然后指定具体项目。目标实例会按计划从源拉镜像权限和签名校验都保留完全不用自己写脚本。垃圾回收要单独提醒一句Harbor 不会自动执行 GC删除镜像后磁盘空间不会立刻释放。UI 的垃圾回收页面可以手动触发建议先在测试环境跑一次确认不影响业务再在生产环境排周期执行。GC 期间 registry 的写入性能会受影响尽量避开发布窗口。5.4 升级备份的基本流程Harbor 升级的套路是固定的先 docker compose down备份 data_volume解压新版本安装包把旧 harbor.yml 拷贝过去再执行 ./install.sh 完成升级。三个关键点一是备份一定要有data_volume 里的数据库目录是恢复的核心二是配置别重新手写直接用归档的旧配置改三是升级前看一眼版本变更说明组件行为有变化时好提前准备。6. 三例真实故障的排查链路6.1 x509 证书校验失败最典型的现象某台机器 docker push 时报 x509: certificate signed by unknown authority或者报 server gave HTTP response to HTTPS client。两个错误其实都不难定位。先确认地址能访问curl https://harbor.example.com/v2/ 看看返回。再检查 Docker 证书目录/etc/docker/certs.d/ 下面必须有一个和访问地址完全同名的目录目录里放着 ca.crt。改完记得 systemctl restart docker让守护进程重新加载证书。如果报的是 HTTP response说明 Harbor 这端没开 HTTPS而 Docker 默认用 HTTPS 访问要么补上 HTTPS要么明确在 daemon.json 里把该地址列为 insecure。6.2 登录界面 502 Bad GatewayHarbor 的 UI 和 API 都经过 nginx 反代到 harbor-core出现 502 基本是后面服务没起来。第一步看容器状态docker compose ps docker compose logs harbor-core我遇到过的两次 502一次是 harbor.yml 里的数据库密码和实际内置库不一致core 连不上数据库起不来另一次是磁盘写满日志组件起不来。排查顺序建议是先 df -h 排除磁盘问题再看 compose logs 里的具体报错。Harbor 的日志文件本身也在 data_volume 下面必要时直接翻 common/logs 里的 nginx 和 core 日志。6.3 磁盘占用异常与孤儿层现象是数据盘快满了但 UI 里看镜像没多少。原因一般是反复推送相同 tag、或者推送中断留下的孤儿层它们没有被 tag 引用却占着磁盘。处理手段就是触发垃圾回收。GC 之后如果空间下降不明显先看是不是还有旧 tag 引用着这些层把无用 tag 删除后再跑一次 GC。另外 registry 的存储目录是分层结构直接 du 刷目录大小会有误导以 GC 前后 df 的差异为准。我建议给数据盘配一个磁盘水位告警别等满了才处理。最后说一个自己的习惯。每次在 Harbor 上做配置变更我习惯先 docker compose down 再动文件改完验证好再起来升级前一定把 data_volume 完整 rsync 一份到别的磁盘。这套流程不能防止所有故障但至少保证凌晨两点处理环境问题的时候还有回滚的余地。Harbor 的搭建本身一两个小时就能跑通真正决定后面省不省心的是证书链路、权限设计和清理这些细节有没有一开始就做对。