☰
Harbor私有镜像仓库搭建实战:从Docker Registry到企业级部署
2026/10/9 10:34:07 网站建设 项目流程

1. 为什么我最终选了 Docker + Harbor,而不是裸跑一个 Registry

先别急着敲命令。我前前后后帮团队和自己搭过好几次私有镜像仓库,从最早图省事直接起一个registry:2容器,到后来老老实实上 Harbor,中间踩了不少坑。如果你只是临时给三五台机器用,裸registry确实够用,但一旦涉及多人协作、权限区分、镜像清理、UI 查看这些真实需求,你很快就会觉得裸registry像个毛坯房——能住,但处处别扭。

Harbor 本质上是基于 Docker Registry 做了一层企业级封装,它把镜像存储、权限管理、Web 管理界面、日志审计、复制同步、漏洞扫描这些能力都打包进去了。你不需要自己再写一套脚本去管理镜像列表,也不用靠命令行一个个看 tag,登录 Web 页面就能看得明明白白。对于团队内部想要一套“像样”的镜像管理系统,Harbor 基本是最稳的起点。

我这次搭建用的环境是 CentOS 7.9 + Docker 20.10 + Harbor 2.5.3。为什么选 Harbor 2.5.3 而不是最新的 2.8、2.9?原因很简单:稳定。2.5 版本在大量生产环境里跑过,坑基本都被前人踩平了,而且对 Docker 20.10 的兼容性非常友好。如果你没有特殊的新特性需求,我建议你也别追新,稳定优先。

这篇内容我会把整个搭建过程拆成四块:环境准备、Harbor 部署、镜像推送拉取、问题排查。每一块我都会讲清楚“为什么这么做”和“我实际遇到的坑”,而不是只丢一串命令给你。

2. 环境准备:先把 Docker 这块地基打牢

2.1 Docker 安装与启动

Harbor 官方推荐配合 Docker 20.10 以上版本使用,但实际测试下来 Docker 18.09 也能跑,只是部分 API 兼容性会有些小问题。我这里用的是 CentOS 7.9,安装 Docker 时建议直接用官方源,别用系统自带的旧版本。

# 卸载系统自带的老版本(如果有) sudo yum remove docker docker-client docker-client-latest docker-common docker-latest docker-latest-logrotate docker-logrotate docker-engine # 安装 yum-utils 并配置官方源 sudo yum install -y yum-utils sudo yum-config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo # 安装 Docker sudo yum install -y docker-ce docker-ce-cli containerd.io # 启动并设置开机自启 sudo systemctl start docker sudo systemctl enable docker

很多人在systemctl start docker这一步会卡住,报错信息五花八门,但最常见的就是Failed to start Docker Application Container Engine。这时候别慌,先执行journalctl -u docker --no-pager | tail -50看日志,我遇到过的几种典型情况后面会在排查章节专门展开。

2.2 权限问题:permission denied 的真相

如果你不是用 root 用户操作,执行docker ps大概率会碰到这个报错:

permission denied while trying to connect to the Docker daemon socket at unix:///var/run/docker.sock

这不是什么神秘问题,就是当前用户不在docker用户组里。Docker daemon 默认监听在/var/run/docker.sock,这个 socket 文件的权限属于 root 和 docker 组,普通用户没权限访问。

解决方式有两种。第一种省事但治标不治本,每条命令前面加sudo。第二种是把你自己的用户加进 docker 组:

sudo usermod -aG docker $USER newgrp docker

执行完newgrp docker之后,当前终端就能直接访问 docker 命令了。但要注意,如果你是通过 SSH 远程连接的,可能需要重新登录一次才生效。还有一种情况是公司安全团队会禁用这个做法,因为加入 docker 组等于拥有了 root 级别的能力——容器可以通过挂载宿主机目录的方式拿到 root 权限。这个就见仁见智了,个人开发环境怎么方便怎么来,生产环境就得按安全规范来。

