☰
Harbor + Rancher 私有化离线部署:从镜像管理到应用发布的完整实践
2026/10/22 3:22:42 网站建设 项目流程

前阵子接手一个部署任务,客户那边要求整套应用环境放在内网,不连外网,也没有任何现成的镜像加速渠道。起初我以为无非是装个 Kubernetes 集群然后硬灌 Helm Chart,真正开始做才发现,镜像从哪来、谁来管镜像版本、推到哪个仓库、谁去拉取、生产测试环境怎么隔离,这些问题比装集群本身更让人头疼。后来我把Harbor + Rancher这套组合用起来,才算是把私有化场景下应用发布的整条链路彻底捋顺了。

这篇内容主要写给正在做私有化交付、或者在隔离网络里部署应用的团队。如果你只是在自己电脑上跑 Docker 玩,可能用不上;但如果是给客户做项目落地、做信创环境适配,或者公司内部要搭一套完全离线的研发测试发布体系,那这套东西值得认真看一遍。

1. 为什么私有化发布绕不开 Harbor 和 Rancher 这套组合

先聊个根本问题:私有化场景和我们在公网开发环境里最大的区别是什么?不是“不能上网”,而是“一切操作都要在闭环里完成”。公网环境下镜像随便从 Docker Hub 拉,Helm Chart 也可以直接在线安装;到了内网环境,这些全都不可用。更麻烦的是,私有化项目的交付对象往往是客户指定的服务器,对方对数据安全、运维边界、权限管控都有自己的要求,你不可能在人家环境里随便搭个公共仓库就完事。

1.1 镜像仓库是私有化的“物流中心”

容器化的本质是把应用及其依赖打包成镜像,然后分发到任意目标机器上运行。如果没有私有镜像仓库,这一步就卡死了。很多团队刚开始做私有化时,习惯用 Docker Registry 顶一下,但实际操作一段时间就会发现,裸 Registry 只能解决“能推送、能拉取”这个最基础的问题,一旦涉及多环境管理、镜像版本清理、跨机房同步、账号权限分配,它几乎帮不上忙。

Harbor 在这个环节扮演的角色,相当于整个发布链路的物流中心。它提供项目级别的隔离,不同业务线用不同项目空间,公开项目随便拉,私有项目必须带凭证;它提供镜像复制功能,可以把一个环境的镜像自动同步到另一个环境的仓库;它还提供镜像清理和保留策略,解决磁盘被历史版本堆满的问题。这些都是裸 Registry 给不了的。

1.2 Rancher 解决的是“集群怎么被业务使用”的问题

有了镜像仓库,下一步就是怎么把镜像变成跑起来的应用。Kubernetes 本身不做镜像管理,它只管调度。但现实中的业务团队,尤其是非专业运维背景的人,直接操作 Kubernetes 的 YAML 是有一道认知门槛的。更麻烦的是,私有化项目往往不止一套环境,客户环境、测试环境、开发环境各有一到多个集群,每个集群的版本、证书、节点状态都需要维护。

Rancher 的作用是把多集群管理做了收敛。它有一个控制面叫 Rancher Server(管理集群),下面挂着若干个下游集群,不管是已有的 K8s 集群还是 Rancher 自己装的集群,都能统一纳管。业务团队在 Rancher 界面上划项目、建命名空间、部署工作负载,不需要直接面对 kubectl 和复杂的 YAML,这对私有化交付时培养客户自己的人员上手也很有帮助。

1.3 为什么不用其他替代组合

做技术选型时我也评估过一些替代方案。比如纯 K8s + GitLab CI/CD,这套在研发环境跑得很顺,但私有化现场往往没有现成的 CI/CD 基础设施,而且客户对 GitLab 这种东西可能根本没有使用习惯;再比如轻量的 K3s 加裸 Registry,部署是简单,但镜像同步、权限审计、多项目隔离很快就暴露问题,最后要么变成靠手工打包搬运镜像,要么到处开 SSH 拷文件,完全脱离了“发布应用”这个目标。

