ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

Nexus与Harbor自建Docker镜像仓库选型与部署实践

Nexus与Harbor自建Docker镜像仓库选型与部署实践

整个Docker应用体系里,镜像仓库是个平时不起眼、坏一次就耽误上线的角色。很多团队最开始用公共镜像站,或者直接在服务器上跑一个registry容器,等镜像数量一旦超过一两百个,权限、磁盘占用、镜像垃圾这些问题就全冒出来了。这篇文章我打算把自建镜像仓库的两套主流方案——Nexus和Harbor,从选型、部署到常见故障,完整走一遍。适合已经能跑通Docker基础命令、想把镜像统一管起来的开发者或运维同学,看完可以直接照着搭一套自己的仓库。

1. 自建镜像仓库:先弄清楚要解决什么问题

1.1 镜像仓库在Docker体系里的位置

Docker的核心模型是“镜像—容器—仓库”,镜像打包好了总得有个地方存,存的地方就是仓库。公共镜像站速度不稳定,而且构建产物大多涉及内部代码,往外面推本身就不合适。自建仓库本质上是在自己的服务器上提供一个类似公共仓库的服务,让相关环境都能快速拉取镜像。

镜像仓库的价值不只是“存文件”。Docker镜像由多层组成,仓库需要存储这些层,还要处理索引、签名、权限校验。如果只用一个裸registry,这些事也能做,但用户管理、界面查看、镜像清理都不方便。Nexus和Harbor恰恰是把这些能力补齐了,才成为自建仓库的主流选择。

1.2 Registry、Nexus、Harbor三套方案怎么选

做镜像仓库,市面上常见的方案大概有三条路:

第一种是官方registry镜像,一个容器就能跑起来,配置非常简单,适合临时测试或者规模很小的场景。缺点是没有界面、没有用户体系,镜像一多就难维护。第二种是Nexus,它不只是镜像仓库,还能统一管npm、Maven、PyPI这些包,是一个多功能的制品仓库。第三种是Harbor,定位更聚焦,专门做容器镜像仓库,内置了RBAC权限、漏洞扫描、镜像复制这些能力。

我统计过常见的使用场景:团队如果只做容器交付,Harbor会顺手很多;团队里同时有Java、前端、Python项目,Nexus一个仓库全管了,维护成本更低。两者不是替代关系,更多看团队的技术栈和运维人力。

1.3 我最终确定的部署规划

我在实际环境里把两套方案都部署过,给的建议是:先用Nexus跑通基础镜像流转,因为它的安装包简单、依赖少,遇到问题更容易定位。等需要回收空间、细分权限、搞多环境同步时,再上Harbor。

规划时重点考虑三点:存储盘要独立挂载到大容量磁盘,不能放在系统分区;数据目录要分开管理,方便备份;证书方案尽量提前想好,内网环境用IP加端口最省事,如果有域名再配置正规证书。下文所有步骤都是基于这套规划进行的。

2. 动手前的基础准备

2.1 服务器资源与目录规划

自建镜像仓库对服务器的要求不算高,但也不能太寒酸。我实际测试下来,Nexus启动默认要预留1GB左右的堆内存,Harbor会跑多个容器,整体内存建议直接给到8GB,磁盘按镜像增长速度预留,至少500GB起步,有条件直接挂NVMe盘。

目录规划我习惯分成两层:程序目录和应用数据分离。Nexus解压到/opt/nexus,数据目录设置为/data/nexus-data;Harbor安装到/opt/harbor,数据卷指向/data/harbor。这样重装、迁移、备份都清晰。别图省事把数据放在默认路径,系统盘一旦写满,整个机器都卡。

2.2 客户端Docker配置:insecure-registries与证书

这是新手最容易踩的第一步。Docker客户端默认走HTTPS,如果私有仓库没配证书,登录时会报错“http: server gave HTTP response to HTTPS client”。两个解决办法:一个是配置正规证书,另一个是把私有仓库地址加入insecure-registries。

内网环境我建议先用insecure-registries,省掉证书运维成本。在/etc/docker/daemon.json里加上地址,然后重启Docker:

# daemon.json { "insecure-registries": ["192.168.1.10:5000", "192.168.1.10:8443"] }
sudo systemctl restart docker

需要注意,insecure-registries只能填IP或域名加端口,不能写路径。修改后最好用docker info看一下有没有加载成功,别急着去登录。

