很多人第一次搞私有镜像仓库,都觉得这事简单:docker run -p 5000:5000 registry,完事。可真到了团队里大伙儿一起用,或者要往生产环境推,问题就接踵而至——没有登录鉴权、没有用户管理、镜像说删就删、说不清谁在什么时候推了什么、连个可视化的界面都没有。这也是我当年从原生Registry迁移到Harbor的直接原因。Harbor是一个开源的云原生镜像仓库,基于Docker环境运行,自带Web控制台、基于角色的访问控制(RBAC)、镜像复制、漏洞扫描等能力,部署方式对Docker环境非常友好。这篇文章不是产品手册,而是把我从零开始下载离线包、改配置、跑安装脚本、接上Docker客户端、日常运维避坑的全过程记录下来。适合的对象包括:要给团队内部搭建镜像仓库的运维工程师,以及希望在自己服务器上搭一个私有仓库的独立开发者。
1. 先搞清楚一件事:Harbor到底比原生registry强在哪里
1.1 原生registry的痛
Docker官方那套registry镜像是所有私有仓库的基础,但如果你只在局域网里跑一台,很快就会发现几个挺难受的限制。第一,没有真正的用户体系,Harbor里每一个用户有单独的账号和密码,而原生registry要么整体开放,要么靠自己在nginx层拼一个非常简陋的基础认证,谈不上角色和权限。第二,没有Web界面,想看仓库里有哪些镜像、哪些tag、大小是多少,只能手动调用registry API,或者装一堆第三方浏览器工具。第三,没有什么审计能力,谁在什么时间推了什么、拉了什么,各凭本事,出了问题想追溯基本靠猜。
这些痛点在小规模、单机单人使用时勉强能忍,一旦变成开发、测试、生产多条流水线共用一个仓库,就会频繁出现“镜像被覆盖”“标签被乱删”“不知道自己该用哪个版本”这类问题,管理成本会立刻上来。更麻烦的是,新同事入职或者外部协作者要访问仓库时,原生registry根本没有一套好用的账号开通和权限分配流程。这也是大部分团队最终切换到Harbor的原因——它就是在registry之上做了一层完整的业务层,把仓库从一个存储工具变成了一个可治理的平台。
1.2 Harbor给你的不只是“一个仓库”
Harbor由很多组件构成,但部署后被整体封装成一组Docker容器:nginx负责统一的访问入口和TLS终结,core处理API和UI请求,registry就是镜像存储后端,jobservice跑复制和清理任务,portal是前端控制台,database保存元数据,redis做缓存。虽然本质上是“把一堆容器拉起来”,但对外看完全像一个单一应用,用户只需要记住一个访问地址。
相比原生registry,它有几个很实用的特性。支持多项目(Project)和精细的角色划分,可以做到只读者、开发者、管理员的权限区别;内置镜像复制(Replication),可以从另一个Harbor或标准Registry拉取同步,也可以推送到远端机房;支持漏洞扫描(配合Trivy)和不可变标签(immutable tag),大团队容易踩的“基础镜像被覆盖”这类坑,基本都在产品层面做了防护。还有操作日志、垃圾回收、配额管理,这些全是生产环境的刚需。
我不是说所有场景都得用Harbor,如果你只是个人电脑上自己倒腾镜像,原生registry加一个简单的UI工具确实够用。但当镜像要服务到一个团队、一个部门甚至一条完整的CI/CD链路时,Harbor的复杂度与它换来的管理能力相比,完全划算。下面进入正题,说安装。
2. 安装前必须定的几件事:版本、机器规格和证书策略
2.1 版本怎么选:2.x还是3.x,离线包还是在线包
先给结论:如今新装Harbor,不要再用1.x,直接从2.x的较新版本开始。Harbor 2.x 调整了组件架构,从依赖Docker Compose v1平滑过渡到支持v2,同时镜像格式升级为支持OCI标准,项目级策略也更丰富。我这次安装用的是2.11.x的离线包,和2.6、2.8这些中早期版本相比,配置方式和核心逻辑差别很小,上手成本很低。
安装包选择上,官方提供online和offline两种,区别在于离线包把Harbor自身需要的镜像和二进制全部打包好了,安装过程不依赖从外网拉取镜像,适合内网部署,也适合网络不稳的环境。我自己的习惯是无论有没有外网都下离线包,一是快,二是版本锁定,不会今天装和明天装的产物不一致。下载就到官方开源仓库的Release页面找harbor-offline-installer-v2.x.x.tgz即可,注意下载后先校验一下SHA256,避免文件损坏。这个校验虽然只多一步,但能省下后面排查“容器起不来”的大量时间。
2.2 机器与依赖检查清单
Harbor整体是一个多容器应用,虽然单机部署不夸张,但也不能拿太小的机器硬顶。我建议生产环境至少2核4G,镜像存储需求比较大的再往上加内存,因为数据库和redis是内存占用的大头。磁盘不只是看总容量,更要看data_volume所在分区的剩余空间,镜像仓库的容量增速往往比你预想得快。系统我用的是主流Linux发行版,内核版本不要太旧,Docker版本建议20.10及以上,同时准备好docker compose插件。Harbor的install.sh脚本会检测docker和docker compose,缺了就直接报错。
依赖环节最容易忽略的是基础工具:openssl用于生成证书、tar用于解包、curl用于接口测试,很多精简服务器镜像没有预装,安装前先补上。另外确认80端口没被nginx或其它Web服务占用,如果机器上已经跑了Web服务,后面配置时就要主动避开,否则大概率会撞端口。
2.3 证书策略:HTTPS还是HTTP,生产怎么选
这一步很多人会直接跳过,本地测试无所谓,但一旦面向团队甚至生产,我强烈建议把HTTPS搞定。Harbor官方安装脚本默认启动http,如果要配https,需要提前准备证书和私钥,并在harbor.yml里指定路径。
如果团队内部有CA体系,或者能拿到受信任的正式证书,直接用最省事。如果没有,就自己生成自签证书。自签证书的坑在于Docker客户端默认不信任私有CA,需要在每台要访问Harbor的机器上把CA证书放到系统信任目录,并在docker daemon的证书目录里单独配置。这块我放到后面第4章具体讲,这里先说结论:即使麻烦一点,也别为了“方便”长期裸跑HTTP。镜像内容里经常包含大量代码和配置信息,明文传一遍相当于把内部结构暴露给沿途的节点。测试环境想省事就先用HTTP,生产一定要上HTTPS,或者让统一网关来做TLS终止,总之不要让镜像流量裸奔。
3. 离线安装一条龙:从tar包到控制台
3.1 下载并解压离线安装包
下载完成后把安装包上传到目标机器的目标目录,比如/opt/harbor-install,然后解压。解压后目录里的核心文件是harbor.yml.tmpl和install.sh,名字带tmpl说明只是个模板,要先复制一份成harbor.yml再修改。注意解压时用tar -zxf,确认文件归属和权限没问题后再进入目录。
我第一次装的时候图省事,直接在原目录里改harbor.yml.tmpl,结果后面执行脚本时发现模板文件被改了,后续升级还会被覆盖,踩过一次坑。所以规范动作一定是cp harbor.yml.tmpl harbor.yml,所有自定义配置都写到harbor.yml里,这也是官方推荐的做法。
3.2 harbor.yml 配置逐项拆解
harbor.yml是安装配置的核心,我建议一行一行过一遍,不要只改hostname就完事。下面按关键项讲:
hostname:这个是你访问Harbor的地址,可以填域名也可以填IP,不要带http://前缀。之后Docker客户端要访问它,所以填的必须是客户端能解析到且能到达的地址,别填localhost,除非你只在同一台机器上用。如果后面要启用外部访问,hostname最好跟证书里的域名保持一致,否则TLS校验会报hostname mismatch。
http.port:默认80,如果80已经被占用,改成8080或其它未占用端口。改完之后记得所有访问地址都要带上新端口,包括docker login、页面URL、复制规则里的远端地址,漏一个就会让你误以为Harbor“挂了”。
https部分:默认注释掉,如果你有证书,取消注释并填写certificate和private_key的绝对路径。注意Harbor对证书文件的权限有要求,私钥不能太开放,否则nginx容器会反复重启,报权限错误。
harbor_admin_password:默认管理员密码是Harbor12345,第一次部署时就要改掉。这个密码字段只在首次安装时生效,后续改管理员密码要登录UI操作或走API,不能只改这个文件再重启,很多新手在这上面吃了亏。
database.password:这是Harbor内部数据库的root用户密码,默认值是root123,生产环境必须改,而且改了要妥善保管,因为后面备份恢复、升级都会用到它。
data_volume:所有持久化数据存放位置,包括镜像层、数据库文件、redis持久化数据。务必指向一个有独立空间的分区,比如/data/harbor。安排磁盘时就要想到,这个目录以后会膨胀,不要放在根分区里挤占系统空间。
log.level:默认info,排查问题想看得细就改成debug,但生产一般用info就好,debug会产生大量日志,日志文件轮转不及时可能反过来占满磁盘。
3.3 执行install.sh与配套组件开关
配置保存后直接执行./install.sh,只要依赖都齐,脚本会自动做几件事:检查环境、准备目录、启动依赖组件、拉起所有Harbor容器。整个过程可能会有几分钟,取决于磁盘写入速度,不要看到命令行长时间没输出就以为卡死了,可以另开一个终端看docker ps。
install.sh还支持一些可选参数,比如--with-trivy启用漏洞扫描,--with-chartmuseum提供Helm Chart仓库,--with-notary提供镜像签名。我的建议是:如果你用Helm部署应用,chartmuseum值得开;Trivy扫描在团队协作时很有用,但会多消耗不少内存;notary除非你确实有镜像签名合规要求,否则别开,维护成本大于收益。组件越多容器越多,占的内存和磁盘也就越大,不要为“都有”而开所有开关,以后每次升级多一组迁移步骤的滋味可不好受。
3.4 验证部署状态与首次登录
安装脚本执行完,第一件事是确认所有容器都处于healthy状态。用docker compose -f /opt/harbor-install/docker-compose.yml ps查看,或者直接docker ps。正常情况会有nginx、core、portal、registry、jobservice、db、redis这几个核心容器,开了trivy的话还能看到trivy相关任务。容器处于starting或restarting状态都说明配置有问题,不要急着登录页面。
容器全部healthy后,浏览器访问你配置好的地址,看到登录页就用admin和前面设置的密码登录。登录后第一建议先去“系统管理-项目”里创建自己的项目,或者调整自带library项目的访问级别,不要一上来就急着推镜像。UI显示正常,说明安装阶段基本通过了,接下来是让Docker客户端能顺畅访问它。
4. 把Docker客户端接进来:daemon.json、登录和推送
4.1 daemon.json改什么:insecure-registries和registry-mirrors不能搞混
部署好Harbor之后,你拿到一台开发机要推送镜像,如果是HTTP访问(比如http://192.168.1.100:80),Docker默认会拒绝连接,报错类似http: server gave HTTP response to HTTPS client。解决方法是让Docker把这个地址标记为不安全仓库,在/etc/docker/daemon.json里加一段:
{ "insecure-registries": ["192.168.1.100:80"] }注意这里有两个地方容易混:insecure-registries里写的是Harbor地址,registry-mirrors里写的是镜像加速器地址,两者完全不是一回事。我曾经看到同事把Harbor地址填到mirrors里,重启Docker后所有公开镜像拉取都走Harbor,效果自然不对,拉个nginx都可能因为仓库里没有而卡上半天。配置文件改完需要重启Docker进程:systemctl restart docker,老版本init系统就是service docker restart。重启前要评估影响,因为很多服务器Docker里还跑着业务容器,最好选维护窗口操作。
如果你配了HTTPS和自签证书,更推荐的方式是把自签CA放到/etc/docker/certs.d/<hostname>/ca.crt,这样Docker会把你Harbor当作可信节点。这个做法的好处是不用无条件信任整个网段,安全控制更精确,客户端机器上也只认这一台仓库。
4.2 docker login与tag、push的完整流程
配置完成后,先在命令行登录:docker login 192.168.1.100:80,输入你在Harbor里创建的用户名密码。注意登录成功后会生成一个登录文件存放在~/.docker/config.json,之后push/pull就不需要再输入密码。这里有的同学会疑惑,明明登录成功了,push时还是提示unauthorized,多半是因为镜像tag不符合格式规则,或者没有推送到你有权限的项目。
Harbor的完整镜像名称结构是:Harbor地址/项目名/镜像名:标签。比如你有一个nginx镜像要传到library项目下:
docker tag nginx:latest 192.168.1.100:80/library/nginx:v1 docker push 192.168.1.100:80/library/nginx:v1项目名的位置很关键,不是想当然地docker push 192.168.1.100:80/nginx:v1,如果那个项目不存在或者你没权限,就会遇到权限问题。拉取时也是一样,docker pull 192.168.1.100:80/library/nginx:v1。在Harbor UI里能看到这个镜像成功出现,说明整套链路已经通了。
4.3 界面操作:创建项目、设置策略、权限分配
客户端接入之后,Harbor真正的管理价值在UI里。登录进入控制台,左侧菜单能看到“项目”入口。新建项目时,名称规则要求和镜像名里的项目名一致,比如team-dev;访问级别建议默认私有,只有被授权的账号才可见可拉;镜像代理缓存、复制规则这些按需配置。
建完项目后进入“成员”页面,把开发、测试人员添加进来并分配角色。Harbor的角色默认有项目管理员、开发者、访客三种,对应不同权限集合。开发者可以push和pull,访客只能pull,项目管理员还能管理成员和配置。实际操作里我一般会把开发者权限给需要的同事,访客权限给下游部署服务器,最小化权限原则能减少很多误删误覆盖的事故。
另外,如果镜像会被多个项目共用,可以考虑library作为公共只读项目,大家统一从里面拉基础镜像,自己的项目再建私有项目单独存。这样既降低了重复推送基础镜像的浪费,也便于控制生产环境的镜像来源。
5. 长期运行避坑:端口冲突、磁盘占用与常见报错
5.1 端口冲突和502排查
Harbor安装阶段最常遇见的错误就是端口冲突。有一次我在一台已经跑着Nginx的服务器上装Harbor,默认80端口被占用,install.sh执行时组件能起来,但portal始终无法访问。当时第一反应是看容器状态,发现所有容器都在running,但页面就是打不开。
排查思路先用ss -lntp | grep 80确认端口被谁占用,再做修正:改harbor.yml里的http.port为8080,重新执行install.sh。但注意旧的容器和卷不会自动清理干净,会留下上次启动的残留。最干净的做法是先docker compose down,再重新部署,确保配置变更生效。把端口改成8080后页面立刻能访问。如果你是已启动Harbor后改配置,必须完整down再up,不能只restart一个容器,因为nginx入口在转发逻辑里会按旧的配置缓存。
502在Harbor里经常是core服务响应超时或数据库连接没起来。排查时看core日志:docker compose logs core,常见的如redis连不上、数据库密码不对,基本都在日志里写得很明确。这时候别急着重启所有容器,先看日志把根因定住,盲目重启只会让问题反复出现。
5.2 数据卷增长与GC清理
Harbor跑起来的第三四个月,最直观的问题就是磁盘看着看着就不够了。镜像层数据很占空间,尤其团队并行开发、频繁构建的场景,仓库里容易堆出一堆没人用的旧tag。Harbor自带垃圾回收(GC)功能,在UI“系统管理”入口能找到,作用是把已经被删除镜像标记、但还在磁盘占用的数据真正清理掉。
但GC不是灵丹妙药,它只回收已经被删除的镜像,不会自动清理那些“还留在仓库里但已经没人用”的旧tag。所以日常维护里要定期做两件事:一是要求团队成员删除无用tag,二是对长期不用的项目设置配额,阻止仓库无限膨胀。也可以在“系统管理-配额管理”里给每个项目设置存储上限,超过后push会被拒绝,避免几轮无节制测试把磁盘打满。
5.3 自签证书告警、时钟偏差等细节
自签证书场景下,Docker客户端偶尔会报x509 certificate signed by unknown authority,原因就是没把CA放入客户端信任目录。另一个比较隐蔽的坑是服务器时间偏差,Harbor在生成和校验token时强依赖时间戳,如果机器时钟差了几分钟,你会看到登录页能打开、但API反复401。这个问题的修复不是在Harbor里,而是用NTP/chrony同步系统时间,然后再重启Harbor容器。顺带一提,如果你是在虚拟机上做实验,宿主机休眠再唤醒后最容易出现这类时间漂移,我在这上面浪费过一个小时。
除了这些,还有一些小点值得注意:定期检查容器状态不要只靠眼睛,我习惯写一个简单的巡检脚本,定时把docker compose ps的输出发到工作通知群,能第一时间发现容器退化到unhealthy。日志目录默认在/var/log/harbor/下面,core、nginx、jobservice各自的日志分开存放,千万别用docker logs去替代,那样看到的内容不全也不会做滚动轮转。
6. 我最后想说的几个实践体会
6.1 配置决策比安装本身更重要
装了几次Harbor之后,我最大的体会是:这个项目真正的复杂度不在安装脚本,而在配置决策上。版本选对、磁盘规划好、证书策略想清楚、权限按最小化分配,后面基本是顺水推舟。反过来,如果一开始只图省事,把密码全用默认、HTTP裸跑、数据目录放根分区,三个月后你大概率要花比安装多十倍的时间去补课。
我见过一个团队,因为省略了数据库密码修改,后来数据库被外部扫描器直接连上,整个仓库元数据被清空,教训相当惨痛。虽然不能完全怪Harbor,但安装时顺手改掉默认密码的成本实在太低了,不值得冒这个险。
6.2 别把组件全家桶当成最优解
另一个体会是,别把所有可选的组件都打开。每次看到install.sh后面跟着一堆--with参数,都觉得功能越多越好,但每个组件都要占用内存、产生日志、增加升级时的迁移步骤。Harbor的核心价值是镜像管理和权限控制,其他能力按实际需要选择,而不是图“全家桶”。
我自己的项目里,长期只跑核心组件加一个Trivy扫描,镜像签名和Helm Chart仓库都没开,升级时省心很多。等以后确实需要了,再加一把开关重新install一次也不麻烦。
6.3 升级迁移前备份是你的保命底牌
最后分享一个小技巧:在做升级或迁移之前,永远先备份。Harbor的备份不需要太复杂,harbor.yml、数据库导出、data_volume完整拷贝,这三样是关键。我自己的习惯是升级前先搞一份完整备份到另一台机器或移动硬盘,升级失败还能退回旧版本,升级成功后再把旧备份归档而不是删除,留一个月。真出问题的时候,这份备份就是你敢继续操作底气的来源。
备份完做一次恢复演练,确认数据真的能带回来,而不是备份完就以为万事大吉。很多人直到丢失数据的那天才发现自己的备份是坏的,那种心情我不想你再体验一次。