Harbor + Rancher 的组合优势在于,两个项目都是开源界运维生态里被反复验证过的组件,能够覆盖镜像存储、镜像分发、集群管理、应用编排这几个核心环节,而且在国内环境下的资料和经验沉淀也很充分,遇到问题基本都能找到对应的解决路径。就私有化场景而言,这套组合是最不容易走偏的基线方案。

2. Harbor 侧的镜像管理:项目空间、同步策略与清理机制

Harbor 部署好之后,真正决定一个私有化项目好不好用的,是镜像管理策略。很多团队只是把 Harbor 装完就不再管了,结果镜像越来越乱、仓库越来越满、权限越来越模糊,最后连哪个镜像是哪个环境的哪个版本都说不清。

2.1 安装形态和存储选型

Harbor 的部署方式,官方提供在线安装和离线安装两种。私有化场景下基本必须用离线安装包,因为目标环境大概率没有外网。我建议安装之前先确定几个参数:Harbor 对外暴露的域名(比如 registry.internal.example.com)、访问协议(HTTP 还是 HTTPS)、数据目录挂载位置。

数据目录我一般单独分一个大容量磁盘,至少按预期镜像总量的两三倍预留空间。启动时通过docker run或 docker-compose 把/data目录挂出来,这样后续升级 Harbor 镜像版本不会影响已有数据。Harbor 后端依赖 PostgreSQL、Redis 和对象存储(默认就用文件系统),生产环境别把这些组件装在容器里就算完,至少给 PostgreSQL 做个定期备份,否则镜像数据出问题时恢复成本极高。

2.2 项目空间怎么划分最合理

Harbor 中的核心隔离单位是“项目(Project)”,不是“仓库(Repository)”。每个项目下面可以包含多个镜像仓库,比如项目erp-system下面有frontend、backend-a、backend-b这些仓库。

私有化场景下,我见过有两种常见的划分思路。一种是用环境来分项目,比如test、prod,另一种是用业务系统来分项目,比如crm、erp。我用下来更推荐按业务系统划分,因为同一个业务系统的不同镜像往往在发布时是一起更新的,放在同一个项目里便于权限统一授予;不同环境之间的区分可以通过镜像仓库地址的二级域名加镜像 Tag 来解决,比如/erp-system/backend:1.4.2-prod和/erp-system/backend:1.4.2-test。

项目的访问权限,建议默认全部设为私有。很多人图省事把项目设成公开,这在公网开发环境问题不大,但在私有化环境等于把镜像凭据直接送人了。如果某个基础镜像确实希望所有项目都能拉取,再单独建一个base-images公开项目,集中管理。

2.3 用 Robot 账号替代真实用户密码

Harbor 自 2.0 开始原生支持机器人账号(Robot Account),这个功能在自动化发布时非常重要。所谓 Robot 账号,本质上是一对专用的访问凭据,可以绑定到指定项目,并限制只有拉取(Pull)权限或推送(Push)权限。

我实际使用中,给持续集成阶段用的账号开的是推送权限,给 Rancher 运行时拉镜像用的账号只开拉取权限,必要时限制到某个具体仓库。这样即使一个账号泄露,影响范围也被限制在一个项目内,而且可以从 Harbor 的审计日志里定位到是哪个账号、什么时间做了什么操作。

2.4 镜像复制:从测试环境同步到生产环境的自动化机制

私有化项目最常见的网络拓扑,是多个各自隔离的机房。测试环境在一个机房,生产环境在另一个机房,网络互不相通。这种情况怎么把镜像送过去?不是让人一台台机器去拷文件,而是用 Harbor 的复制规则(Replication Rule)。

复制规则有两种模式:Push-based 和 Pull-based。Push 模式适合源仓库主动向目标仓库推送,Pull 模式适合目标仓库主动从源仓库拉取。我实际偏向用 Pull 模式,因为生产环境的网络出网策略通常更严格,让生产侧主动拉取,更容易在防火墙侧放行,而且生产仓库作为拉取方,管理更主动。

