镜像仓库这东西,看着简单,管不好真的会要命。Harbor镜像仓库是当前云原生场景里最主流的企业级开源镜像管理方案,也是我这两年在多个项目里反复使用的核心基础设施。它的核心价值一句话能说清:让内网团队安全、高效、有权限地区拉取和推送容器镜像,同时把镜像的复制、清理、扫描、审计这些脏活累活一并接过去。不管你是刚接触容器的运维新手,还是已经维护了上百节点集群的开发者,只要你准备在离线环境或者企业内网里把镜像规范管起来,这篇实操笔记应该对你有帮助。
1. 为什么我不建议裸奔式使用公共镜像仓库
1.1 公共仓库的三个典型痛点
刚开始做容器化的时候,很多人习惯直接用公共镜像仓库,反正 docker pull nginx 一下就到手了。但真到了团队协作和生产环境,问题会一个一个冒出来。
第一个痛点是“拉不动”。公共仓库的节点大多在海外,在内网环境里拉一个几百MB的基础镜像能把人急死,尤其是离网络高峰时段,超时重试是家常便饭。即便配了加速器,也解决不了制品的私密性和版本可控性问题。
第二个痛点是“权限失控”。公共仓库的仓库命名空间和权限模型是给开放共享设计的,我们要的是一套“谁能读、谁能写、谁能删”的细粒度控制,它给不了。一个团队几十号人,每个人都能乱推 tag,谁也不知道线上跑的是哪一版镜像。
第三个痛点是“回收缺失”。公共镜像仓库不会主动清理你的冗余镜像,旧 tag 越堆越多,磁盘越占越满,最后连构建机都跟着遭殃。
注意:这三个痛点不是孤立的,它们会在团队规模超过十个人之后集中爆发。如果只是个人学习和单机实验,公共仓库完全够用,不必过早自建。
1.2 Harbor 到底解决了哪些问题
Harbor 镜像仓库做的不是“又一个镜像存储服务器”,而是把容器镜像仓库从一个“存储目录”升级成了“企业管理平台”。
从功能角度拆,它解决的关键问题包括:
- 基于角色的访问控制(RBAC):管理员、开发者、访客三种角色可以独立配置项目级权限,也能创建只读账号给 CI 或外部系统使用。
- 镜像复制与多机房同步:两个 Harbor 实例之间可以按项目或按 tag 做复制,一个推送动作触发跨机房镜像同步。
- 漏洞扫描:配合 Trivy 等扫描器,在镜像推送和拉取时自动做漏洞检测,把安全防线前置到镜像层。
- 审计日志:谁在什么时间对哪个镜像做了什么操作,全部有迹可循。
- 垃圾回收(GC)与保留策略:可以按规则保留最近 N 个版本,自动清理历史镜像,避免磁盘被吃满。
这套能力覆盖的不只是运维同学,还有开发、测试、安全团队。影响范围可以直接延伸到 CI/CD 流水线:构建产物推到 Harbor,测试环境从 Harbor 拉取,生产发布也从 Harbor 拉取,整个交付链路都建立在“镜像仓库是可信的、安全的、可追溯的”这个前提之上。
1.3 组件构成与“庞大感”的真相
第一次解压 Harbor 离线包的人,基本都会被它输出的那一大类容器吓一跳:数据库、Redis、核心服务、注册表服务、任务服务、扫描器、Nginx…… 看上去很重,其实每个组件都有明确分工。
- core:对外提供 API 和 UI 后端,处理认证、授权、审计。
- registry:真正的镜像存储引擎,相当于 Docker Registry 的增强版。
- registryctl:负责镜像清理、配额检查等生命周期操作,是 registry 的“管理员遥控器”。
- jobservice:跑复制、GC、扫描这类异步任务。
- trivy:漏洞扫描引擎。
- postgresql / redis:元数据、任务队列、缓存的基础依赖。
- nginx:统一入口网关,对外暴露 80/443 端口。
Harbor 官方把它们打包成 docker-compose 体系,所以安装过程的本质是“生成配置文件 + 拉取/加载镜像 + 启动一堆容器”。理解了这一点,后续排查问题的时候就不会慌,至少知道该去哪个容器里看日志。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 部署前规划,决定了后面三个月省不省心
2.1 硬件和系统的底线
Harbor 对硬件的需求量并不夸张,但“能跑”和“跑得稳”是两回事。
我的经验是:
- CPU:2 核起步,4 核更稳。扫描和复制任务都比较吃 CPU,如果有定时批量扫描,建议 4 核以上。
- 内存:4GB 刚够入门,8GB 是常态推荐。PostgreSQL 和 Trivy 是吃内存大户。
- 磁盘:取决于镜像体量,建议数据目录使用独立挂载盘,容量按“未来半年预计镜像体积的 1.5 倍”规划。SSD 优先,机械盘在大量并发推送时 IO 延迟会很难看。
操作系统方面,主流 Linux 发行版都可以,内核版本不需要刻意追新。但 Docker 引擎版本不建议太老,至少 20.10 以上,配合 Compose v2 使用更顺畅。
注意:数据盘一定不要和系统盘混在一起。一旦镜像写满磁盘,受影响的不只是 Harbor,Harbor 所在主机的系统日志、容器运行时都可能跟着出问题。我见过好几次因为磁盘写满导致其他无关服务一起挂掉的例子,教训很深。
2.2 域名、证书和端口:先定规矩
Harbor 对外访问只有两个“脸面”:一个 UI/API 的 HTTP/HTTPS 端口,以及镜像操作走的同一个入口。建议在安装之前就把访问域名定下来,比如 registry.example.local。域名的作用不只是好看,它会直接影响镜像 tag 的命名规范,也会影响证书的 SAN(Subject Alternative Name)配置。
端口方面,默认配置是 HTTP 80 和 HTTPS 443,两者可以共存。生产环境如果只有一个入口,建议直接上 HTTPS,并把 HTTP 关闭,避免客户端用错协议产生一堆奇怪的报错。
证书规划也是“第一次就要做对”的事。自建仓库最常见的选择是自签 CA,然后给各客户端统一分发 CA 证书。如果你所在机构有条件申请内部公共 CA 签发的证书,那更省事,不过以下内容仍然适用。
2.3 在线部署还是离线部署
Harbor 提供在线和离线两种安装包。在线包很小,安装过程会从公共仓库拉取镜像,适合网络条件好的实验环境;离线包体积通常在数百 MB 到 1GB 左右,里面打包了全部组件镜像,不依赖外网,适合生产内网和离线环境。
我的建议是:只要是正式环境,一律用离线包。理由很简单,生产内网往往没有稳定的外网访问,在线安装过程中如果镜像拉取失败,整个安装流程就尴尬了。离线包多下的那几百 MB 体积,换来的是确定性和可控性。
3. 从零到一:Harbor 安装的完整实操过程
3.1 下载并解压安装包
这里以离线安装包为例,假设当前版本号是 v2.11.0。实际使用时请以官方发布页的最新稳定版为准,不要用太老的版本,否则部分功能参数会不一样。
bash复制# 假设安装包已经通过某种方式传到服务器上
tar -zxvf harbor-offline-installer-v2.11.0.tgz
cd harbor
解压后目录里会看到 harbor.yml.tmpl、install.sh、common.sh 等文件。harbor.yml.tmpl 是配置模板,我们需要先复制一份再修改,保留原始模板方便以后对照。
bash复制cp harbor.yml.tmpl harbor.yml
3.2 修改 harbor.yml 的关键参数
安装前需要先规划登录时的端口、账号密码、数据存储位置。这些参数后期虽然可以改,但改起来要重新执行 prepare,会比第一次麻烦得多,所以最好一次到位。
yaml复制hostname: registry.example.local
http:
port: 80
https:
port: 443
certificate: /data/harbor/certs/server.crt
private_key: /data/harbor/certs/server.key
harbor_admin_password: "A_Str0ng_P@ssw0rd"
database:
password: "DB_P@ssw0rd"
data_volume: /data/harbor
trivy:
enabled: true
github_token: ""
几个关键点解释一下:
hostname是整个 Harbor 对外暴露的主机名,登录界面和各类功能跳转都以它为准。如果你后面用 IP 访问,这里最好直接填 IP,并且证书里要加上这个 IP 的 SAN,否则很多客户端会提示证书校验失败。harbor_admin_password是初始管理员密码,安装后可以改,但初始化时别设太简单。data_volume是镜像和数据的总存放目录。建议一个独立目录,方便后续备份和迁移。trivy.enabled设为true才会启用漏洞扫描。如果不需要扫描功能,可以设为false或装完不指定--with-trivy。
注意:
harbor.yml里 database 密码不用设置到多复杂,但不要用默认的。因为 Harbor 会用这个密码初始化 PostgreSQL,如果使用默认值被别人扫出来,等于仓库元数据裸奔。
3.3 执行安装脚本并验证服务状态
配置改完后,执行安装脚本,加 --with-trivy 参数可以一并启用漏洞扫描引擎。
bash复制sudo ./install.sh --with-trivy
脚本会做几件事:检查环境依赖、加载组件镜像、生成 docker-compose.yml、初始化数据库并启动所有服务。整个过程通常几分钟,取决于磁盘读写速度。
装完以后先看看容器状态:
bash复制cd /opt/harbor
docker compose ps
正常情况下你会看到 Nginx、Core、Registry、Registryctl、DB、Redis、Jobservice、Trivy、Portal 这些容器全部处于 running 状态。如果没有 docker compose,用旧命令 docker-compose ps 也可以,但建议尽早换成新版。
浏览器访问 https://registry.example.local,用 admin 加刚才设置的密码登录。如果能看到控制台界面,说明 Harbor 主流程已经通了,接下来可以推一个测试镜像。
bash复制docker pull hello-world:latest
docker tag hello-world:latest registry.example.local/library/hello-world:v1
docker push registry.example.local/library/hello-world:v1
这里提个醒:如果 docker push 报证书或协议错误,先别急着怪 Harbor,大概率是客户端没有信任你的 CA 或没配好协议,下一节专门讲。
4. HTTPS 证书:自建仓库最容易被新手卡住的一环
4.1 为什么官方默认以 HTTPS 为第一推荐
Docker 客户端访问镜像仓库时的行为比较“轴”:默认情况下,它会优先尝试 HTTPS,并且要求服务端证书能被系统信任。如果你只提供了 HTTP 服务,要么在每台机器上把该仓库地址加入 insecure-registries,要么接受一堆讨人厌的警告。
更重要的是,镜像内容在传输过程里是压缩包数据,如果走明文 HTTP,理论上存在被截获和篡改的风险。企业内网环境虽然相对可控,但审计和安全扫描越来越严格,明文传输的镜像仓库很难过合规这一关。所以不到万不得已,别省证书这一步。
4.2 用 OpenSSL 签发一套自签名证书
这里只讲最实用的一种做法:自建一个内部 CA,用它签发 Harbor 的服务端证书。这样做的好处是后续给一堆客户端分发一个 ca.crt 就行,不用每台机器单独导入服务端证书。
先在证书目录工作:
bash复制mkdir -p /data/harbor/certs && cd /data/harbor/certs
第一步,生成 CA 私钥和 CA 证书:
bash复制# 生成 CA 私钥
openssl genrsa -out ca.key 4096
# 自签 CA 证书,有效期拉长到10年
openssl req -x509 -new -nodes -sha256 -days 3650 \
-subj "/CN=Harbor Internal CA" \
-key ca.key -out ca.crt
第二步,生成 Harbor 服务端的私钥和证书签名请求(CSR):
bash复制# 服务端私钥
openssl genrsa -out server.key 4096
# 生成 CSR
openssl req -new \
-subj "/CN=registry.example.local" \
-key server.key \
-out server.csr
第三步,创建扩展配置文件,把域名和可能的 IP 都写进 SAN:
bash复制cat > server.ext << 'EOF'
authorityKeyIdentifier=keyid,issuer
basicConstraints=CA:FALSE
keyUsage=digitalSignature,nonRepudiation,keyEncipherment,dataEncipherment
subjectAltName=@alt_names
[alt_names]
DNS.1 = registry.example.local
IP.1 = 192.0.2.10
EOF
第四步,用 CA 签发服务端证书:
bash复制openssl x509 -req -in server.csr \
-CA ca.crt -CAkey ca.key -CAcreateserial \
-out server.crt -days 3650 -sha256 \
-extfile server.ext
这一步做完,/data/harbor/certs 下会有 ca.crt、server.crt、server.key,其中 server.crt 和 server.key 就是 harbor.yml 里需要引用的证书和私钥。
注意:SAN 配置很重要。如果证书里没有你访问时用的域名或 IP,docker login 会报
x509: certificate is valid for xxx, not yyy。这类问题不是密码错误,而是证书匹配问题。
4.3 让每台客户端都信任你的私有 CA
服务端证书配好以后,每个需要用这个仓库的机器都要信任你的 CA。Linux 上最省事的做法是直接放到 Docker 的证书目录里:
bash复制sudo mkdir -p /etc/docker/certs.d/registry.example.local
sudo cp /data/harbor/certs/ca.crt /etc/docker/certs.d/registry.example.local/ca.crt
sudo systemctl restart docker
如果 Harbor 用的是非 443 端口,比如 registry.example.local:8443,那目录名要改成 registry.example.local:8443。Docker 是根据访问地址精确匹配证书目录的。
Windows 和 macOS 的 Docker Desktop 用户则要注意,单靠修改 daemon.json 不够,最好把 ca.crt 导入操作系统的信任证书库中。步骤不复杂,但各版本界面略有不同,搜索一下“导入证书”就能找到入口。
提示:如果你是临时测试,也可以在
/etc/docker/daemon.json里添加"insecure-registries": ["registry.example.local"]来跳过证书验证。生产环境千万别这么干,一把梭的结果就是全集群都不安全。
5. 镜像仓库上线后的核心玩法
5.1 项目和用户权限:先立规矩再使用
Harbor 里的“项目(Project)”是权限管理的基本单位,类似 Docker Hub 里的命名空间。项目可以设为“公开”或“私有”。公开项目意味着所有登录用户都能拉取,适合基础镜像、公共组件;私有项目则严格控制拉取权限,适合业务应用镜像。
刚安装完默认只有一个 library 项目,是公开的。我习惯先把 library 改名或直接复用,但更多时候建议按团队和业务域新建项目。比如:
base-images:存放操作系统、运行时、中间件基础镜像。backend-svc:后端服务镜像。frontend-web:前端应用镜像。ci-cache:构建缓存镜像。
在“用户管理”里创建用户时,可以给每个用户分配一个默认角色。角色体系核心三种:
- 项目管理员:负责项目下的镜像、成员、复制规则。
- 开发者:可以推送和拉取镜像。
- 访客:只能拉取,适合用来配置测试环境自动部署。
这套权限模型的妙处在于它跟 Docker CLI 的认证是天然打通的。开发者用 docker login 登录后,推送目标里带上项目名,权限就会被 Harbor 自动校验。
5.2 镜像复制让多机房同步变得简单
如果你们有两套环境,一套生产、一套灾备,或者总部和分公司各有一台内网服务器,Harbor 的镜像复制功能能省掉大量手工搬运。
复制规则的核心是“目标端点 + 触发方式 + 资源过滤器”。目标端点可以是另一套 Harbor,也可以是其他标准 Registry。Harbor 支持两种触发方式:
- 手动触发:点一下就复制一次,适合临时同步。
- 事件驱动:源项目里推了新镜像,立刻触发复制,适合主备实时同步。
复制方向可以配置为推(Push)或拉(Pull)模式。如果两个机房网络互通,一般用 Push 从主中心推过去;如果目标端点在另一个内网拉不到源端,可以反向用 Pull 模式,让目标端主动来拉。
我实际使用中的一个细节是:复制规则不要图省事做整站复制,建议按项目分规则建。否则偶尔想把某个项目调整为“不同步”时,翻规则列表会非常痛苦。
5.3 垃圾回收与保留策略:磁盘是会被吃满的
Harbor 不会自动删镜像,除非你配置了保留策略或者手动清。镜像的 layers 是共享的,所以直接删一个 tag 不一定能释放空间,因为同一个 blob 可能被其他 tag 引用着。这时候就要用到 Harbor 的垃圾回收(GC)功能。
在 Harbor 2.x 里,清理操作路径是:进入某个项目 -> 查看镜像 -> 选中不需要的 tag -> 删除。完成删除后,再到“系统管理 -> 垃圾回收”里触发一次 GC。执行 GC 时务必勾选“删除未引用的 blob”,否则删除的 tag 只是从元数据里消失了,底层的 blob 还占着磁盘。
关于保留策略,Harbor 支持在项目配置里设置自动清理规则。三种常见规则:
- 按“最近 N 天”保留:只保留最近 14 天的镜像,适合频繁发版的测试项目。
- 按“最近 N 个 tag”保留:每个镜像名下只留最近 10 个 tag,适合生产环境控制版本数量。
- 按正则排除:比如把带有
latest、release-的 tag 排除在清理范围外,防止重要版本被干掉。
这三个策略我可以组合用。比如生产项目用“最近 5 个 tag”加正则排除 latest,测试项目用“最近 3 天”。
注意:GC 是一个相对重量级的操作,执行期间 registry 可能会短暂变慢。建议在低峰期执行,并且不要同时开多个 GC 任务。
5.4 配额管理:对每一个项目设上限
如果团队里每个人都觉得自己“只推一小个镜像”,那其实是误会。几个月下来,几百 GB 毫无预兆就没了。Harbor 的配额管理可以直接在项目维度做限制,支持两个维度:最大副本数量和最大存储大小。
在项目配置里设置“存储上限”,比如 50 GiB。一旦达到上限,后续推送会被拒绝,镜像会保留在暂存区排队。设置配额不是为了卡自己人,而是为了让镜像体积成为可观测的数字。我习惯新建项目时默认给 20 GiB 配额,等团队明确需要再提高,避免失控。
当然,配额管理不是替代 GC 的万能药。它最多是“止血”,真正释放空间还得靠保留策略和定期 GC。
6. 我踩过的那几个坑:常见问题排查实录
6.1 登录失败与证书信任问题
docker login registry.example.local 报 x509: certificate signed by unknown authority 是我收到过最多的咨询问题。这个报错几乎和账号密码无关,原因就是 Docker 不认得你的 CA。
按 4.3 的步骤把 ca.crt 放到 /etc/docker/certs.d/registry.example.local/ 下,重启 Docker,基本解决。如果已经放了还报错,检查一下目录名是否完全等于地址,不能多不能少,端口也要写对。
6.2 HTTP 服务给 HTTPS 客户端的问题
还有一类报错是:
text复制Error response from daemon: Get "https://registry.example.local/v2/": http: server gave HTTP response to HTTPS client
这句话的意思是:Docker 尝试用 HTTPS 访问,但 Harbor 实际返回的是 HTTP 明文。要么 Harbor 真的没有开启 HTTPS,要么你用了 IP:Port 连到一个只监听 HTTP 的入口。
最简单的排查方式是先确认浏览器里访问的是 https:// 还是 http://。如果 Harbor 只配置了 HTTP,就在每台客户端配置 insecure-registries;如果目标是 HTTPS,检查 harbor.yml 里 https 配置和端口映射是否有冲突。
6.3 admin 密码遗忘了怎么办
Harbor 没有提供一条简单的 reset admin password 命令,但实际可以通过修改数据库里的密码哈希来完成。以最新版本为例,密码字段存在 PostgreSQL 的 harbor_db 库中的 user 表里,值是 bcrypt 哈希。
先用 htpasswd 生成新哈希:
bash复制htpasswd -bnBC 10 "" 'YourNewPassword' | tr -d ':\n'
然后进入数据库容器,更新对应 username=admin 的那一行:
bash复制docker exec -it harbor-db psql -U postgres -d harbor_db -c \
"UPDATE user SET password='<上面生成的哈希>' WHERE username='admin';"
执行完重启所有 Harbor 容器即可。这里有个前提:你要有进入数据库容器的权限。如果你连 harbor-db 容器都进不去,说明服务器权限本身已经被锁死了,那还是回到主机权限管理上去处理吧。
提示:如果担心自己执行 SQL 出错,操作前先备份一下
user表的记录。查询语句很简单:
docker exec -it harbor-db psql -U postgres -d harbor_db -c "SELECT user_id, username FROM user;"。
6.4 磁盘清理不释放,GC做了却无效
这个问题通常是因为 GC 时没有勾选“删除未引用的 blob”。镜像 tag 在 UI 里删掉之后,底层的 blob 并没有立刻被清理,而且同一层可能被多个 tag 共享。需要再执行一次 GC,勾选删除未引用 blob,才会把真正不再被引用的数据层删除。
另一个常见原因是项目下有复制规则,目标端点了“保留已删镜像”。复制过来的 tag 会形成引用,源端删了,目标端还在,复制规则会周期性让删除行为反复出现,造成 GC 一直存在残留。要彻底清理,需要先暂停或调整复制规则。
6.5 组件间端口冲突
Harbor 默认通过 Nginx 暴露 80/443,如果宿主机上这两个端口已经被占用,安装脚本虽然不报错,但你会一直访问不到界面。排查方法:
bash复制sudo netstat -tlnp | grep -E ':(80|443)\s'
如果有其他进程占用,可以在 harbor.yml 里把 http.port 改成比如 8080,把 https.port 改成 8443。改完记得重新执行 ./install.sh,它会基于新配置重新生成 compose 文件。
6.6 常见问题速查表
| 现象 | 可能原因 | 处理方法 |
|---|---|---|
unknown authority |
客户端未信任 CA | 分发 ca.crt 到指定目录并重启 Docker |
server gave HTTP response to HTTPS client |
Harbor 实际用 HTTP | 配置 insecure-registries 或开启 HTTPS |
| 登录提示密码错误 | 密码过期或数据库异常 | 按 6.3 重置密码哈希 |
| 磁盘空间不释放 | GC 未勾选删除未引用 blob | 重新 GC 并勾选删除未引用 blob |
| Harbor 页面打不开 | 80/443 端口被占用 | 修改 harbor.yml 端口并重启安装 |
| 推送镜像速度极慢 | 数据盘 IO 性能差 | 迁移 data_volume 到 SSD 挂载盘 |
| 复制任务一直失败 | 目标端点证书不信任 | 在源 Harbor 配置目标仓库时导入可信 CA |
这套步骤写下来,其实就一条主线:先把 Harbor 当成一个普通 Web 服务装起来,再用 HTTPS 和权限给它穿上铠甲,最后把 GC、配额、复制这些自动运维能力打开,让仓库自己管理自己。
我个人在实际操作中的体会是,Harbor 镜像仓库真正难的不是安装,而是“想清楚怎么用”。建议你部署完成后的第一周,不要急着把所有项目都搬进来,先跑一个真实项目的镜像流转,让开发、测试、运维三方都把流程走通,再逐步推广。另外还有一个值得提前做的小事:给 Harbor 安排一个独立的监控项,周期性检查 /api/v2.0/health 接口和数据盘水位,很多磁盘问题其实可以在爆发之前就被观察到。希望这篇文章能让你少走几步弯路。
