作为干了几年容器化落地的人,我越来越觉得私有镜像仓库是刚需。公网仓库拉取镜像慢是一回事,更关键的是代码资产安全、版本管控和离线交付,这些都绕不开一个自建的Registry。以前大家常用官方Registry,但那个东西功能太简陋,管理界面没有、权限控制弱、还能被随意删除镜像,稍微正式一点的团队根本没法用。后来我换成了Harbor,一下踏实多了。今天这篇就从头到尾拆解一遍Harbor私有仓库的搭建流程,包括网络规划、HTTPS配置、存储后端选型、日常运维和常见坑。
1. Harbor到底是什么、为什么需要它
Harbor是某开源社区推出的云原生制品仓库项目,核心功能就是管理Docker镜像,同时也支持OCI制品、Helm Chart等。它和官方Registry最大的区别在于,Harbor自带一套完整的企业级功能:基于角色的访问控制、镜像签名与漏洞扫描、异地复制、垃圾回收、审计日志,还有一套相对友好的Web界面。真实场景中,你不需要每个人都去命令行敲Docker命令翻镜像列表,给开发、测试、运维分配不同权限,让QA只能拉取不能推送,让运维能管理项目配额,这些Harbor都能做到。
我为什么坚持用Harbor而不是直接用官方Registry加一套脚本去管理?因为省心。一开始我也写过几个Python脚本调Registry API做镜像清理,后来发现根本管不住。API文档不够完善、用户管理要做二次开发、审计日志要自己落库、并发删除还会把存储搞得乱七八糟。换到Harbor之后,这些能力基本都内置了,配置项可以持久化,Webhook能对接飞书或邮件告警,出了问题也容易排查。
从适用场景来看,Harbor适合以下情况:团队超过五个人且共用镜像仓库;有镜像版本回溯或安全审计的需求;需要离线部署和跨机房同步;希望把制品管理和CI/CD流水线做联动。你如果只是自己开发玩,机器上装个Docker Registry也够用,但凡是正经交付项目,建议直接上Harbor。它带来的管理收益远大于那点部署成本。
Harbor本身的架构并不复杂。核心组件包括核心服务、数据库(PostgreSQL)、缓存(Redis)、注册表适配层(registry)、Portal前端、JobService(负责复制、扫描等异步任务),以及可选的Trivy扫描器等。我推荐至少用一台4核8G的机器去部署,磁盘单独一块,用来放镜像数据。
2. 部署前要考虑清楚的事
2.1 网络规划与端口选择
Harbor默认监听80端口,但你要是想走HTTPS,就要把443端口留出来。我建议在一开始就规划好访问域名和IP,不要后面再改配置,因为Harbor的访问地址会和镜像仓库地址绑定,一旦推送镜像时写了域名,后面换域名或IP,所有客户端都要改配置,麻烦得很。
我自己习惯用宿主机IP加端口方式来访问,比如registry.example.internal:8443。这种用法在测试环境没毛病,但生产环境还是建议用域名加反向代理。Harbor前面对一个Nginx,把443转发到Harbor的8443端口,证书统一放在Nginx层管理。你要是让Harbor自己处理证书也完全可以,就是后续证书轮换时要多操作Harbor的配置重启。
网络规划还要考虑机房防火墙和跨网段访问。很多公司企业内部有多个网段,开发网、测试网、生产网是隔离的,你要提前确认需要访问Harbor的网段都放通了,端口方面TCP的80、443是基础,如果开了复制功能还得看各机房之间能不能互通。另外提醒一句,Harbor和K8s集群的交互流量不低,尽量别把仓库放在跨公网的链路上,否则拉取镜像延迟会高得离谱。
2.2 存储后端的选型
Harbor的镜像数据默认存放在宿主机的/data/docker/registry目录下(具体看data_volume配置)。这块存储怎么选,直接影响仓库的可靠性和可用性。单机环境我直接挂一块独立的云盘或物理磁盘,不做任何叠加层,最稳。如果你用NFS或Ceph网络存储,要特别留意I/O延迟和锁冲突,多个Harbor实例共享同一份存储时,在某些并发写场景下会出现目录互操作异常。
大文件在分布式存储上的性能表现往往不如本地盘,尤其是镜像层多且大时。如果能接受单点风险,本地磁盘是最省心的选择。如果非要高可用,我更建议做双Harbor实例加主从复制,而不是两个实例挂同一份共享存储。主从复制写起来简单、逻辑清晰,还能顺便做跨机房灾备。
数据库方面,Harbor默认使用PostgreSQL,数据放在/data/database目录。小规模使用不需要怎么操心,但如果镜像量和用户量大,建议定期备份这个目录,或者干脆把数据库外置到托管数据库服务上。镜像元数据丢了还能通过重新推送恢复,但用户权限、项目配置、复制规则丢了,重新配置一遍够你喝一壶的。
2.3 版本选择与兼容性
Harbor的各个版本差异不小,安装前一定先看Release Notes。比如较新的版本会调整配置项名称、默认启用Trivy扫描、改变Harbor.yml的结构,你要是直接拿旧教程的配置去填,部署时有可能报错。
提醒一个最容易翻车的点:老版本Harbor使用的docker-compose文件版本和新版本不一定兼容。有时候升级Harbor之后,依赖的镜像tag变了,但docker-compose文件没对,导致起不来。建议部署前看清楚Harbor对应要求的最低Docker和Docker Compose版本。我这里建议Docker 20.10以上,Docker Compose 2.x以上。Docker Compose 1.x也不能说完全不行,但功能上差一些,且新版Harbor的compose文件语法它可能不认识。
另外提醒一点:某些Linux发行版自带的docker-compose是很老的版本,比如Ubuntu 20.04自带的1.25,Harbor要求2.0以上,直接装可能遇到Compose文件格式解析失败。这里统一推荐单独安装docker-compose-plugin或者直接用docker compose子命令。
3. 环境准备:把地基打好
Harbor本体依赖Docker和Docker Compose,这部分环境如果没弄好,后面全是坑。先说我的实际操作环境:一台私有的CentOS 7.9虚拟机,4C8G,一块200G数据盘挂在/data,系统盘和数据盘分开,避免镜像数据把系统盘撑爆。
安装基础工具链时,我习惯先换好系统源再装软件包。CentOS上修改/etc/yum.repos.d/下的源文件,Ubuntu则是/etc/apt/sources.list。这一步不用多说,但现实是很多人的安装卡在拉取依赖包阶段,花在等下载的工夫比部署本身还长。如果公司有内网源,直接用内网源,那才是最快的。
Docker安装我走官方脚本或包管理器都试过。建议保留包管理器方式,方便后续指定版本升级:
# CentOS yum install -y yum-utils yum-config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo yum install -y docker-ce docker-ce-cli containerd.io systemctl enable --now docker安装完确认一下版本:
docker --version docker compose version我碰到过装了docker-ce后没有docker compose子命令的情况,因为新版Docker把Compose拆成了独立插件。你需要单独装docker-compose-plugin:
yum install -y docker-compose-pluginDocker配置方面,有几个参数建议改一下。第一是>{ "data-root": "/data/docker", "exec-opts": ["native.cgroupdriver=systemd"], "log-driver": "json-file", "log-opts": { "max-size": "100m", "max-file": "5" }, "registry-mirrors": ["https://docker.mirrors.internal"] }
注意:
registry-mirrors是给docker pull用的公共镜像加速配置,和Harbor推送无关。你要是公司有内网镜像代理,填内网地址;没有的话,可以留空或填可用的公网加速地址。
改完配置重启Docker:
systemctl daemon-reload systemctl restart docker验证一下Docker能正常跑:
docker run hello-world拉不下来hello-world也不用慌,本地docker version能正常显示Client和Server就行。另外,千万别忘了检查时间同步。容器镜像的tag验证和日志审计都依赖时间,机器时间飘了,轻则证书校验失败,重则Garbage Collection把还在用的镜像层清了。装好chrony或者ntpdate,保持和NTP服务器同步。
4. Harbor安装包下载与配置文件修改
4.1 选择合适的安装包
到Harbor的GitHub Release页面下载对应版本的离线安装包。在线安装包体积小一些,但安装时还要从公网拉取大量镜像,离线包更省事。我一般直接选离线包,例如harbor-offline-installer-v2.11.0.tgz。下载完先校验SHA256,避免文件损坏或下载不完整。
解压之后会得到一个harbor/目录,里面有install.sh、harbor.yml.tmpl、common.sh和一堆预打包的镜像tar包。部署Harbor必要的内容基本都在这里,不要手动去删解压出来的tar文件,install.sh脚本在加载镜像时需要找到它们。
4.2 harbor.yml关键配置项解读
先复制模板:
cp harbor/harbor.yml.tmpl harbor/harbor.yml然后逐项修改。重点配置项我列一个表说清楚:
| 配置项 | 作用 | 我常用的值 | 注意点 |
|---|---|---|---|
| hostname | 仓库访问地址 | 内网域名或IP | 客户端要能解析或直接访问到 |
| http.port | HTTP端口 | 80 | 要改的话同时注意端口占用 |
| https.port | HTTPS端口 | 443 | 如果走TLS,证书路径要配好 |
| harbor_admin_password | 管理员初始密码 | 强随机字符串 | 至少要12位,部署完尽快改 |
| data_volume | 镜像数据目录 | /data/harbor | 确保目录存在且空间足够 |
| database.password | 数据库密码 | 强随机字符串 | 不要用默认值,安全第一 |
| trivy.enabled | 漏洞扫描开关 | true | 开启后占资源,但安全收益大 |
hostname这一项,不是随便填一个域名就完事。客户端机器上配置Docker仓库时,依赖hostname做TLS证书校验和仓库识别。万一hostname写成了IP,后续想换域名,就得重新生成证书、改配置、重启Harbor,所有镜像tag里的仓库地址可能也要跟着改。建议一开始就定一个稳定的内网域名。
harbor_admin_password和database.password这两处如果不修改,Harbor启动后会生成随机密码并打印到日志里,可它只出现一次,你要是错过了还得重置,反而更麻烦。稳妥做法:在harbor.yml里直接写死强密码,部署完成后存到密码管理库里。
data_volume强烈建议换成独立数据盘的路径,不要在/根目录下。镜像文件占磁盘空间是逐渐涨的,一旦把根分区写满,系统直接卡死,那真是欲哭无泪。先创建目录:
mkdir -p /data/harbor检查磁盘空间:
df -h /data4.3 HTTPS证书配置
生产环境一定要开启HTTPS。Docker客户端和Harbor之间的通信如果走明文HTTP,镜像内容在网络上裸奔,这个风险太高了,无论从安全合规还是从数据完整性的角度都不能接受。
如果你的域名通过正式CA签发证书,直接把fullchain和私钥填进去就行。自己内部用的,更多是自签证书。我来走一遍完整的自签证书生成流程。
先生成CA私钥和证书:
mkdir -p /data/certs && cd /data/certs openssl genrsa -out ca.key 4096 openssl req -x509 -new -nodes -sha256 -days 3650 \ -subj "/CN=Harbor Internal CA" \ -key ca.key -out ca.crt生成仓库域名私钥:
openssl genrsa -out harbor.key 2048创建证书签名请求配置文件。这里要注意把域名和IP都写进subjectAltName,否则客户端通过IP访问时TLS校验会失败:
cat > harbor.csr.cnf <<'EOF' [ req ] distinguished_name = dn req_extensions = ext prompt = no [ dn ] CN = registry.example.internal [ ext ] subjectAltName = @alt_names [ alt_names ] DNS.1 = registry.example.internal IP.1 = 192.168.10.20 EOF生成CSR并签发证书:
openssl req -new -key harbor.key -out harbor.csr -config harbor.csr.cnf openssl x509 -req -in harbor.csr -CA ca.crt -CAkey ca.key -CAcreateserial \ -out harbor.crt -days 825 -sha256 -extensions ext -extfile harbor.csr.cnf将证书和私钥放到Harbor能读取的位置,并在harbor.yml中配置:
https: port: 443 certificate: /data/certs/harbor.crt private_key: /data/certs/harbor.key提示:自签CA的情况下,每个需要访问Harbor的客户端机器,都要把ca.crt加到Docker的信任列表里。路径一般是
/etc/docker/certs.d/<hostname>/ca.crt。这一步漏了,直接后果就是pull/push时报x509 certificate signed by unknown authority。
5. 安装启动Harbor
5.1 执行安装脚本
配置改完后,运行安装脚本:
cd /path/to/harbor ./install.sh --with-trivy--with-trivy就是开启漏洞扫描功能。如果不需要扫描,可以不加这个参数,能省不少内存。Trivy扫描器在Harbor中是按需启动的,真正扫码时才会跑大量任务,平时处于待命状态,所以也不用太担心空占资源。
install.sh会做这几件事:首先加载离线包中的镜像tar包到Docker中;然后根据harbor.yml生成docker-compose.yml;最后通过docker compose把Harbor的各个组件拉起来。整个过程中,如果看到✔ ----Harbor has been installed and started successfully----,说明成功了。
5.2 验证Harbor各组件状态
安装完成后,用docker compose查看服务状态:
cd /path/to/harbor docker compose ps正常情况下,你会看到这些容器处于运行状态:
| 容器名 | 职责 |
|---|---|
| harbor-core | 核心业务逻辑,API处理 |
| harbor-db | PostgreSQL数据库 |
| harbor-jobservice | 异步任务(复制、GC等) |
| harbor-portal | Web前端 |
| harbor-redis | 缓存 |
| harbor-registry | 镜像存储与上传下载 |
| harbor-trivy | 漏洞扫描器 |
| nginx | 入口反向代理 |
有一个非常容易踩的坑:Harbor容器全都起来了,但浏览器访问页面是502。这种基本都是Harbor内部网络组件启动顺序问题,或者磁盘空间不够导致某些容器启动失败。先看nginx容器日志,再看harbor-core日志,基本就能定位。
5.3 浏览器与Docker客户端联通测试
浏览器访问https://registry.example.internal,用admin账号和harbor_admin_password登录。第一次登录之后,我建议立刻创建一个普通管理员或开发者账号,日常业务别用admin,减少误操作风险。
Docker客户端配置信任自签证书。每台客户端机器上执行:
mkdir -p /etc/docker/certs.d/registry.example.internal cp /data/certs/ca.crt /etc/docker/certs.d/registry.example.internal/ca.crt systemctl restart docker不要直接往/etc/docker/certs.d/丢Harbor的证书,要放在以hostname命名的子目录下。Docker找证书的逻辑就是按hostname匹配目录,目录名不对照样不认。
登录Harbor:
docker login registry.example.internal输入用户名密码,显示Login Succeeded就通了。接下来打一个标签推送测试:
docker tag nginx:latest registry.example.internal/library/nginx:1.25 docker push registry.example.internal/library/nginx:1.25登录Web界面,到library项目下就能看到这个nginx镜像。这一套通了,说明Harbor部署基本没问题。
6. 配置与运维的一些实际操作
6.1 镜像复制规则和跨机房同步
在Web界面的“复制管理”里可以创建复制规则。我配置过一个从生产Harbor复制到备份Harbor的规则,走的是内网专线,同步大量镜像时很稳定。复制方式有push和pull两种。push是源Harbor推送到目标,pull是目标从源拉取,我个人更推荐pull模式:备份端主动去拉,源端不用开启外部访问,安全上更可控。
创建复制规则时要填目标仓库的地址、用户名密码,以及同步的资源和过滤条件。比如只同步含latest标签的镜像,或者只同步nginx、redis这些基础组件,都可以写正则过滤。JobService会定期执行这些复制任务,即使某次失败,也会有重试和日志记录。
6.2 垃圾回收(GC)
镜像被删除后,底层的镜像层不会立即清除,需要手动触发垃圾回收。Harbor的GC功能在“系统管理 -> 垃圾回收”里。执行GC前注意,它会暂时停止对某些镜像清单的引用操作,如果此刻正在拉取大镜像,理论上可能报错。我一般选择在凌晨低峰期执行。
GC完成后,查看磁盘空间是否释放:
df -h /data/harbor6.3 镜像漏洞扫描
开启Trivy扫描后,你可以在项目的“扫描”标签下手动触发扫描,也可以设置策略让Harbor在镜像推送后自动扫描。自动扫描的配置在项目设置里,在Automatically scan images on push前打勾。扫描结果会标记漏洞等级,包括严重、高危、中危、低危,并展示漏洞详情和CVE编号。
实际体验中,Trivy的扫描比较快,但首次扫描大镜像时需要拉取漏洞数据库,耗时会偏长。如果离线环境无法更新漏洞库,扫描效果会大打折扣,这一点要心中有数。有安全合规要求的话,漏洞库的离线同步机制务必提前搭建。
6.4 用户、项目与权限管理
Harbor的项目分为公开和私有两种。公开项目任何人登录后都可以拉取镜像,适合存放基础镜像或公共制品;私有项目只允许被授权用户访问。生产环境我基本都用私有项目,按团队划分权限。
权限模型里有几个角色:项目管理员、开发者、访客,以及系统级别的管理员。开发者可以推送镜像,访客只能拉取,项目管理员还能管理成员和策略。我通常把CI/CD机器要使用的账号配成开发者,只赋予指定项目的权限,避免它获取整个Harbor的管理权限。
有一个细节:Harbor3.0以后新增了机器人账号,专门给CI/CD系统使用。机器人账号没有密码交互,用令牌方式认证,可以做成自动定期轮换,权限隔离做得好,审计日志也干净。如果还在用普通账号挂到CI系统里,建议尽快迁移到机器人账号。
7. 常见问题和故障排查实录
7.1 镜像push时报x509证书错误
这个问题八成是Docker客户端不信任你的自签CA。检查几处:
ls /etc/docker/certs.d/registry.example.internal/目录里必须有ca.crt,而且这个文件得是CA证书本身,不是Harbor的服务器证书。确认无误后重启Docker:
systemctl restart docker再次执行docker login测试。还有一种少见情况:某些Docker版本会用系统CA库忽略/etc/docker/certs.d下的配置。这时把ca.crt也拷贝到系统CA目录下:
cp /data/certs/ca.crt /usr/local/share/ca-certificates/harbor-ca.crt update-ca-certificates7.2 端口被占用导致启动失败
harbor.yml里http.port和https.port如果有冲突,nginx容器会起不来。优先排查占用情况:
netstat -tlnp | grep -E '80|443'如果80端口已经被其他服务占了,你可以只启用HTTPS,把http.port注释掉,或改到别的端口。注意DNS、防火墙都要同步调整。
7.3 磁盘空间不足导致Harbor服务假死
这个坑我踩过不止一次。Harbor依赖的组件多,日志文件也多,如果/和/data的分区都达到90%以上,容器时不时就会OOM或无响应。先快速定位:
df -h再看哪些目录吃空间:
du -sh /data/harbor/* | sort -rh | head清理大文件后,记得顺手清理Docker日志。日志文件路径通常是/var/lib/docker/containers/<container_id>/*-json.log,被日志撑爆的场景我见过太多了。如果已经设置了log-opts参数轮转,就不用太担心。
7.4 删除镜像后磁盘空间没释放
Harbor的机制是镜像被delete后,需要GC才能真正删除存储层。去系统管理页签开启并执行垃圾回收。如果你用的是分布式存储,GC是在线执行的,但数据层的碎片回收依赖底层存储的实现,有时确实需要等一会儿。
7.5 Harbor升级时的坑
升级前先备份harbor.yml和/data/database目录。用新版离线包覆盖旧版时,install.sh会自动做数据库迁移,但迁移失败也有概率。我后来都是先用一台测试机跑一遍升级流程,确认无异常再上生产,这个经验特别重要,不想线上翻车就别省这一步。
7.6 内网DNS解析导致docker login超时
有些机器能访问HarborIP,但docker login非常慢。我先用手动解析测试:
curl -v https://registry.example.internal/v2/如果curl很慢,检查/etc/resolv.conf中的DNS配置,或者为Harbor域名指定本地DNS记录。实在不行就加--add-host参数临时绕过,但生产环境还是得把DNS配好。
8. 一点个人实操总结
Harbor部署本身不复杂,真正花时间的是前期规划:存储放哪、证书怎么发、权限怎么设、备份怎么做。这些事如果在安装之前想清楚,后面会非常省心;反过来,要是装完再改,每一次变更都牵动一堆客户端配置。
我后来在实际项目里直接把Harbor配合K8s使用,每个命名空间对应Harbor里的一个项目,通过imagePullSecret来认证。这样跨团队协作时权限边界很清晰,哪个团队只能拉取自己项目的镜像,一目了然。再配合Webhook通知到内部IM,推送镜像时很快就能反馈到团队群里。
如果你正在从零开始搭Harbor,建议先按本地虚拟机走一遍完整流程,了解安装脚本、目录结构和配置项背后的关系,然后一次到位部署到生产机器。不要一上来就加一堆高级功能,先把基础走通,再逐项开启复制、扫描、配额这些能力,出问题时排查面会小很多。