2.3 确认端口与防火墙策略

仓库搭好后,客户端要通过特定端口连接,这些端口在部署前就要想清楚。Nexus的Web管理端口是8081,Docker仓库如果是group仓库,需要单独一个端口,比如5000。Harbor默认使用80提供HTTP服务,UI和Registry走同一个入口,如果80被占用,就在配置里改为其他端口,比如8082。

防火墙方面,要确保客户端能访问相应端口。很多部署失败不是程序问题,而是防火墙拦了。先用telnet或者nc测一下端口通不通,再排查仓库服务本身。这个顺序能节省大量排错时间。

3. Nexus3搭建Docker私有仓库全过程

3.1 下载、解压与基础运行配置

Nexus3的安装包是tar.gz格式,解压就能用。下载完成后先把包传到服务器,例如放到/opt目录下,然后解压:

cd /opt tar -zxvf nexus-3.*-unix.tar.gz mv nexus-3.* nexus

解压后注意目录结构,里面默认带了JDK环境,不需要单独安装Java。Nexus在启动时会自动创建一个数据目录,默认是解压目录下的sonatype-work,我习惯通过修改nexus.vmoptions或者环境变量把它改到独立数据盘,避免和程序目录混在一起。

内存配置也很关键。默认的JVM参数可能比较保守,镜像数量多了以后,Nexus做sha256计算和索引会吃力。我建议在nexus.vmoptions里把-Xmx设到2GB以上,小团队规模用4GB更稳妥。改完记得重启服务。

/opt/nexus/bin/nexus start

启动后用浏览器访问http://服务器IP:8081。首次打开页面右上角的管理员账号密码在/opt/nexus/sonatype-work/admin.password文件里,登录后第一步是改密码并配置匿名访问。

3.2 创建Docker仓库:hosted、proxy、group

Nexus的仓库类型分为hosted、proxy和group三种,用Docker仓库时这三类的配合很有意思。hosted是本地存储的私有仓库,推送的镜像都存在这里;proxy是代理仓库,可以缓存上游镜像源,比如公共镜像站;group是组合仓库,把多个仓库聚合在一起,让客户端只面对一个地址。

我的实际规划是:创建一个hosted仓库作为私有镜像存储端,创建group仓库作为统一入口。group里把hosted和proxy都放进去,这样客户端通过一个端口既能推也能拉,推的是私有镜像,拉的时候本地没有就走代理缓存。操作路径是登录Nexus后进入Administration -> Repository -> Create repository,选择docker (hosted)或docker (group)。创建hosted时要勾选HTTP connector并指定端口,比如5001;创建group时指定统一入口端口,比如5000。

这里有个细节,Nexus默认的Docker仓库在未配置时是不允许匿名访问的,后续需要在Realms里启用Docker Bearer Token Realm,否则登录后推镜像会出现认证相关的问题。这个配置项在管理后台的Security -> Realms下。

3.3 Docker客户端登录与镜像推送

仓库创建好之后,客户端首先要把仓库地址加入insecure-registries(如果没配HTTPS),然后执行docker login:

docker login 192.168.1.10:5000

输入Nexus里配置的账号密码,登录成功后再给镜像打标签推送:

docker tag myapp:v1 192.168.1.10:5000/myapp:v1 docker push 192.168.1.10:5000/myapp:v1

推送成功后,在Nexus的仓库浏览里就能看到镜像和对应的标签。这个过程如果出现405或格式错误,多半是仓库创建时没有勾选Docker API相关选项,回去检查仓库配置里的connector和服务端口就行。我在第一次操作时就栽在端口上,创建仓库时没注意端口被占用,登录一直失败。

3.4 Nexus日常维护的注意点

Nexus跑的时间长了,有两个问题一定会遇到。第一个是匿名访问要不要开。对内网测试环境,开匿名访问确实方便,但稍有规模后就该关掉,所有账号通过权限体系统一管理。第二个是磁盘清理,Nexus的Docker镜像不会自动清理,旧的、无用的镜像标签会一直占用空间。

清理Nexus磁盘的办法不是直接删文件,那样会导致索引不一致。需要进入Nexus的管理界面,通过维护任务创建定期删除未被引用的Docker镜像任务,或者借助API手动清理。另外,升级Nexus前一定要备份sonatype-work目录,我在一次版本升级时丢过仓库索引,直接导致客户端拉取历史镜像失败,教训比较深刻。