2.3 Docker 加速器与镜像下载慢的问题

这个问题说到痛点了。很多人搭私有镜像仓库,起因就是“从 Docker Hub 拉镜像太慢”。我这边实测,直接拉一个 ubuntu:22.04 可能要几分钟,某些大镜像比如 pytorch 相关的能拉到怀疑人生。

共享一下我在用的加速方案。编辑/etc/docker/daemon.json:

{ "registry-mirrors": [ "https://docker.m.daocloud.io", "https://dockerproxy.com", "https://docker.nju.edu.cn" ] }

然后重启 Docker:

sudo systemctl daemon-reload sudo systemctl restart docker

提醒一句:加速器不是百分百可靠,某些镜像源会间歇性失效,所以建议配置多个作为备选。而且加速器只对从 Docker Hub 拉取的公共镜像有效,如果你后面要从 Harbor 拉镜像,走的是另一条链路,跟这里没关系。

2.4 装机必备:docker-compose 的安装

Harbor 安装依赖 docker-compose。为什么?因为 Harbor 本身就不是一个单容器,它由一堆组件构成:nginx、core、portal、registry、redis、postgresql、jobservice 等等。用 docker-compose 一键编排是最合理的方式,你手动一个个docker run的话,那叫自虐。

sudo curl -L "https://github.com/docker/compose/releases/download/v2.27.0/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose sudo chmod +x /usr/local/bin/docker-compose docker-compose --version

注意,如果你用pip install docker-compose装的是 1.x 老版本,也能用,但 Harbor 2.x 官方要求 docker-compose 1.18.0 以上,实际用 2.x 版本更稳。另外不同发行版安装方式略有差异,Ubuntu 上可以直接apt install docker-compose-v2,效果一样。

3. Harbor 部署全流程:拿一个离线包,四步搞定

3.1 下载安装包:在线包 vs 离线包

Harbor 提供了两种安装包:在线安装包(harbor-online-installer)和离线安装包(harbor-offline-installer)。

在线包体积小,只有几十 MB,但你执行install.sh的时候它会去 Docker Hub 拉镜像。如果服务器本身拉 Docker Hub 就慢,那在线包会把安装过程拖得很痛苦。离线包把 Harbor 需要的所有镜像打包好了,体积约 1GB 左右,拷到服务器上解压就能用,完全不依赖外部网络。

我第一次搭的时候图省事用的在线包,结果在load images那一步等了近十分钟,后来学乖了直接用离线包。搭内网服务这件事本身就是反依赖的,能用离线包就别折腾在线包。

下载地址在 GitHub Releases 页,文件名类似harbor-offline-installer-v2.5.3.tgz。下载后解压:

tar -zxvf harbor-offline-installer-v2.5.3.tgz cd harbor

3.2 证书问题:HTTP 还是 HTTPS?先想清楚

解压后目录里有个harbor.yml.tmpl,你需要复制一份改成harbor.yml再修改配置:

cp harbor.yml.tmpl harbor.yml

这里有一个关键决策:你是用 HTTP 还是 HTTPS 访问 Harbor?

我之前在搭建过程中踩过一个非常隐蔽的坑:Harbor 默认配置https端口 443,证书指向的是自带的自签名证书。你看着好像开箱即用,但实际上 Docker 客户端默认不信任自签名证书,docker login的时候会直接报x509: certificate signed by unknown authority。

有两种解法。第一种是做成 HTTPS 并让所有客户端信任你的 CA 证书,这个适合正规企业环境;第二种是图省事直接走 HTTP,然后把 Harbor 的地址写进 Docker daemon 的 insecure-registries 配置里,跳过证书校验。

我这篇内容以第二种方式为主线,因为内网私有仓库这个场景,HTTP 完全够用,重点是要把配置说透。但不管选哪种,我必须强调:生产环境如果有外网暴露需求,一定要上 HTTPS,否则镜像内容是明文传输的,等于把你的代码和配置裸奔在网络上。