配置复制规则时,要填写远端 Harbor 的地址、访问凭据和项目名称,再设置触发方式。触发方式我建议用“推送镜像事件”而不是“定时轮询”,这样源项目推入新镜像后,复制任务会立刻执行,阶段状态能实时看到。如果同步量大,还可以参考 Harbor 的分布式部署方案,在边缘机房部署 endpoint 节点,但一般项目用单 Harbor + 复制规则就足够了。

2.5 清理与保留策略、配额管理

镜像仓库最怕的一件事,就是存储被不起眼的历史镜像堆满。K8s 滚动发布每发布一次就生成一个新 Tag,而旧镜像如果不打新 Tag,只会留在仓库里“占着茅坑”。Harbor 的镜像清理策略可以设置两条规则:按保留数量清理,比如每个仓库保留最近 20 个 Tag;按时间清理,比如删除超过 90 天的未使用镜像。注意清理规则是定期执行的,要先手动触发一次看看清理计划选中的镜像是否符合预期,再放开定时执行,避免误删。

在 Harbor 2.5 之后的版本里,项目可以设置存储配额(Quota),一旦超过阈值,推送会被拒绝。这个功能建议开起来,不然磁盘满了之后 Harbor 会进入半宕机状态,推镜像时报 500,拉镜像时也报错,排查起来特别被动。

3. Rancher 侧的集群组织:项目、命名空间与私有仓库的信任问题

Harbor 管好了镜像,接下来就是 Rancher 怎么把镜像真正拉到集群里并跑起来。这个环节最大的坑不是部署容器本身,而是集群如何信任私有仓库、如何在运行时拿到拉取镜像的凭据。

3.1 项目与命名空间的关系

很多刚接触 Rancher 的人搞不清“项目”和“命名空间”的区别。Kubernetes 的命名空间是硬件级别的隔离单位,而 Rancher 的“项目”是命名空间的分组管理单位。你可以把 Rancher 项目看作一个资源池,项目里面有若干命名空间,可以统一设置资源配额、绑定成员角色。

私有化部署时,我习惯按业务线来建项目。比如“客户A环境”作为一个项目,项目下面再拆分命名空间:erp-backend、erp-frontend、crm-web。这样做的好处是,如果要为一个业务线整体调整资源额度,直接在项目层面改就行了,不需要逐个命名空间操作;成员权限也按项目来分配,运维团队看整个项目,某个业务负责人只看自己那部分命名空间。

3.2 私有仓库证书:三个层面必须同时打通

私有化环境里,Harbor 几乎必然使用自签名证书,因为客户不会为内网域名申请公网证书。而 Rancher 往集群里拉镜像,涉及三个不同系统对证书的信任判断,任何一个没打通,都会出现镜像拉取失败。

第一层是容器运行时的私信任。如果节点用的是 Docker,需要在/etc/docker/daemon.json里配置insecure-registries或registry-mirrors。如果节点用的是 containerd(例如 Rancher 自己建的 RKE2 或 K3s 集群),则要修改/etc/rancher/rke2/config.yaml或 K3s 的 registries 配置,声明对私有仓库地址如何处理。

第二层是 Rancher 界面上的集群级私有仓库配置。在 Rancher 的集群设置里,有一个“集群注册(Cluster Registration)”相关的配置,实际是通过添加“镜像仓库凭据(Registry Pull Secret)”的方式,让 Rancher 在部署工作负载时自动注入拉取凭据,而不需要每个负载都手动创建 Secret。这个配置在新建集群时最好不要跳过,不然后期每部署一个应用都要单独配一遍。

第三层是应用自身的 imagePullSecret。即便 Rancher 集群级配置了仓库地址,业务负载所在的命名空间也必须有对应的 Secret 对象,才能让 Kubelet 发起认证。可以手动创建,也可以用 Rancher 的“资源 -> 密钥”功能自动生成。