4. Harbor搭建Docker镜像仓库实操

4.1 获取离线安装包与基础环境检查

Harbor的部署方式和Nexus不太一样,它本身是一个容器化的应用,安装包里带了一堆镜像和docker compose配置,所以对环境有额外要求。先把离线安装包下载下来,传到服务器上并解压:

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

解压后目录里会有harbor.yml、install.sh和若干镜像压缩包。在跑install.sh之前,先确认机器上装了docker compose插件。新版Harbor要求docker compose v2以上,检查方式是执行docker compose version,如果没有就安装一下。Harbor会把所有组件以容器方式跑起来,包括数据库、Redis、核心服务、注册表服务等,所以内存一定给够,否则容器会反复重启。

4.2 配置harbor.yml并生成部署文件

harbor.yml是Harbor安装的唯一入口配置,所有关键参数都在里面改,然后通过脚本生成部署使用的环境文件。常见的必改项包括:hostname、http端口、管理员初始密码、数据目录、数据库密码等。

hostname要注意改成客户端能解析到的地址,用IP就写IP,用域名就写域名。如果不想用80端口,把http下的port改成其他端口即可。数据目录在data_volume字段里指定,建议改成独立数据盘,比如/data/harbor。

数据库密码默认是配置好的,但生产环境一定要改掉,否则等于把仓库的访问凭证敞开了。配置示例大致是这样:

hostname: 192.168.1.10 http: port: 8082 harbor_admin_password: 此处输入一个强密码 database: password: 此处输入数据库密码 data_volume: /data/harbor

改完配置后,运行prepare脚本检查配置,之后install.sh会真正拉起容器。新手常见的坑是hostname写错或者http端口被占用,prepare阶段就会报错,这时改掉配置重新prepare即可。

4.3 使用install.sh完成部署与验证

配置无误后执行:

./install.sh

脚本会提示缺少的依赖,比如docker compose插件,并且输出Harbor内部组件的启动日志。整个流程大概几分钟,看到各项服务是healthy状态,部署就基本完成。

之后打开浏览器访问http://服务器IP:8082,用admin和harbor.yml里配置的初始密码登录。第一次登录后建议马上在用户管理里创建新用户,不要所有操作都拿admin做,避免权限混乱。登录进去后能看到总览、项目、仓库、日志等菜单,这些是Nexus没有的一站式体验。

客户端推镜像的流程和Nexus类似,先登录,然后打标签推送:

docker login 192.168.1.10:8082 docker tag myapp:v1 192.168.1.10:8082/projectname/myapp:v1 docker push 192.168.1.10:8082/projectname/myapp:v1

注意Harbor推送到项目下,路径里必须带项目名。项目可以是公开的,也可以是私有的,公开项目允许匿名拉取,私有项目必须登录且有权限。这个节奏和Nexus的hosted仓库逻辑一样,但Harbor的项目概念更接近用户平时使用的公共镜像站规则。

4.4 Harbor项目权限、复制与垃圾回收

Harbor比Nexus更贴合容器交付场景的功能,主要是项目权限、复制管理和垃圾回收。项目权限能控制谁能推送、谁能拉取,还能在项目内设置成员角色,这一点在多人团队里很关键。

复制管理是用来实现多环境同步的。比如部署环境有两套,一套测试一套正式,可以在Harbor向导里创建复制规则,把测试仓库的镜像复制到正式仓库,不用再手动迁移文件。这样能保证两套环境下跑的镜像内容一致。

垃圾回收是Harbor解决磁盘占用的重要手段。镜像推送时会形成大量无用的中间层,在Harbor界面里找到垃圾回收,勾选“删除未使用的镜像”,就能释放磁盘空间。这个功能可以设置定时任务,我通常设成每月执行一次,执行前看下磁盘用量,防止空间突然不够。

5. Nexus与Harbor对比:不同场景下的选型参考

5.1 功能与使用体验差异

Nexus和Harbor在镜像仓库这个功能上都能满足基本需求,但使用体验差异比较大。Nexus的管理界面是通用的制品仓库风格,Docker功能只是其中一个模块,功能多,但面向镜像的操作相对分散。Harbor的界面则完全围绕镜像和项目设计,项目创建、成员管理、复制任务、垃圾回收一目了然。

