最近有朋友问我,手上有几台服务器,镜像总是散着放,一会儿存在这台,一会儿放在那台,版本还经常搞混,问我有没有什么“轻量又不失控”的方案。我的建议一直很统一:别折腾那些复杂的平台,直接搭一个Harbor私有镜像仓库就够了。Harbor不是新东西,企业里已经跑了十几年,但个人用起来一样顺手,甚至比你想的更简单。今天这篇就完整记录我最近一次从零搭建Harbor私有仓库的整个过程,包括环境准备、离线安装、HTTPS证书处理、日常推送拉取,以及我踩过的一些坑。如果你正打算给自己建一个专属镜像仓库,这篇文章可以直接照着操作。
1. 为什么个人也要折腾一个私有镜像仓库
1.1 私有仓库到底解决了什么
很多人觉得“镜像仓库”是公司才需要的东西,个人开发没必要。但只要你手上超过一台服务器,或者你同时在维护两三个项目,你就一定会遇到下面这些场景:
场景一,你在一台机器上构建了某个镜像,调试完想去另一台机器部署,最原始的办法是docker save打个 tar 包,再拷过去docker load。镜像一多,版本一多,tar 包就成了一堆谁也分不清的“黑盒子”。场景二,你在不同机器上构建同一个项目的镜像,结果两台机器上的基础镜像版本、编译参数都不完全一致,最后生产环境跑出来的东西和测试环境完全对不上。场景三,你不想把业务镜像推到公共仓库,尤其是涉及数据库连接串、内部配置的时候,公共仓库基本不在考虑范围。
私有仓库解决的就是这些问题:镜像集中存放、版本统一管理、内网高速拉取、可控的权限边界。打个比方,公共镜像仓库像快递柜,东西能放能取,但柜子不是你的,容量和权限都受限;自己搭的私有仓库像是自家工具间,东西归置好了,想怎么摆怎么摆,给谁钥匙也完全由你定。
1.2 Harbor相比其他方案好在哪
如果你只是临时用一下,Docker 官方那个registry:2镜像也能跑一个最简单的私有仓库,几行命令就能拉起来。但用过一段时间你就会发现,它只有存储功能,没有可视化界面,没有权限管理,没有镜像清理策略,连想看个镜像列表都得写脚本调 API。个人用还勉强,稍微需要管理就捉襟见肘。
Harbor 的优势恰恰在这里:它自带完整的 Web 管理界面,能可视化查看镜像列表、标签、大小、拉取次数;支持多租户的“项目 + 仓库”二级结构,每个项目有独立的权限体系;可以做镜像复制(Replication),从一个 Harbor 同步到另一个 Harbor,天然适合做多机房备份或环境迁移;还支持垃圾回收、策略清理、操作审计这些功能。虽然这是企业级产品的定位,但对个人来说,多占一点内存换来的管理效率非常值。
我当初也纠结过是不是用轻量的registry就够了,后来发现在上面做权限、做清理完全靠手工,维护成本远高于把 Harbor 跑起来。最终结论是:一台 2 核 4G 的机器,跑 Harbor 绰绰有余,个人完全没必要在“轻量”这件事上委屈自己。
2. 安装前:环境、域名、Docker 准备
2.1 服务器怎么选、配置要求
Harbor 的官方文档建议最低 2 核 CPU、4G 内存,实际跑起来之后,空闲状态下内存占用大约在 2G 左右。我给朋友配置的时候用的是某云厂商的一台轻量服务器,2 核 4G、系统盘 80G,跑 Harbor 加两个 Redis 实例,还有不少富余。
磁盘这块我建议别抠,因为镜像占空间的速度比你想象中快得多。一个带基础环境的基础镜像动辄上百兆,如果还自己打包 Python 环境或者 Go 二进制,单个镜像轻松上 GB。系统盘不够的话,安装前先把数据盘挂好,Harbor 的所有数据都会集中在data_volume目录下,把那个目录放到大容量数据盘,后面会省很多事。
操作系统我比较推荐 Debian 系的或 RHEL 系的长期支持版本,Ubuntu 20.04 / 22.04、Debian 11 / 12、Rocky Linux 8 / 9 之类。Harbor 官方对 Linux 发行版没有特别挑,只要内核版本和 Docker 兼容就行。Windows 方面不用考虑,直接跳过。
2.2 装好 Docker 和 Docker Compose
Harbor 的安装方式虽然看起来是一个“安装脚本”,但本质上是通过 Docker Compose 拉起一组容器,所以前提条件是两个:Docker 和 Docker Compose。Docker 版本建议不低于 20.10,Docker Compose 建议是 v2 版本,也就是docker compose这个子命令。
旧的发行版自带源里可能只提供旧版本 Docker,建议用官方安装脚本或者配置官方源来装。如果服务器在网络上有代理限制,至少要让 Docker 能拉到 Harbor 基础镜像,否则在线安装会卡住。
安装完成后先验证一下环境:
docker --version docker compose version这两条命令都能正常输出,就说明 Dcoker 和 Compose 环境没问题。如果你的系统上还没有 Docker Compose v2,可以单独下载二进制文件放到/usr/local/bin/docker-compose,Harbor 的安装脚本也会尝试调用它,不过新版 Harbor 更推荐直接支持docker compose子命令。
2.3 离线包下载与版本选择
Harbor 的官方 GitHub Release 页面会同时提供两种安装包:harbor-online-installer和harbor-offline-installer。名字容易误导人,很多人以为“在线”更方便,实际恰好相反。
在线安装包只包含安装脚本和配置文件,真正安装时要当场从镜像仓库拉取 Harbor 各个组件的镜像,如果网络状况不好,安装过程会非常痛苦。离线安装包把 Harbor 的所有组件镜像都打包在压缩包里了,安装时直接本地导入,速度飞快,也不依赖外部网络。我第一次装的时候没经验,选了个在线包,光拉镜像就卡了快二十分钟,后来果断换成离线包,两分钟就导完了。
版本选择上不用太追新,挑一个当前 stable 的版本就好。以我这次安装的 v2.11.0 为例,直接下载:
wget https://github.com/goharbor/harbor/releases/download/v2.11.0/harbor-offline-installer-v2.11.0.tgz下载完以后先校验一下 SHA256,再决定要不要用。别嫌这个步骤麻烦,有一次我在网络状况不稳定的环境下下载,解压时就提示文件损坏,搞了半天才发现是压缩包断了。
3. 一步一步完成 Harbor 安装
3.1 解压和修改 harbor.yml
把离线包传到服务器上之后,找一个合适的目录解压。我习惯放在/opt/harbor下,因为这样整个 Harbor 的环境自包含,后续备份和升级都方便:
mkdir -p /opt/harbor tar -zxvf harbor-offline-installer-v2.11.0.tgz -C /opt/harbor cd /opt/harbor/harbor目录下有install.sh脚本和大量 Harbor 相关的 CFG 文件,还有一个harbor.yml.tmpl,这是模板配置,需要复制成harbor.yml再修改:
cp harbor.yml.tmpl harbor.yml vim harbor.ymlharbor.yml 是 Harbor 安装的核心配置,所有关键参数都在这里定义。以下几个字段是最需要上心的:
hostname是最关键的字段,它决定了 Harbor 对外暴露的访问地址。如果填的是 IP,那访问就是https://<IP>;如果填的是域名,那访问就是https://<域名>。这个值必须与后面证书里的域名或 IP 对应上,否则客户端会报证书不匹配错误。
http.port默认是 80,https.port默认是 443。如果你的服务器上 80 或 443 端口已经被其他服务占用,这里一定要提前改掉。比如你同时还想跑一个 Web 服务,那就把 HTTP 端口改成 8080 之类的不冲突端口。我印象里有个朋友在这里栽过跟头,他机器上 Nginx 已经占了 80,Harbor 装完以后怎么访问都不对,最后发现是端口冲突,改完配置重新装才恢复正常。
data_volume指定所有镜像数据和数据库文件的存放目录。建议填一个独立数据盘路径,比如/data/harbor,顺便可以加大磁盘容量。默认配置写的/data/harbor,如果你没有挂载独立数据盘,这个目录就会落在系统盘,后面很容易不够用。
harbor_admin_password是管理员密码,默认是Harbor12345,安装前一定得改掉。别问为什么,默认密码被人扫到,你的私有仓库就变成公共仓库了。另外还有database小节里的password,这是 Harbor 内部数据库的管理密码,默认叫root123,建议一并改掉。
3.2 生成自签名 HTTPS 证书
Harbor 默认配置强制要求 HTTPS,我之前为图省事试过把 HTTPS 关掉直接用 HTTP,但 Docker 客户端那边反而更麻烦,会在推送时报http: server gave HTTP response to HTTPS client。所以老老实实把 HTTPS 配上,一劳永逸。
如果你有正规域名,可以用云厂商免费证书,申请下来以后把证书三个文件放到/data/cert/就行。如果只是个人使用,没有域名,那用自签名证书完全够用。下面是我常用的自签名证书生成脚本:
# 生成 CA 私钥 openssl genrsa -out ca.key 2048 # 生成 CA 证书 openssl req -x509 -new -nodes -key ca.key -days 3650 -subj "/CN=Harbor-CA" -out ca.crt # 生成服务器私钥 openssl genrsa -out server.key 2048 # 创建证书签名请求,这里要包含域名或IP openssl req -new -key server.key -subj "/CN=192.168.1.100" -out server.csr # 生成带扩展信息的配置文件 cat > ext.cnf <<EOF basicConstraints=CA:FALSE keyUsage=digitalSignature,keyEncipherment extendedKeyUsage=serverAuth subjectAltName=IP:192.168.1.100,DNS:harbor.local EOF # 用 CA 签发服务器证书 openssl x509 -req -in server.csr -CA ca.crt -CAkey ca.key -CAcreateserial -out server.crt -days 3650 -extfile ext.cnf注意subjectAltName这一行非常关键,如果漏了,就算证书本身有效,Docker 客户端也会因为域名不匹配拒接。证书生成好以后,把server.crt和server.key放到一个目录,比如/opt/harbor/certs/,再把ca.crt也复制过去,留着给客户端配置信任。
然后在 harbor.yml 的https小节里配置证书路径:
https: port: 443 certificate: /opt/harbor/certs/server.crt private_key: /opt/harbor/certs/server.key3.3 执行安装脚本并验证
配置好 harbor.yml 和证书之后,直接执行安装脚本。脚本会根据配置自动完成 harbor 基础组件的启动:
./install.sh安装过程会陆续启动 Harbor 的各个组件,包括nginx、registry、core、portal、database、redis等容器。看到输出里出现Harbor has been installed and started successfully就说明安装成功了。
启动完成后,先看下所有容器状态是否正常,命令是docker compose ps。正常情况下所有容器的 STATE 都是Up。然后直接在浏览器里访问https://<你的IP>,用admin和你改过的密码登录。
除了这几个核心组件,Harbor 还支持附带安装一些扩展功能,例如:
--with-chartmuseum开启 Helm Chart 仓库支持,如果做 Kubernetes 相关开发可以加上。--with-notary开启镜像签名验证,类似给镜像加上“防伪标签”。--with-trivy开启镜像漏洞扫描,这个功能对安全有要求的话有价值,但个人使用如果资源紧张可以先不开。
提示:如果安装过程中报错,先看输出信息。最常出现的问题是端口被占用、证书文件不存在、docker compose 命令找不到这三大类。缺什么补什么,重跑 install.sh 就行,不会重复拉镜像。
4. 客户端接入:推送和拉取镜像
4.1 让 Docker 信任私有仓库
Harbor 装好以后,接下来就是让其他机器的 Docker 客户端能访问它。这里有两种方式:
第一种方式是配置 Docker 的ca.crt信任。把之前生成的ca.crt拷贝到客户端的/etc/docker/certs.d/<Harbor地址>/目录下,目录结构类似这样:
/etc/docker/certs.d/192.168.1.100:443/ └── ca.crt这样 Docker 会在访问该地址时自动用这个 CA 证书校验 Harbor 的服务器证书,安全性更好,适合正式环境。
第二种方式是直接把 Harbor 地址加进insecure-registries列表。修改/etc/docker/daemon.json:
{ "insecure-registries": ["192.168.1.100:443"] }然后重启 Docker:
systemctl restart docker这种方式简单直接,但会跳过该地址的证书校验,安全性弱一些。个人内网环境图省事,用这种方式的也不少。我的建议是:如果就在家里或公司内网折腾,insecure-registries够用;如果你还想从外网访问,或者对安全比较在意,建议老老实实配 CA 信任。
4.2 推送第一个镜像
配置好信任之后,用docker login登录 Harbor:
docker login 192.168.1.100:443 -u admin按提示输入密码,提示Login Succeeded就说明已经连上了。接下来找一个本地镜像测试一下:
docker tag nginx:latest 192.168.1.100:443/library/nginx:v1 docker push 192.168.1.100:443/library/nginx:v1推送完成后,打开 Harbor 的 Web 页面,在默认的library项目里就能看到刚才推送的nginx镜像以及v1标签。以后构建出来的新镜像,打上同一个带 Harbor 地址和项目名的 tag,推送进去就统一管理了。
这里有个细节很容易绕晕:镜像 tag 必须带完整的仓库地址格式。本地镜像名是nginx:latest,推送地址是192.168.1.100:443/library/nginx:v1,两者之间用docker tag建立关系。这也是 Docker 判断推往哪个仓库的唯一依据——看 tag 前缀里有没有注册地址。
4.3 项目、用户和权限管理
Harbor 的权限模型是“项目 + 成员”的形式。你可以创建多个项目,每个项目代表一个功能域,比如前端项目、后端项目、中间件项目。项目可以设置成公开(Public)或私有(Private)。公开项目意味着所有登录用户都能拉取镜像,适合放一些不敏感的基础镜像;私有项目则只有项目成员能操作。
实际操作步骤是:
- 在 Harbor 的管理界面的“项目”页面,点击“新建项目”,填写项目名称,设置访问级别。
- 在“系统管理”的“用户管理”里创建新用户,设置用户名、密码和邮箱。
- 进入某个项目的“成员”页面,点击“添加用户”,把刚才创建的用户加进来,分配角色。
Harbor 的角色有三种:项目管理员、开发人员、访客。项目管理员能编辑项目配置、添加成员、删除镜像;开发人员能推送和拉取镜像;访客只能拉取。权限分配清楚后,别人拿到的登录凭据就只会推动你允许他们动的镜像仓库,不会因为误操作把关键镜像删掉。
经常有个人开发者忽略权限管理,嫌麻烦,但我还是要提醒一句:哪怕只有你一个人用,也要把“部署机”和“开发机”分开建账号。开发机有推送权限,部署机只给拉取权限。万一部署机被攻破,至少不让别人直接改你仓库里的镜像。
镜像复制(Replication)也提一下。如果你有两套 Harbor 环境,比如一台测试机一台生产机,可以在“系统管理”的“复制管理”里配置规则,让生产机自动从测试机拉取镜像。这样测试环境的镜像只要推送上去,生产环境会自动同步,省去了手动docker pull+docker tag+docker push的往返操作。
5. 日常运维与常见问题
5.1 常用启停命令
Harbor 本质上是一组 Docker Compose 管理的容器,所以启停命令非常直接。进入 Harbor 安装目录:
cd /opt/harbor/harbor # 停止 Harbor 容器 docker compose down # 启动 Harbor 容器 docker compose up -d # 查看 Harbor 容器状态 docker compose ps更新配置以后,想要重新加载配置,可以执行docker compose up -d让 Compose 按新配置重建容器。不过改 harbor.yml 这种级别的配置变化,比如端口或证书路径,光up -d可能不够,更稳妥的办法是docker compose down再up -d,让容器彻底重启。
5.2 磁盘清理和垃圾回收
Harbor 跑久了以后,最头疼的问题是磁盘占用。每删除一个镜像,实际上只是移除了标签引用,底层数据还在磁盘上躺着。时间一长,几十 GB 的“僵尸数据”就这么攒下来了。
Harbor 内置了垃圾回收功能,在 Web 界面的“系统管理”里找到“垃圾回收”,点击立刻回收即可。它会遍历所有镜像仓库,把已删除标签引用的存储层清理掉。正式环境建议在低峰期执行,因为垃圾回收过程中 registry 会进入只读状态,部署操作会短暂影响。
我一般会定一个手动运维计划,每两周看一眼磁盘占用,如果超过 70% 就触发一次垃圾回收。也别回收太频繁,因为每次垃圾回收会扫描大量镜像层,有一些 I/O 压力,对个人使用来说,一个月一次就够了。
日志也会慢慢占空间,Harbor 容器都在 Docker 里,可以定期清理 Docker 日志文件。最简单的做法是在/etc/docker/daemon.json里加上日志大小限制:
{ "log-driver": "json-file", "log-opts": { "max-size": "10m", "max-file": "3" } }重启 Docker 之后重启 Harbor 容器,日志膨胀的问题就基本控制住了。
5.3 问题排查速查表
实际操作中一定会碰到各种报错,这里把我遇到和听说的常见问题整理成一张表,方便你快速定位。
| 问题现象 | 原因 | 解决办法 |
|---|---|---|
http: server gave HTTP response to HTTPS client | Harbor 配置了 HTTPS,但客户端 Docker 没启用证书信任 | 配置insecure-registries或 CA 证书,重启 Docker |
x509: certificate signed by unknown authority | 客户端不信任 Harbor 的自签名 CA | 把自签名 CA 拷贝到/etc/docker/certs.d/<地址>/ca.crt |
certificate is valid for xxx, not yyy | 证书 CN/SAN 与实际访问地址不一致 | 重新生成包含正确 IP/域名的证书,更新到 Harbor 和 Docker 两端 |
| Harbor 页面打不开 | 80/443 端口被占用 | 检查端口占用,修改 harbor.yml 里的端口并重装启动 |
docker login很慢或超时 | 服务器时间与客户端时间不一致 | 保持两端时间同步,建议配置 NTP |
| 镜像推送成功但看不到 | 项目设置成了私有,当前用户没有权限 | 把用户加入项目,或把项目改成公开 |
| 磁盘空间不足 | 镜像层占满磁盘 | 清理无用镜像,执行垃圾回收 |
| 忘记 admin 密码 | 密码丢失或配置混乱 | 进入 Harbor 数据库容器重置密码 |
admin 密码重置这块单独说下。进入 Harbor 的数据库容器,把harbor_user表里的 admin 密码改成新值,具体命令如下:
docker exec -it harbor-db psql -U postgres -d harbor -c "UPDATE harbor_user SET password = '<新密码密文>', salt = '<新盐值>' WHERE username = 'admin';"密码密文需要用 bcrypt 生成,可以直接用一个临时 Python 环境计算。手边没有现成工具的话,用htpasswd的帮助也行。更省事的办法是备份数据库之后,直接把harbor.yml里的密码改回初始值,重新执行/install.sh,让 Harbor 把数据库重新初始化一遍,然后再从备份恢复镜像资料。不过这个操作比较重,适合数据不重要、只想把服务救活的场景。
注意:重置密码前一定先备份数据库。Harbor 的所有配置和用户数据都存在 PostgreSQL 里,镜像数据虽然不影响,但没有用户和项目配置,仓库就完全没法用了。
最后再说几句实际操作中的体会
我前后搭过好几次 Harbor,从最早用在线包卡在拉镜像,到后来用离线包几分钟装好,最大的感受是:安装过程本身不难,难的是把证书、端口、数据目录这几个基础问题想清楚。证书尤其要提前规划,别等装完了发现访问不了,又回头折腾。另一个感受是权限这件事,个人使用者很容易忽略,但只要你未来有可能把仓库暴露到非本机环境,多花十分钟建两个用户、分好角色,后面会少掉太多麻烦。
还有一个小技巧分享给有备份习惯的人:Harbor 装完后,把harbor.yml、证书目录、以及install.sh的输出日志一起备份一份,放到独立目录里。以后万一服务器坏了要重建,只要把配置文件和证书放回去,几分钟就能恢复一个同样配置的 Harbor,不用再从头摸索一遍。