3.3 用项目级的 Pull Secret 管理跨命名的拉取权限

实际操作中,我很少让业务团队手动去创建 imagePullSecret,因为很容易填错用户名密码或者选错命名空间。更省心的做法是在每个项目下创建一个只读 Robot 账号对应的 Secret,然后把 Secret 复制到项目内的所有相关命名空间。

具体步骤大概是:先在 Harbor 里建一个只有 Pull 权限的 Robot 账号,拿到对应的 Docker config 内容;然后到 Rancher 里用“Import”的方式把它作为一个 Secret 导入;之后在工作负载的镜像设置里选择这个 Secret。这样业务人员即使完全不懂镜像仓库的账号体系,也能正常完成部署。

3.4 离线环境的 Rancher 应用商店

Rancher 自带的应用商店功能,默认源指向公网仓库。私有化环境里要做离线安装,就需要自己搭建一个本地 Chart 仓库。最省事的方式是用 Harbor 的项目空间来托管 Helm Chart,然后把它挂到 Rancher 的应用商店里。

建好之后,Rancher 里选“工具 -> 应用商店 -> 修改配置”,把默认的全部替代为本地仓库地址,并关闭公网 Catalog 的更新,确保集群鼻息不会尝试外联。这样在后续交付新业务时,只要把 Chart 推到 Harbor 的 Helm 项目下,Rancher 界面上的应用商店就能直接看到并安装,整套流程完全闭环。

4. 一版应用从镜像到落地的完整发布链路

有了前面这些基础,现在可以跑通一版真实应用从构建到发布的完整流程。我以“某跨平台系统”的后端服务为例,展示从本地推送镜像到 Rancher 上完成滚动发布的全部过程。

4.1 镜像命名与 Tag 规范

私有化发布里,镜像的命名和 Tag 规范最好提前定下来,不然到后期每次发布都要临时扯皮。我目前在用的规范是:

  • 仓库路径格式:<Harbor地址>/<项目名>/<服务名>
  • Tag 格式:<版本号>-<环境标识>或者直接使用构建产物号(比如1.4.2配12345)

对于生产环境,我强烈建议不要在 Tag 里只写latest。一旦镜像latest被多次覆盖,想回滚到上一次发布的准确版本几乎不可能,光靠时间戳反推太痛苦。线上我就吃过这个亏,后来改成版本号固定后,回滚一键搞定。

4.2 推镜像到 Harbor

开发机构建好镜像后,登录 Harbor 再推送。

docker login registry.internal.example.com -u robot$erp-push -p <password> docker tag erp-backend:1.4.2 registry.internal.example.com/erp-system/backend:1.4.2 docker push registry.internal.example.com/erp-system/backend:1.4.2

推送完成后,在 Harbor 的 Tag 列表里确认一下 digest 值也是对的,别只看 Tag 名称。很多时候同一个版本号会被反复覆盖,正确的 digest 才是唯一确定镜像内容的标识,Rancher 里滚动更新也应该关注 digest 是否变化。

4.3 在 Rancher 中创建工作负载

进入 Rancher 的目标集群,切到对应的项目/命名空间,然后选择“工作负载 -> 创建”。

关键配置有以下几个:

  • 镜像地址填 Harbor 里的完整地址,包括 Tag
  • 镜像拉取策略建议设为IfNotPresent或Always,根据发布节奏决定;如果调试频繁改 Tag,用Always更省心
  • 拉取凭据选择前面加到项目里的 Robot 账号 Secret
  • 资源请求与限额,务必填写,否则某个服务可能会把节点内存吃满
  • 健康检查建议配一个是 TCP/HTTP 探针,Rancher 的标签页可以直接配置 Readiness 和 Liveness,不要跳过

创建之后,Rancher 会生成对应的 Deployment。第一次部署失败最常见的三类原因是:镜像拉取凭据不对(ImagePullBackOff)、资源配额不足(Unschedulable)、探针配置不当导致容器反复重启。

4.4 滚动升级与回滚