3.3 harbor.yml 核心配置详解

这是我的 harbor.yml 关键配置片段:

hostname: 192.168.209.133 http: port: 80 https: port: 443 certificate: /your/cert/path/harbor.pem private_key: /your/cert/path/harbor-key.pem

注意,在 2.x 版本里,如果你决定用 HTTP,虽然 http 段是必填的,但 https 段可以注释掉。我的建议是:内网测试环境直接注释掉 https,用 HTTP 80 端口,干净利落,避免后面 docker login 报 HTTPS 错误。

为什么写hostname: 192.168.209.133而不是localhost或域名?因为 hostname 决定的是镜像仓库的访问地址,你推镜像的 tag 必须和这个地址一致,镜像名才会被识别为仓库地址。如果写成localhost,其他机器就根本没法访问这个仓库。内网环境没有 DNS 的情况下,直接写 IP 最省事。

其他几个关键参数:

  • harbor_admin_password:默认是Harbor12345,不解释,你懂的,必须改。
  • data_volume:默认/data,这是 Harbour 所有镜像数据的存储位置。我强烈建议挂到独立磁盘上,避免系统盘被镜像占满。
  • database和redis段:单机部署不用动,保持默认即可。

改完配置执行安装:

sudo ./install.sh

装完以后屏幕会输出一堆容器启动日志,最后有✔ ----Harbor has been installed and started----这种提示就说明成了。用docker ps看一下,正常会拉起 8 个左右的容器。如果其中有容器反复重启,大概率是某个组件初始化失败了,优先看docker logs对应容器名。

3.4 验证 Harbor 服务状态

打开浏览器访问http://192.168.209.133,你会看到 Harbor 的登录界面。用admin和你修改后的密码登录进去,左侧菜单能看到项目、仓库、日志管理、系统管理这些模块。

先创建一个测试项目,叫test,访问级别选“公开”或者“私有”都行。如果是团队内部使用,建议先设私有,通过配置镜像同步或者给成员分配账号来控制访问权限,比较可控。

到这里,Harbor 本身的搭建就算完成了。

4. 镜像推送与拉取实战:把第一个镜像怼上去

4.1 Docker daemon 配置 insecure-registries

这是整个 Harbor 使用流程中 80% 的人会翻车的地方。

Harbor 装好了,Web 界面也能打开了,但执行docker login 192.168.209.133的时候报:

Error response from daemon: Get "https://192.168.209.133/v2/": http: server gave HTTP response to HTTPS client

这个报错信息我太熟了。它的意思是:你告诉 Docker 要去登录一个 HTTPS 地址,但 Harbor 实际用的是 HTTP 协议,Docker 客户端不甘心,强制走了 HTTPS,于是被 Harbor 的 HTTP 服务无情拒绝了。

解决方式就是在/etc/docker/daemon.json里加上 insecure-registries 配置:

{ "insecure-registries": ["192.168.209.133"] }

然后重启 Docker:

sudo systemctl daemon-reload sudo systemctl restart docker

这一步的“为什么”值得说一下:Docker 出于安全考虑,默认只用 HTTPS 跟镜像仓库通信。你确定要用 HTTP 的话,必须显式地告诉它“这个 IP 我信任,允许它用 HTTP”。insecure-registries就是干这个的。它还有一个隐藏能力:如果你需要连接一个没有 DNS 记录的 IP 地址,可以写成192.168.209.133:8080这种带端口的形式,Docker 也认。

4.2 登录、打 tag、推送

配置好重启之后,先登录:

docker login 192.168.209.133

会让你输入用户名和密码,默认管理员是admin,密码就是你改过的那串。

本地准备一个测试镜像,没有的话拉一个小的:

docker pull alpine:latest docker tag alpine:latest 192.168.209.133/test/alpine:v1 docker push 192.168.209.133/test/alpine:v1

推完后打开 Web UI,进入test项目,在“仓库”页签就能看到test/alpine这个镜像,tag 是v1。