权限方面,Nexus用的是仓库级别加权限域,Harbor用的是项目级别加角色,更容易对应到团队里的不同人员。如果团队里不只是开发,还有测试和运维一起用仓库,Harbor的体验更清晰。

5.2 资源占用与运维成本

资源占用上,Nexus单实例、单容器,占用的内存大约是2GB起步,运维简单,升级包一个tar.gz就搞定。Harbor是完整的多容器系统,跑起来至少五六个容器,资源占用明显更高,运维时需要熟悉日志和容器状态排查。

运维成本的对比也体现在升级上。Nexus没有数据库,数据文件一套目录,备份只要拷贝目录;Harbor依赖数据库和Redis,备份需要同时处理文件和数据库,复杂度高了不止一档。为了安全备份,我在Harbor部署环境里会周期性导出一份数据库。这一对比下来,Nexus很适合小团队先把仓库用起来,Harbor适合有专人维护、对权限和同步要求更高的环境。

5.3 从“够用”到“好用”的演进路径

我的建议不是二选一,而是根据阶段演进。一开始镜像量少,用Nexus把统一入口跑通,顺便管理npm、Maven包;等镜像超过几百个、团队协作变复杂,再引入Harbor作为容器交付的核心平台。

两种仓库可以并存,Nexus照旧作为通用制品仓库,Harbor承担容器镜像分发。很多团队都是这个路线,并不是用了Harbor就立刻拆掉Nexus。基础设施建设的稳妥做法是平滑迁移,而不是一次性推倒重来。

6. 常见问题与排查记录

6.1 登录与推送阶段的报错处理

docker login私有仓库时,最常见的错误是“http: server gave HTTP response to HTTPS client”。原因基本就是客户端没配置insecure-registries或者服务端没启用HTTPS。解决办法按前文配置insecure-registries,然后重启Docker再登录。这里有个小细节,重启Docker后运行中的容器不受影响,但新操作会加载新配置,等几秒再试。

推送镜像时报错“no basic auth credentials”,代表没有登录成功,多半是账号密码错误或者没执行docker login。执行docker login时输入密码不回显,这是正常现象,不要以为是卡住。我遇到过几次因为键盘输入法切到中文导致密码打错,登录一直失败,这个细节提醒一下。

镜像推送时报405或者manifest错误,常见于Nexus仓库创建时没有正确启用Docker V2 API。检查仓库配置里的Docker connector端口是否绑定成功,重新创建仓库或调整端口,再推送一次。

6.2 Nexus与Harbor部署期故障

Nexus启动失败可以看/opt/nexus/sonatype-work/nexus3/log目录下的日志。内存配置改过但没生效,多半是修改的vmoptions文件路径不对。端口占用也是高频问题,8081被别的服务占住,启动时会报Address already in use,用netstat -tlnp查看占用进程解决。

Harbor部署失败的常见原因,一是prepare阶段提示配置错误,二是install脚本找不到docker compose命令。前者检查harbor.yml的hostname和端口是否冲突,后者确认docker compose插件装在系统PATH里。Harbor容器起来后接口无法访问,先用docker compose ps看看每个服务状态,如果是 unhealthy,基本是数据库或Redis启动失败,去容器日志里定位原因。

6.3 磁盘增长与性能问题

仓库运行久了磁盘肯定涨,这是镜像层堆积的结果。Nexus要定期清理未引用的Docker镜像,Harbor则用垃圾回收功能释放空间。清理前确认没有构建任务还在跑,否则可能出现拉取失败。

另外,Nexus在高并发推送时会占满JVM堆内存,表现为页面卡死、推送超时。我给的建议是监控数据目录大小和JVM内存使用,磁盘路径尽量单独挂载,不能和系统日志混用。Harbor则要注意数据库磁盘空间,数据库撑满整个仓库接口就会罢工,这种情况我遇到一次,最后只能扩容数据盘恢复。

搭建自建镜像仓库这件事,部署成功只是开始。我个人在实际操作中的体会是,把权限、存储、备份这三件套提前规划好,比折腾任何高级功能都有用。最后分享一个小经验:不管用Nexus还是Harbor,第一次搭好后,打开浏览器翻一遍配置页面,把所有默认密码改掉,再创建一个普通权限的测试账号走一遍登录、推送、拉取的完整流程,这套动作做完,仓库才算真正落地。

RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进