在线升级时,我通常直接在 Rancher 的工作负载页面修改镜像版本号,然后保存。Rancher 会触发滚动更新,新 Pod 起来、Ready 之后,旧 Pod 才会被回收。

如果新版本有问题,优先回滚而不是去修改代码重新构建。Rancher 的“回滚”功能可以快速把 Deployment 恢复到上一个 ReplicaSet 版本。操作路径是进入工作负载详情,在“监控”或者“历史版本”入口选择之前的版本并执行回滚。

这里有个运维细节:回滚之后镜像 Tag 可能不会自动跟着回退,因为 Deployment 定义里写的是旧的 Tag。最好再手动把 Tag 改回上一个版本并保存,确保定义和实际一致,不然后续再次编辑时又会被带到新版本上去。

4.5 服务暴露方式的选择

应用部署完,还得让用户能访问。Rancher 支持多种服务暴露方式,私有化场景我用得最多的是 Ingress 和 NodePort。

如果客户没有现成的负载均衡器,Ingress 需要先部署 Ingress Controller 并指定对外域名和证书。如果是给内网人员访问的内部系统,NodePort 反而是最简单可靠的方案,因为节点 IP 和端口是确定的,不存在 DNS 解析问题。Rancher 界面对这两种方式都做得很简单,选好工作负载后直接填端口映射即可。

要注意的是,NodePort 的端口范围默认限制在 30000-32767。如果客户希望用 80 或 443,通常还得靠 Nginx、HAProxy 这类反代转发,或者在集群上改 kube-apiserver 的service-node-port-range参数,这属于另一层规划了。

5. 私有化场景下最容易翻车的几个坑和排查路径

做了几轮私有化交付后,我总结出了几个最容易让人“卡住一整天”的典型问题。这些坑不一定在教程里能看到,但几乎每次换一个新客户、新环境都会再踩一遍。

5.1 ImagePullBackOff 和 ErrImagePull 的完整排查链

镜像拉取失败,是私有化里最常见的故障。排查思路要按链路往下走,不要只看表面报错。

先看 Pod 的事件:

kubectl describe pod <pod-name> -n <namespace>

如果事件里写的是Failed to pull image "registry.internal.example.com/erp-system/backend:1.4.2": rpc error: code = Unknown desc = failed to pull and unpack image ...,那基本能确定是拉取阶段的问题。

下一步逐项排查:

  1. 节点上能否手动解析 Harbor 域名:ping registry.internal.example.com,很多客户内网 DNS 记录没加,域名解析失败非常常见。
  2. 节点上 curl Harbor 地址:curl -v http://registry.internal.example.com/v2/,确认网络可达,返回 401 反而说明正常,因为没带认证信息。
  3. 检查 imagePullSecret 是否存在且内容是有效 Docker config。验证方式是kubectl get secret <secret-name> -o yaml,看data里的.dockerconfigjson是否包含对应仓库地址。

一个很容易被忽略的细节是:secret 里的仓库地址必须和镜像地址完全一致,包括端口和域名。如果镜像写的是registry.internal.example.com/erp-system/backend:1.4.2,而 secret 里的 servername 只写了 IP 或漏了端口,认证依然会失败。一个秘钥只匹配一个域名。

5.2 自签名证书引发的连锁坑

自签名证书在很多 K8s 环境里都会制造诡异的错误。比如某些组件能拉镜像,但 Harbor 的“复制”任务会报证书错误;再比如用 Docker 登录 Harbor 时提示 X509,但 everything 看起来都对。

我的经验是,证书问题要一次性统一配置。首先给 Harbor、Rancher Server 生成证书时,要尽可能把内部域名都加进 Subject Alternative Name。其次在每一台节点上,把 Harbor 和 Rancher Server 的 CA 证书拷入系统信任目录,然后重启容器运行时和 Rancher agent。

尤其要注意 containerd 的参数配置。K3s 集群的/etc/rancher/k3s/registries.yaml配置写法和 Docker 的 daemon.json 完全不同,需要写成如下形式:

mirrors: registry.internal.example.com: endpoint: - "https://registry.internal.example.com" configs: "registry.internal.example.com": auth: username: robot$erp-pull password: <password> tls: ca_file: /etc/ssl/certs/harbor-ca.crt

如果没有把 CA 文件放到每台节点对应路径,到 cert 时报错才是正常的。配置完成后,用systemctl restart k3s或者对应的 containerd 服务重启,再删除掉旧的失败 Pod 重新部署验证。

5.3 Harbor 磁盘写满和 500 报错

Harbor 跑着跑着突然不能推送镜像,返回 500 或者blob upload失败,最常见的原因就是存储满了。Harbor 的存储目录一般是/data/registry/docker/registry/v2,镜像数据都在这里。磁盘满时会先出现推镜像缓慢,再变成推镜像失败,后续连登录都可能超时。

排查方式:

df -h /data

如果是/data满了,先用 Harbor 的清理策略删除一批无用镜像,清完后还要手动触发 GC(垃圾回收),否则旧的 blob 不会真正从磁盘释放。还有一个容易忽略的点:Harbor 的 PostgreSQL 数据库也随数据卷增长,如果磁盘不宽裕,可以调整数据库的日志保留策略,不然一块几十 GB 的盘很容易被数据库日志占满。

5.4 Rancher Server 证书过期导致的管理面宕机

Rancher Server 自己也有一套证书体系。用 Helm 安装 Rancher 时,如果没指定ingress.tls.source作为自有证书,默认会生成一个自签证书,这个证书有效期只有一年左右。到期之后,Rancher 界面还能打开,但集群里的 Agent 会陆续失联,下游集群的 Pod 无法调度到新节点。

遇到这种情况别急着卸载重装,有一个相对省事的处理路径:清理 Rancher 的证书生成目录后触发重新生成。但更推荐的做法是在安装时就把 Rancher Server 的 Ingress 证书换成过期时间足够长的自有证书,并把证书纳入监控告警。我见过不少团队在这里吃哑巴亏,因为证书过期引发的故障比较隐蔽,事件日志里只会显示 agent 报 TLS 握手错误。

5.5 多环境 Harbor 同步断开的恢复操作

跨机房的 Harbor 复制任务偶尔会断。原因有很多:目标 Harbor 重启、磁盘满、网络抖动、凭据过期等等。恢复的第一步不是删掉复制规则重建,而是进入复制任务列表,查看最近一次任务的状态和日志。

如果是凭据失效,去“仓库管理”里重新更新对应目标仓库的访问密码。如果是网络问题,可以先确认源和目标 Harbor 之间能否用curl访问/v2/端点。私有化环境常见的一个问题是,源 Harbor 和目标 Harbor 的版本不一致,导致复制协议不兼容。如果持续报错,可以考虑把两端统一升级到相同的大版本,至少保证小版本差异不要太大。

这里再分享一个运维习惯:给 Harbor 复制任务加上失败报警,或者每天检查一下复制任务的状态。多环境之间镜像不同步,表面上是发布失败,深层次其实是同步策略没有纳入日常监控,等发布当口发现镜像还是旧的,整个变更窗口就被卡住,只能手工补传,但手工操作在这种链路里最容易漏。

我的实际体会

这套 Harbor + Rancher 组合用下来,最大的感受是稳定省心,但前提是前期要把证书、账号权限、命名规范这些“看起来没什么技术含量”的基础项做好。许多人容易被界面炫酷的仪表盘吸引,一上来就疯狂部署应用,等交付到客户现场才一个个补证书、抠权限、清理仓库,往往浪费更多时间。如果你刚开始搭建,建议从最小闭环开始:一台 Harbor、一个 Rancher、两个节点组成的小集群,先把一个测试镜像完整跑一遍推送、拉取、发布、升级的流程,再往后铺多环境。这套链路跑通之后,再大的私有化项目也就只是这套流程在更多节点上重复执行而已。

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

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

立即咨询