这里解释一下 tag 的构成规则:192.168.209.133/test/alpine:v1,第一个/之前的部分是仓库地址,接下来是项目名,再后面是镜像名,最后冒号是 tag。Harbor 没有“命名空间”这个概念,它用“项目”来隔离镜像。所以你在 tag 里写的test必须对应 Harbor 里已存在的项目,否则推送会直接失败,报错大概长这样:

denied: requested access to the resource is denied unauthorized to access repository: 192.168.209.133/test/alpine

我当时第一次踩这个坑的时候,半天没反应过来,还以为账号密码不对。其实是项目没创建。

4.3 其他机器怎么从 Harbor 拉镜像

如果你在第二台机器上拉取镜像,操作步骤和推送类似:

  1. 在这台机器上也要配置/etc/docker/daemon.json,加上"insecure-registries": ["192.168.209.133"],重启 Docker。
  2. 执行docker login 192.168.209.133登录。
  3. docker pull 192.168.209.133/test/alpine:v1。

如果这台机器业务上不允许重启 Docker(比如上面跑着一堆生产容器),会稍微麻烦一点,但也不是完全没救:你可以用docker login的 HTTP 方案做一些曲线操作,不过因为 docker daemon 全局共用一个 registry-mirrors 和 insecure-registries 配置,不改 daemon.json 基本绕不开,建议还是提前规划好维护窗口。

4.4 创建普通用户并分配项目权限

实际团队使用的时候,不应该全员共用 admin 账号,太危险。我建议你在 Harbor 的“系统管理”里创建一个普通用户,比如dev_user,然后在项目的“成员”里把它加进去,角色选“开发者”或者“维护者”。

这个做法的好处有两层:一是操作审计清晰,谁推了谁的镜像一查便知;二是权限可控,不是所有人都能手滑把正式镜像删了。Harbor 的角色权限大致是这样:管理员全权,维护者可以管理镜像和成员,开发者可以推送拉取镜像,访客只能拉取。

这里多分享一个细节:如果你在项目详情里勾选了“会自动创建机器人账号”,那么你就可以拿到一个形如robot$dev_user的账号,这个账号通常用于 CI/CD 流程,比如 Jenkins 构建完镜像后自动推送。机器人账号的好处是权限可以精确到单个项目,密码也是密钥形式的,比明文密码安全得多。

5. 常见问题与排查技巧实录

5.1 Harbor 推送失败 dial tcp 192.168.209.133: connect: connection refused

这类问题是搭建过程中遇得最多的。我的排查习惯是分三步走:

第一步,检查 Harbor 所在机器的监听端口:

ss -tnlp | grep 80

如果 80 端口没有监听,说明 Harbor 的 nginx 容器可能挂了。docker ps -a看容器状态,如果nginx容器反复重启,docker logs nginx看日志。我遇到过一种情况是 80 端口被宿主机上其他服务占了,Harbor 的 nginx 起不来。

第二步,检查客户端到服务器的网络连通性:

ping 192.168.209.133 curl http://192.168.209.133/api/v2.0/systeminfo

如果 ping 通但 curl 不通,要么是防火墙挡了,要么是 Harbor 服务没起来。CentOS 7 上执行systemctl stop firewalld可以临时关掉防火墙验证,但生产环境不建议这样干,建议用firewall-cmd --permanent --add-port=80/tcp放行端口。

第三步,检查 Docker 的 daemon.json 配置有没有生效:

docker info | grep -A 2 "Insecure Registries"

Insecure Registries下面应该能看到你的 Harbor 地址。如果没有,说明配置没加载成功,检查一下 JSON 格式是否合法。

5.2 x509: certificate signed by unknown authority

这个报错是 HTTPS 场景特有的。如果你按照我上面的 HTTP 方案来,不会遇到。但如果你确实配了 HTTPS,又在客户端上没有信任 Harbor 的 CA,就会看到这个。

解决方式是把 Harbor 的 CA 证书放到客户端的 Docker 证书目录:

