部署一个私有镜像仓库这种事,我印象里每次都有人卡在同一批坑上。这次我用 docker-compose 在云主机上完整部署了最新版 Harbor v2.3.4,从离线安装包下载、自签名证书生成、harbor.yml 配置,到 prepare 脚本、docker-compose up -d 全流程走完,中间还解锁了不少网上好多人懒得写清楚的细节。这篇就是把这次实操经历完整记录下来,尤其是官方文档语焉不详、但实际部署又绕不开的部分,我尽量用大白话讲透。你手头有 Linux 服务器、会基础 docker 命令就能跟上;要是完全没接触过 Harbor,第二节的配置拆解也能帮你少搜半天资料。
1. 部署前先理清的问题
1.1 为什么用 docker-compose 而不是 Helm 或其他方式
Harbor 官方给出的安装方式其实有好几种:在线安装包、离线安装包、Helm Chart,还有基于 Operator 的方式。标题既然说 docker-compose,那重点就落在官方前两种安装包上,因为这两类安装包底层就是靠 docker-compose 编排的。
有同学可能会问,现在 K8s 都普及成这样了,为什么还要用 docker-compose 装 Harbor?我的看法是:对于中小团队、单机或少量节点这种最常出现的使用场景,docker-compose 方案是代价最小的。它不需要维护一整套 K8s 集群,故障排查链路也短——容器起没起,docker-compose ps 一眼就能看出来;哪个服务挂了,docker-compose logs 直接追日志。Helm 方案虽然更适合已经被 K8s 托管的场景,但引入的 values 配置、存储卷、网络策略这些复杂度,对只想快速落地一个镜像仓库的团队来说完全没必要。
这里要特别注意一个细节:docker-compose 这个命令本身分两个时代。老一代是独立安装的 python 写的 docker-compose 二进制,新一代是 docker 官方插件 docker compose(中间没有横杠)。Harbor 官方安装脚本在 v2.3.4 这个版本里依旧用的是带横杠的 docker-compose 命令。如果你装的是新版插件,要么补一个软链接别名,要么直接用老版本二进制。这一步没搞对,后面 install.sh 一执行就报 command not found,很多人在这里就卡住了。
1.2 v2.3.4 这个版本有什么值得注意的
很多人一看到“最新版”三个字,会默认选市场上数字最大的版本号。但实际在企业环境里,版本选择更看重的是稳定性和团队熟悉程度。v2.3.4 属于 2.x 系列里比较扎实的一个小版本,它的底层架构和之后 2.4、2.5 没有颠覆性差异,而 2.3 这个系列有几个标志性能力:
- 默认漏洞扫描组件从 Clair 切换成了 Trivy,扫描速度比之前快不少
- 支持 Proxy Cache 功能,拉公共镜像的时候可以先经过 Harbor 缓存一下,内网重复拉取就不走外网了
- 机器人账号可以做项目级和系统级两种维度,CI/CD 场景下权限隔离更灵活
- API 全面走 v2.0,脚本自动化对接没障碍
对于标题这个任务来说,只要是 2.3.4 这个版本,后续涉及到的配置文件字段、环境变量和目录结构基本是同一套,照着这篇文章操作即可。
我个人的建议是:不需要为了追新版本而追新版本。把 v2.3.4 跑稳,理解清楚它的配置逻辑,将来再升级到更高版本,迁移路径也是顺的。真正等你需要新版本功能的时候,再花半小时看 release notes 升级也不晚。
1.3 服务器资源、端口和数据目录的规划
Harbor 这套东西看着就是几个容器,但实际跑起来比你想象中要“肥”。官方给的最低要求是 2GB 内存、10GB 磁盘空间,我用下来的真实体感是:2GB 内存只能算是能启动,一旦有镜像推送、漏洞扫描任务同时跑,内存很容易顶到 90% 以上。我的建议是 4GB 起步,8GB 会比较舒服,这还没算上数据库连接数和 Trivy 扫描器的临时占用。
磁盘方面,镜像数据增长速度完全取决于你的使用频率,开发环境可能一个月几个 GB,生产环境一天几十 GB 也不稀奇。所以数据盘至少 50GB 起步,并且强烈建议把 data_volume 单独挂到非系统盘上。系统盘和数据盘放在一起,迟早会出现“镜像把系统盘塞满,docker daemon 直接罢工”的事故。
端口规划主要是三块:
| 端口 | 用途 | 说明 |
|---|---|---|
| 80 | HTTP 访问 | 默认提供,可改 |
| 443 | HTTPS 访问 | 默认提供,可改 |
| 4443 | Notary 签名组件 | 云服务器安全组默认不放开,需要手动加规则 |
这三个端口如果已经被宿主机上的 Nginx、Apache 或其他容器占用,就会出大问题,具体排错我在第四节展开。另外提醒一句,Harbor 各组件之间依赖时间和令牌校验,服务器 NTP 时钟同步一定要配好,否则你会发现登录时好时坏、拉取镜像偶尔 401,排查半天最后发现是时钟漂移。
2. 安装包获取与配置文件逐项拆解
2.1 在线包和离线包怎么选
Harbor 官方发布包分两种:在线安装包 harbor-online-installer 和离线安装包 harbor-offline-installer。相同版本下,离线包体积要大得多,因为里面预先打包好了所有组件的镜像,比如 harbor-core、harbor-registry、harbor-jobservice、trivy-adapter、nginx 等等全都在 harbor.v2.3.4.tar.gz 这个文件里。
选哪个?我这次用的是离线包。原因很简单:在线包安装的时候 install.sh 里要现场从网络拉取镜像,一旦网络波动或者超时,安装过程就断在半路上。离线包下载下来之后,内网环境也能装,整个过程不依赖外网连通性。下载完记得校验一下压缩包完整性,常见做法是对一下官方发布的 sha256 校验值,别嫌这一步麻烦,压缩包损坏导致的玄学报错比想象中多。
解压之后会得到一个 harbor 目录,里面的东西大致是:
harbor/ ├── harbor.yml.tmpl # 配置模板,必须复制成 harbor.yml 才能用 ├── install.sh # 一键安装脚本 ├── prepare # 配置生成脚本,核心 ├── common.sh # 公共函数库 ├── docker-compose.yml # compose 编排文件 └── harbor.v2.3.4.tar.gz # 离线镜像包install.sh 本质上就是先 load 离线镜像,然后调用 prepare 生成配置,最后 docker-compose up -d。理解这个执行链条以后,你以后手排问题会轻松很多。
2.2 harbor.yml 关键配置逐行讲
第一次部署,先把模板复制成正式配置:
cd harbor cp harbor.yml.tmpl harbor.yml vi harbor.yml配置文件里几个必须关注的项我拆开讲:
- hostname:这个值写你的域名或 IP,客户端访问 Harbor 的地址就是它。生产环境强烈建议用域名,比如 registry.example.com,而不是裸 IP。后面证书、客户端配置、复制目标配置全都以这个 hostname 为基准,这会带来 ip 直连想象不到的便利。
- http.port 和 https.port:默认 80 和 443。如果宿主机上已经有进程占用,可以改成 18080 和 18443,但改完后意味着客户端访问、docker login 都要带上对应端口,尽量保持默认。
- https.certificate 和 https.private_key:指向证书和私钥的绝对路径,证书生成过程在下小节讲。
- data_volume:默认是 /data,这个就是所有容器的挂载卷根目录。我建议改成 /data/harbor,避免和其他应用共用目录造成混乱。镜像数据、数据库文件、redis 数据全在这里,备份时重点关注这个目录。
- harbor_admin_password:初始化 admin 用户的密码。v2.3 开始对密码复杂度有要求,太短会在启动阶段直接报错,别图省事设个 123456 就完了。
- database.password:PostgreSQL 的密码,生产环境必改,别用默认值。
- trivy 相关配置有两个值值得留意:ignore_unfixed 表示是否只显示已修复漏洞,skip_update 表示是否跳过漏洞库更新。离线环境建议把 skip_update 设为 true,否则 Trivy 容器会尝试从网络下载漏洞数据库,失败后扫描任务会一直 Pending。
配置文件里还有 chart、jobservice、notification 等段落,正常部署保持默认即可,不需要过度配置。把上面几个关键项填对,这个文件就算过了 80%。
2.3 证书生成这一步别偷懒
HTTPS 证书有两种路线:正规 CA 签发的证书,以及内网自签名证书。公网开放使用,我建议直接买或者申请可信 CA 证书。纯内网、测试环境,自签名足够。这次我就用自签名走一遍。
自签名有一个标准操作流程,先用 CA 私钥造一个 CA 根证书,再用 CA 给服务器签一张证书。很多教程只给一个命令生成证书,但那种不带 Subject Alternative Name 的证书在现在的浏览器里直接被标记为不安全。OpenSSL 命令如下:
# 1. 生成 CA 私钥和根证书 openssl genrsa -out ca.key 4096 openssl req -x509 -new -nodes -sha512 -days 3650 \ -subj "/CN=registry.example.com" \ -key ca.key -out ca.crt # 2. 生成服务器私钥和证书签名请求 openssl genrsa -out server.key 4096 openssl req -new -sha256 -key server.key -out server.csr \ -subj "/CN=registry.example.com" # 3. 用 CA 签发服务器证书 openssl x509 -req -sha512 -days 3650 \ -extfile v3.ext \ -CA ca.crt -CAkey ca.key -CAcreateserial \ -in server.csr -out server.crtv3.ext 文件内容如下,IP 字段按需保留,域名建议放 DNS 里:
subjectAltName = @alt_names [alt_names] DNS.1 = registry.example.com IP.1 = 192.168.1.10签发完成之后,把 ca.crt、server.crt、server.key 放到一个固定目录,比如 /data/harbor/cert,然后在 harbor.yml 里对应指向。证书文件权限也要注意,server.key 权限建议 400,server.crt 权限 644,权限不对会导致 nginx 容器启动失败。自签名方案下,ca.crt 必须永久保留,因为之后每个要访问 Harbor 的客户端都得信任它。
3. 完整部署流程实录
3.1 prepare 阶段容易踩的三个报错
很多教程上来就让你跑 install.sh,但对于排错来说,手动拆开执行反而更清楚。先把离线镜像导进去:
docker load -i harbor.v2.3.4.tar.gz然后执行 prepare 脚本:
./prepareprepare 这个脚本是 Python 写的,它读取 harbor.yml,生成各组件运行所需的配置目录和 docker-compose.yml 最终版本。这个环节最常见的三个报错:
第一,Python 环境不对。prepare 脚本需要 Python 3 和 PyYAML 模块。很多 CentOS 7 默认 Python 版本还是 2,运行时报语法错误或者 ModuleNotFoundError。处理办法是装好 Python 3,再用 pip 安装 pyyaml,之后重新执行 prepare。注意 prepare 不会自动检测 Python 版本,报错信息有时候非常隐晦,看到类似 ModuleNotFoundError: No module named 'yaml' 的提示,基本就是它了。
第二,配置校验不通过。harbor.yml 里 hostname 留成了 localhost 或者填了带下划线的域名,prepare 会直接报 invalid config,启动流程中断。趁早把所有配置项填规范,免得来回试。
第三,端口被占用。这时候 prepare 一般能过,但在后面 docker-compose up 时才会暴露,我会在下节一起说。
3.2 docker-compose up -d 执行与启动验证
配置生成完毕,正式启动:
docker-compose up -d第一次启动会创建并拉起十几个容器,主要包括 harbor-core、harbor-portal、harbor-registry、harbor-db、harbor-jobservice、harbor-log、harbor-redis、nginx、trivy-adapter 等。启动时间取决于机器性能,一般一到三分钟。
启动完先别急着访问,确认所有容器状态是 Up:
docker-compose ps如果某个容器一直是 Restarting,大概率是下面几个原因:
端口占用是最常见的。harbor 的 nginx 容器要监听宿主机 80/443,如果这台机器已经装了 Nginx、Apache,或者某容器占用着端口,docker 会报 driver failed programming external connectivity on endpoint,底层错误写着 bind: address already in use。处理办法:先把占用端口的进程停掉,或者改 harbor.yml 端口后重新执行 prepare,注意改完之后必须重新 prepare 一次,直接 docker-compose up 不会生效。
第二种情况是目录权限。data_volume 目录如果不允许容器内用户写入,容器会反复启动失败。把数据目录属主改成当前用户,或者用 chmod / chown 调整权限后再 up。
容器都稳定 Up 之后,浏览器访问 https://registry.example.com,首次访问会出现自签名证书告警,点“继续前往”即可。如果连页面都打不开,检查云安全组和防火墙规则,放行 443(还有 80)入方向。
登录页面出来后,用 harbor.yml 里配置的 admin 密码登录。登录成功不代表部署完全没问题,真正的验证还要看镜像 push / pull 能不能跑通,这是第四节的重点。
3.3 把自签名证书分发到客户端
部署完成后第一件让人头疼的事就是 docker login 直接失败,报错信息一般是:
x509: certificate signed by unknown authority原因很简单:docker 客户端不认识你这套自签名 CA。正确做法是把 CA 证书分发到每个需要访问 Harbor 的机器上,并放到 docker 的证书目录里。
Linux 客户端做法:
sudo mkdir -p /etc/docker/certs.d/registry.example.com sudo cp ca.crt /etc/docker/certs.d/registry.example.com/ca.crt sudo systemctl restart docker路径里的 registry.example.com 必须和 harbor.yml 的 hostname 完全一致,否则不生效。然后执行:
docker login registry.example.com如果公司内网没有 DNS 解析,还要在客户机 /etc/hosts 里加一行记录:
192.168.1.10 registry.example.commacOS 和 Windows 的 Docker Desktop 处理方式略有不同:可以直接把 ca.crt 导入系统钥匙串或受信任根证书颁发机构,也可以在 Docker Desktop 设置里把 registry.example.com 加进 insecure-registries 列表。但 insecure-registries 本质是关闭证书校验,有中间人风险,不建议多团队生产环境用。还有一个容易被忽略的点:配置文件里的 hostname、证书里的 CN/SAN、客户端访问的域名、docker 证书目录的文件夹名,这四个必须完全一致,否则即使你信任了证书,docker 也会报证书与主机名不匹配。
4. 部署完成后的功能校验
4.1 先跑通 push / pull 全流程再谈别的
容器仓库如果不能 push / pull,就算 UI 登录成功也是白搭。完整验证流程如下:
先在 Harbor 网页上创建一个项目,比如叫 test,访问级别先选私有。然后在本机给镜像打个标签:
docker tag nginx:alpine registry.example.com/test/nginx:alpine docker push registry.example.com/test/nginx:alpine第一次 push 需要先 docker login。登录成功后 push 应该正常完成。到 UI 的 test 项目仓库页面里,能看到 nginx 这个镜像和对应标签,就说明核心链路通了。
接着验证拉取:
docker rmi registry.example.com/test/nginx:alpine docker pull registry.example.com/test/nginx:alpine如果手头有一台全新客户端,建议在另一台机器上重复一次 login、pull 流程,这样才能确认证书分发的完整性,而不是恰好本地运气好。
我建议在这个阶段顺带验证一下 Trivy 漏洞扫描:在项目里给刚才推送的镜像发起扫描,如果一直处于 Pending 状态,多半就是漏洞库更新失败。离线环境把 harbor.yml 里 trivy 的 skip_update 改成 true 再重新 prepare,能暂时绕过去。联网环境则可以等它自动下载,首次下载漏洞数据库耗时比较长,不用慌。
4.2 用户体系、项目隔离和机器人账号配置
部署完第一件事,不要急着把 admin 密码发给所有人。多个人共享一个 admin 账号,后面查操作记录、权限回收都是灾难。正确定级是:创建几个真实用户,给不同人分配项目维度角色。Harbor 项目角色分为项目管理员、开发者、访客等,开发者角色能 push / pull 私有项目镜像,访客只能 pull。
对 CI/CD 这种机器身份,正确的做法是创建机器人账号。v2.3.4 的机器人账号可以精细控制权限到项目级别,比如给测试流水线创建一个只能推送到 test 项目、不能删除镜像的机器人账号。这样就算 Jenkins 机器被攻破,波及范围也只有一个项目,而不是整个仓库。这个习惯越早养成越好,因为后期镜像多了之后再回头补权限,工作量大到你根本不想动。
如果团队里多个项目都依赖同一个公共基础镜像,v2.3.4 的 Proxy Cache 功能很好用。在目标项目里配置一个代理缓存指向 Docker Hub,拉取 nginx 这种公共镜像时先走 Harbor,缓存到本地,后面再有机器拉取就直接内网命中,省外网流量,速度也快很多。
4.3 日常巡检和日志查看
Harbor 跑起来之后,日常巡检不用太复杂,我固定就做四件事。
第一,看容器状态。docker-compose ps 一眼扫过去,确认没有 Restarting 容器。第二,看磁盘水位。镜像仓库这玩意别的毛病没有,就是吃磁盘,用 df -h 盯住 data_volume 所在分区。第三,看关键容器日志,主要看 core、registry 和 nginx 这几个:
docker-compose logs -f harbor-core docker-compose logs -f nginx日志里有大量 401 / 403 就要警惕,可能是凭证泄露或者配置错误。第四,定期执行垃圾回收。Harbor UI 里删除镜像后,底层存储不会立刻释放,需要在“垃圾回收”功能里执行清收。这个操作会暂时锁住仓库,建议放在低峰期,别在团队正忙着发版的时候点。
5. 备份、升级与回滚的实操经验
5.1 备份策略的侧重点
很多人的备份策略是拷贝一下 data_volume 目录就完事,这个做法不算错,但不够严谨。Harbor 持久化数据分两类:一类是镜像数据,即 registry 存储里的 blob;另一类是元数据,包括用户、项目、标签、复制规则等,这些都在 PostgreSQL 里。稳妥的备份建议分两层:
第一层,对数据卷做快照或者 rsync 同步,保证镜像数据有副本。第二层,单独对数据库做逻辑备份:
docker exec harbor-db pg_dump -U postgres -F c -b -f /tmp/registry_backup.dump harbor docker exec harbor-db pg_dump -U postgres -F c -b -f /tmp/postgres_backup.dump postgres把生成的 dump 文件拷贝出服务器保存。数据库备份频率至少一天一次,镜像数据不用每天全量备份,用快照或 rsync 增量即可。
恢复流程一定要演练过才叫备份,很多人备份了三年从没恢复过,真出事时才发现备份文件已经损坏。恢复的大致路线是:部署一个同版本的新 Harbor,停掉容器,把数据卷和 dump 文件放回原位,再启动服务。别嫌麻烦,至少完整演练一次。
5.2 小版本升级要做什么
Harbor 从 2.3.3 升到 2.3.4 这种小版本,操作相对轻量,但两条铁律不能破:先备份数据库,再看官方 release notes 确认升级路径。升级前把整个服务停掉:
docker-compose down然后备份 /data/harbor 下数据库相关目录,再把新版本安装包解压覆盖,重新执行 prepare 和 docker-compose up -d。数据卷保持不变,配置沿用原来的 harbor.yml,一般就能顺利起来。
大版本升级则要谨慎得多,比如从 2.2 升到 2.3。官方提供了 upgrade.sh 脚本,会自动做数据库迁移。跨版本升级不支持跳过,有些路径要求逐步升级。升完级之后进 UI 重点检查几个地方:项目列表是否完整、复制规则是否正常执行、镜像标签是否还在。一旦发现异常,立即回滚,回滚就是恢复备份的流程,所以备份永远放在升级第一步。
5.3 常见问题速查表
我把这次部署过程中实际遇到的高频问题整理一下:
| 现象 | 原因 | 处理 |
|---|---|---|
| docker login 报 x509 unknown authority | 客户端未信任自签名 CA,或四个名称不一致 | 分发 ca.crt 到 /etc/docker/certs.d/,检查 hostname 一致性 |
| UI 无法访问,页面超时 | 云安全组/防火墙未放行端口 | 放行 443、80、4443 |
| 容器一直 Restarting | 端口占用或数据目录权限错误 | 检查端口占用,修复目录属主 |
| docker-compose 命令不存在 | 只装了新插件未做兼容别名 | 安装 docker-compose 二进制或创建 alias |
| Trivy 扫描总是 Pending | 离线环境漏洞库下载失败 | 配置 trivy skip_update,或手动导入漏洞库 |
| 登录时好时坏,随机 401 | 服务器时间漂移 | 配置 NTP 时间同步 |
| push 大镜像速度极慢 | 磁盘 IO 瓶颈 | 数据目录换 SSD,检查磁盘负载 |
表格整理完,我自己这次部署最大的体会是:Harbor 本身是个很稳的项目,绝大多数问题不是软件缺陷,而是部署环境和对概念的理解没到位。你把这套流程完整走一遍之后再看官方文档,会发现很多之前觉得模糊的段落突然就能读懂了。
最后分享一个小技巧:证书和域名映射最好在第一次部署前就固化下来,以后再添加客户端时,只需要把 ca.crt 和 hosts 记录分发过去就行。如果中途换域名、换证书,所有配置了旧地址的客户端都得跟着改,这个成本比你想的高得多。