sudo mkdir -p /etc/docker/certs.d/192.168.209.133 sudo cp harbor_ca.crt /etc/docker/certs.d/192.168.209.133/ca.crt sudo systemctl restart docker

注意目录名必须和镜像仓库地址完全一致。如果你用域名访问,目录名就要写成域名。

5.3 镜像下载慢的真正解法

在 2.3 节我讲了对 Docker Hub 镜像的加速方案,但如果你是从 Harbor 拉镜像慢,情况完全不一样。Harbor 和客户端都处于同一个局域网的话,瓶颈一般不在网络,而是硬盘 IO。Harbor 默认把镜像服务组件跑在容器里,数据存储在/data目录,如果/data在机械硬盘上,大规模并发读写时很容易成为瓶颈。

我的建议是:

  • 把data_volume挂载到 SSD 磁盘或独立分区上
  • 有条件的话用 RAID 或者分布式存储
  • 定期检查磁盘使用率,Harbor 本身不会帮你自动清理旧镜像,建议开启 Harbor 自带的“仓库回收”功能,它能把不再被引用到的镜像层从存储中清掉

5.4 容器重启后 Harbor 起不来的处理

服务器重启后,Harbor 容器有时候不会自动恢复,这在老版本里是个常见问题。处理方式是进入 Harbor 的安装目录,执行:

sudo docker-compose stop sudo docker-compose start

注意不是down,down会连带删除容器和网络配置,需要重新up -d。我一般直接使用docker-compose restart。如果你把 Harbor 装成了 systemd 服务(有的安装方式会创建),那直接systemctl start harbor也行。

这里说一个我踩过的坑:如果你执行docker-compose up -d反复失败,且错误信息指向端口冲突,别急着改端口,先确认是不是旧容器还没完全退出。用docker ps -a看到一堆 Exited 状态的容器,先docker rm清掉再说。

5.5 一个容易忽略的坑:时区和日志

Harbor 容器的默认时区是 UTC,查看镜像推送历史日志的时间戳,可能和你本地时间差 8 小时。这不影响使用,但排查问题的时候容易误导你。如果你在日志里看到某个镜像在“凌晨”推送,可能只是时区问题。想解决可以在 harbor.yml 里把时区设置为Asia/Shanghai,具体字段是timezone。

另外,Harbor 的日志文件会越攒越多,默认在/var/log/harbor目录,时间长了会占不少磁盘空间。建议配置 logrotate 定期清理,几十 GB 的日志堆积起来的时候再处理就晚了。

6. 我的几点使用体会和后续扩展建议

Harbor 搭完用起来之后,我的整体感受是:它确实是私有镜像仓库里“最不用操心”的解决方案。你可以把它当成一个内网 Docker Hub 来用,也可以利用项目隔离、机器人账号这些特性把镜像流转和 CI/CD 流程串起来。我后续在用的一个扩展操作是把镜像复制功能打开,让 Harbor 从一个外部镜像仓库同步镜像过来,这样团队里每个人都能在内网共享同一份镜像,而不需要各自去外网拉取,网络带宽和耗时都省下不少。

如果按我个人实际经验来说,搭 Harbor 本身不是难点,难点是后续的使用习惯和维护。比如项目命名规范要提前定好,不同环境(dev/staging/prod)要用不同项目隔离;tag 要带上版本号而不是一律打latest;清理策略要定时执行,不然镜像库越堆越大。等你把这些规范都立起来,这个私有镜像仓库才真正变成团队的“基础设施”。

还有一个我一直想提的小技巧:如果你总是忘记docker login,可以试试把登录凭证做成docker-credential-helpers的方式,或者干脆在 CI 里用机器人账号代替普通账号,这样既不依赖人工输入密码,也不需要担心凭证过期的问题。这些内容看起来像是“后面再说”的事,但在正式使用前面花十分钟配好,能省下后面无数次想骂人的排错时间。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询