最近刚把公司内网一批业务从 Rancher 管理的 Kubernetes 集群迁移到了 Sealos 私有化集群上。原本以为只是换一个管理面板,没想到牵扯出资源梳理、存储卷处理、中间件数据搬迁和流量切换一整套活。整个过程比预想中琐碎,但落地之后回头看,这笔账是算得过来的。这篇东西就当一次复盘,把从 Rancher 迁到 Sealos 的完整思路、关键步骤和踩过的坑都整理出来,给正在评估或者已经准备动手做私有化迁移的同行一个参考。
先说结论:如果你手里只有一两套 K8s 集群,Rancher 的集群管理能力其实够用,但如果你要做的是面向公司内部或者交付客户的私有化平台,需要的是一个自带镜像仓库、应用编排和中间件管理能力的整体底座,这时候 Sealos 这种“云操作系统”形态的私有化方案会更顺手。迁移的核心工作不是把 YAML 复制过去,而是把资源依赖、数据存储、网络策略这些隐性资产一起搬过去。
1. 为什么选择从 Rancher 迁移到 Sealos
1.1 Rancher 用着不差,但痛点很真实
Rancher 在 Kubernetes 圈子里名气很大,它解决的是“多个集群集中管理”的问题。一个控制平面可以接入几十个下游集群,统一做 RBAC、项目管理、应用目录和监控告警,这在多集群规模下确实方便。我之前的团队从单集群起步,慢慢扩展到开发、测试、预发三套环境,Rancher 帮我们省了不少心。
但随着做私有化交付的需求变多,Rancher 的一些问题开始冒头。首先是它本身是一套比较重的控制系统,Rancher Server 部署在集群里,有自己的 CRD、Controller、Agent,升级的时候要考虑版本匹配问题,一旦控制面出问题,下游集群的日常管理也会跟着受影响。其次是 Rancher 的管理面和应用交付面是分离的,部署一个业务应用你仍然需要先写 Dockerfile、构建镜像、再写 Deployment/Service/Ingress,然后还要配一套 CI 流程,说白了它还是一个“Kubernetes 管理器”,不是“应用交付平台”。
另一个让我下定决心迁移的原因是资源利用率。Rancher 默认会在每个下游集群安装一组 agent,加上 logging、monitoring、catalog 等一堆组件,一台 8C16G 的机器跑控制面加组件后剩下的资源明显缩水。对于中小规模团队来说,这个开销不小。
1.2 Sealos 私有化的定位正好不一样
Sealos 的定位是“云操作系统”,底层依然是 Kubernetes,但它在 K8s 之上做了一层应用交付和资源管理抽象。部署完 Sealos 之后,你得到的不只是一个容器编排平台,而是一个自带镜像仓库、应用商店、可观测面板和数据库中间件一键部署能力的底座。
私有化场景里最看重的就是“开箱即用”和“离线交付”。Sealos 的集群安装本身支持离线包,把镜像包导入内置 registry 后,整个环境完全脱离公网也能正常工作,这对内网部署和客户环境交付是刚需。另一个让我觉得差异明显的是应用商店,里面对齐了很多常用中间件,比如 MySQL、PostgreSQL、Redis、MinIO、Nginx,通过图形化界面或者命令行就能拉起,这对 Rancher 里的 catalog 来说,操作体验根本不在一个层级。
还有一个比较关键的对比点:Rancher 为了方便多集群纳管,会往命名空间、工作负载里注入自己的一堆注解和 CRD 引用,你导出 YAML 再往别的集群导,经常因为字段不兼容要逐个清洗。而 Sealos 本身就是直接操作原生 Kubernetes 对象,资源声明更干净,迁移成本天然就低一些。
1.3 选型对比:不只看功能,还要看迁移后的运维模型
我整理了一张对比表,能比较直观地看出两者在私有化交付场景下的侧重差异:
| 对比维度 | Rancher | Sealos |
|---|---|---|
| 核心定位 | Kubernetes 多集群管理平台 | 基于 K8s 的应用交付底座 |
| 私有化离线交付 | 需要额外搭建内网镜像仓库并处理组件依赖 | 自带镜像仓库和离线包机制 |
| 应用部署模式 | 写 YAML + Helm,依赖外部工具链 | 应用商店 + 封装格式,开箱即用 |
| 中间件管理 | 需要自己维护 operator 和存储 | 一键部署常见数据库和消息队列 |
| 资源开销 | 控制面组件占比较高 | 轻量,资源集中在业务 Pod |
| 对原生 K8s 资源的侵入性 | 注入大量 cattle 相关字段 | 保持原生对象,几乎无侵入 |
选型的逻辑很简单:如果团队的核心诉求是“管 K8s 集群”,Rancher 依然是很好的选择;如果诉求是“搭建一套可持续交付的私有化平台”,Sealos 的架构形态更对路。我们迁移的根本原因,就是要把平台能力从“K8s 管理”上升到“应用交付和运维一体化”。
2. 迁移前准备工作:盘点资产和确定策略
2.1 先把 Rancher 里的家底全部摸清楚
迁移前最怕漏掉东西,第一步就是把 Rancher 管理的所有业务资产完整盘点一遍。我会按以下几个维度来梳理:
- 工作负载:所有 Deployment、StatefulSet、DaemonSet,记录命名空间、副本数、镜像地址、资源 request/limit。
- 服务暴露:Service、Ingress、ServiceEntry 和证书配置,特别是 HTTPS 证书的签发方式(Rancher 里常用 cert-manager 还是自定义 Secret)。
- 配置数据:ConfigMap、Secret,这部分最容易迁移,也最容易被忽略。
- 存储资源:PVC、StorageClass,记录每块存储的容量、访问模式、底层存储类型(local-path、NFS、云盘)。
- 基础组件:DNS、网关、日志收集、监控告警,确认这些是 Rancher 自带的还是独立部署的。
- 中间件:数据库、缓存、队列,确认连接地址是否已经写在业务配置里,还是通过服务名解析。
我这次迁移的规模大概是 30 个命名空间、120 多个服务,其中包括三套 PostgreSQL、两套 MySQL、一个 Redis 集群、一个 MinIO 对象存储,以及一批无状态 Java 和 Go 服务。盘点之后才发现,很多服务之间存在隐式依赖,比如业务服务直接通过 PVC 挂载共享目录,或者某个 ConfigMap 里硬编码了另一个服务的 IP。
2.2 规划目标集群资源,避免迁到一半资源不够
Sealos 私有化集群的节点规划,不能拍脑袋。迁移时要保留一定的扩容余量,建议按原集群资源的 1.2 到 1.5 倍来准备。比如原来 120 个服务加起来要 80 核 160G 内存,新集群至少准备 100 核 200G 内存。
节点划分也建议明确角色:Master 节点跑控制面组件,承担调度和 API 服务;Worker 节点跑业务负载。如果业务里有数据库这类有状态服务,建议单独挂一块 SSD 数据盘,存储类单独定义,避免和日志型应用抢 I/O。另外节点之间注意网络互通,私有化环境通常要提前规划好网段,避免 Docker 网段和业务网段冲突。
存储这块要提前想清楚。原 Rancher 集群如果用的是 local-path-provisioner,数据落在各节点本地目录上,迁移后要把这些磁盘数据转移过去,或者直接把旧磁盘挂载到新机器上;如果用 NFS,搭建新环境的 NFS 要做好权限映射。总之存储改造是迁移里最容易翻车的点,我会在后面单独展开。
2.3 设计迁移批次和回退方案
一般建议分批迁移,而不是一次性全量切换。我采用的策略是三个批次:
第一批迁无状态应用,比如前端静态资源、网关接口、定时任务,验证新集群的网络和 Ingress 是否正常。 第二批迁有状态中间件,比如数据库、Redis、对象存储,这部分涉及数据同步,需要预留充足时间窗口。 第三批迁对稳定性要求最高的核心链路,确认前两批没有问题之后再做最后切换。
每一批迁移都要有回退方案。比如数据库迁移前保留原集群数据,并同时开启增量同步,万一导入出问题可以切回去。业务层通过负载均衡或者 DNS 权重逐步切流量,观察日志和监控指标没有异常再完全切干净。
3. Sealos 私有化部署的实操记录
3.1 节点环境准备和离线包下载
Sealos 的私有化部署在离线环境里非常顺,但前提是提前准备好安装介质。我实际用到的核心组件是labring/kubernetes、labring/helm、labring/cert-manager、labring/sealos自带的内置 registry,以及应用商店相关的镜像包。在有网的环境下先把这些镜像通过sealos pull拉到本地,然后打成人sealos load导入目标机器。
我准备了三台机器:一台 Master,两台 Worker,操作系统是 CentOS 7.9,内核版本需要确认支持 Kubernetes 的网络要求。所有节点关闭 swap,配置好主机名和 hosts 解析,然后安装容器运行时。Sealos 支持 containerd 作为运行时,配置比较简单,只需要确认sandbox_image可以正常拉取。
这一步有一个很关键的注意点:所有节点的时钟必须同步,建议搭一个内网 NTP 服务,否则节点之间证书校验会报错,排查起来非常耗时。
3.2 用 Clusterfile 声明集群并一键安装
Sealos 部署集群可以通过命令行直接指定节点,也可以写一个 Clusterfile。我用的是 Clusterfile 的方式,好处是配置可以保存下来,后续扩容就基于这个文件改一改再执行。
下面是我实际使用的 Clusterfile 核心内容,Hosts 部分定义节点角色和地址,后面接 SSH 认证信息。
apiVersion: v1beta1 kind: Cluster metadata: name: my-private-cluster spec: hosts: - name: master01 role: master address: 192.168.10.21 user: root passwd: "YourStrongPassword" arch: amd64 - name: worker01 role: node address: 192.168.10.22 user: root passwd: "YourStrongPassword" arch: amd64 - name: worker02 role: node address: 192.168.10.23 user: root passwd: "YourStrongPassword" arch: amd64 ssh: passwd: "YourStrongPassword" port: 22然后执行安装命令,指定要装的 Kubernetes 版本和辅助组件:
sealos run labring/kubernetes:v1.31.4 \ labring/helm:v3.14.4 \ labring/cert-manager:v1.15.0 \ --cluster my-private-cluster整个过程大概十分钟左右,日志会输出每个节点的执行情况。安装完成之后,kubectl get node应该能看到所有节点 Ready。
3.3 内置镜像仓库和应用商店的启用
Sealos 自带镜像仓库这一点在私有化场景里太省心了。部署完成后,集群内部会有一个 registry 服务,可以手动上传镜像包。日常发布流程里,构建机把镜像推到 Sealos 的 registry,业务节点拉镜像时走的就是没网也能拉取的内网地址,不会再因为公网拉取受限而失败。
应用商店是一大亮点。私有化平台经常要部署 Redis、MySQL 这类基础组件,如果靠手写 operator 和 YAML,工作量大还容易出错。Sealos 的应用商店里提供了一键部署的入口,我这边直接通过商店拉起了一套 Redis 和一个 MinIO,耗时比从 Rancher 的 catalog 里装快很多,状态管理也更直观。
不过有一点要注意:在离线环境里要用应用商店,需要先把商店对应的应用镜像导入内置 registry,否则页面客户端会提示找不到镜像。这个步骤我一开始没注意,导致后面点安装老是卡在拉镜像,后来手动导入镜像包才恢复正常。
3.4 存储类和证书的提前配置
存储是迁移成功与否的关键。我在 Sealos 集群上提前定义了两个 StorageClass:一个用本地盘做默认存储,给无状态服务配 PVC;一个用 NFS 给有状态中间件提供共享存储,比如 MinIO 的持久化目录。
NFS 的配置过程中,我踩过一个坑:新机器上挂载 NFS 时权限锁死,容器进程运行用户是 nobody 或者 65534,写不进去数据目录。解决办法是设置 NFS 服务端导出目录时指定no_root_squash,同时把目录属主改成容器运行用户 UID,然后在 StorageClass 里挂载时不要强制改属主。
证书这边,我直接复用 cert-manager 的流程。Sealos 集群里部署好 cert-manager,然后创建 ClusterIssuer,内部域名签发用自签名 CA,外部域名则对接已有的内网 CA。这样可以保证 HTTPS 证书在迁移后无缝衔接。
4. 从 Rancher 导出资源并迁入 Sealos 集群
4.1 清洗 Rancher 注入的 CRD 字段和注解
Rancher 在命名空间上会打上field.cattle.io/projectId这类注解,工作负载里也会出现cattle.io/timestamp或者 ownerReferences 指向 Rancher 管理的 CRD。这些字段在目标集群中大多无效,直接 apply 虽然不一定报错,但会干扰后续的资源管理和回收。
我在每个命名空间执行一次完整导出:
kubectl get deploy,sts,ds,svc,cm,secret,pvc,ingress -n app-namespace -o yaml > app-namespace-backup.yaml然后写了一个简单的 Python 脚本来做字段清理,重点删除以下内容:
metadata.uid、metadata.resourceVersion、metadata.generation、metadata.creationTimestampmetadata.managedFields这整个数组metadata.annotations里所有以field.cattle.io和cattle.io开头的键metadata.ownerReferences指向非原生资源的引用- Deployment 里
status整个段,这个只是运行时状态,导入时会自动重新生成
清洗的时候要留意:有些注解虽然带 cattle 前缀,但业务可能依赖,比如某些灰度发布配置。稳妥的做法是保留名单机制,先拉一个常见删除清单,确认无副作用后再批量执行。
4.2 适配目标集群的镜像地址和存储类
清洗完字段之后,第二步是全局替换镜像地址。原 Rancher 集群的镜像放在公网仓库或者公司旧的内网 registry,迁移后统一指向 Sealos 内置 registry。替换方法不复杂,用 sed 批量处理 YAML 中的镜像前缀:
sed -i 's#old-registry.example.com#registry.sealos.svc.cluster.local:5000#g' app-namespace-backup.yaml但有一点要注意,不同命名空间拉取镜像的 Secret 可能不同。如果是私有镜像,要将原 registry 的 docker-registry Secret 重新创建到 Sealos 集群对应命名空间,否则 Pod 启动会报 ImagePullBackOff。
存储类的替换更直接,把 PVC 定义里的storageClassName从原来的local-path改成目标集群实际存在的存储类名,比如nfs-storage。如果原 PVC 容量比较大,需要确认目标存储类支持动态扩容,否则只能预先按规格创建 PV,再通过 label 匹配绑定。
4.3 处理 Ingress 和 Service 的访问关系
Rancher 的 Ingress 依赖它自己实现的 nginx-ingress 或 Traefik,而 Sealos 默认的网关层可能不同,因此 IngressClass 要重新指定。我把 PYC 里所有 Ingress 的:
- 删除
kubernetes.io/ingress.class注解中的旧值 - 在
spec.ingressClassName字段填上 Sealos 集群的platform-navigation-ingress或默认网关类
Service 本身的 ClusterIP 会变化,但集群内通过服务名访问不受影响,因为 DNS 解析走的是服务名。要重点检查的是跨集群调用有没有硬编码 IP 的地方,比如某些业务配置里写了192.168.10.5:3306这类地址,这类地址必须改成新集群中对应服务的对外地址或者集群内服务名。
4.4 批量 apply 和资源校验
清洗完的 YAML 文件放在一个目录里,然后逐个命名空间进行校验和应用:
kubectl create --dry-run=client -f app-namespace-cleaned.yaml kubectl apply -f app-namespace-cleaned.yaml先跑 dry-run 的目的是尽早在客户端侧发现字段非法或不兼容的问题。批量 apply 之后,等 Pod 拉起来,检查 PVC 是否 Binding、Service 的 Endpoints 是否正常、Ingress 是否得到地址。
我实际导入之后,发现部分 Deployment 的 imagePullPolicy 被 Rancher 改写成了 Always,导致在离线环境里每次启动都要检查镜像是否存在心跳。这个改为 IfNotPresent 能显著降低拉取延迟,也避免网络抖动引起的拉取失败。
5. 数据迁移:数据库、对象存储和共享目录
5.1 数据库迁移要用逻辑导出导入,而不是直接搬文件
数据库迁移是整个项目里风险最高的环节。我处理了三套 PostgreSQL 和两套 MySQL,统一采用逻辑导出导入的方式,而不是直接拷贝数据文件,这样避免版本兼容性和文件权限问题。
MySQL 迁移流程:
mysqldump -h old-mysql -u root -p --single-transaction --routines --triggers --events --databases db1 db2 > db-backup.sql mysql -h new-mysql -u root -p < db-backup.sqlPostgreSQL 迁移流程:
pg_dump -h old-pg -U postgres -d dbname --no-owner -F c > dbname.dump pg_restore -h new-pg -U postgres -d dbname --no-owner dbname.dump注意几个细节:一是--single-transaction保证 MySQL 导出时的一致性视图,避免出现数据中途修改导致备份不一致;二是--no-owner解决新旧环境数据库用户名不一致导致的权限问题;三是大表一定要分库分表导出,不要一把梭全库一次性 dump,否则恢复时间太长,还会因为某个表出问题被迫从头再来。
数据导入完成之后,要立刻做表结构和行数对比,我写了一个对比脚本遍历每张表统计COUNT(*),与原库做差值。另外跑几个关键业务查询,确认索引和存储过程都正常。
5.2 Redis 和对象存储的快速迁移
Redis 迁移用redis-cli --pipe做全量填充。源库是集群模式下就用redis-cli -c,导出格式用MIGRATE脚本或者直接RDB再导入。小数据集下直接取 RDB 文件复制过去再重启最省事,但要注意版本,Redis 6 和 7 的 RDB 格式多数不通用。安全起见我用dump.rdb的方式时会先匹配版本号。
MinIO 这类对象存储,我用的是mc mirror命令做同步:
mc mirror --overwrite --remove source-bucket/ minio-target/bucket/同步完成之后记得做一次抽查,对比几个大文件的 ETag 是否一致,避免传输中损坏。
5.3 共享目录和 PVC 数据怎么处理
无状态服务的 PVC 数据量不大,可以直接重建空 PVC 再复制数据。但如果是共享目录,比如多个服务共用一个 NFS 挂载的目录,迁移时要注意保持目录结构和属主一致。
我先在旧节点上打包共享目录:
tar czvf shared-data.tar.gz -C /data shared然后在目标节点上解压,并将目录属主改成容器运行的 UID。如果是 NFS,先确认目标 NFS 服务端的目录挂载属性,避免权限拒绝。
还有一类比较特殊的情况:服务在启动时对临时目录有大量写入,旧集群里用hostPath挂载。我迁移时改成新集群里统一的 StorageClass 动态分配 PVC,避免节点重启后数据丢失,也不用再和具体节点绑定。
6. 流量切换、灰度验证与原集群下线
6.1 低风险流量切换到新集群的方式
内部服务切流量的方式我做了两种:有域名入口的,通过 DNS 解析把权重逐步调向新集群的负载均衡 IP;没有域名只有集群内访问的,直接改动上游调用方配置指向新服务名,然后滚动重启上游服务。
为了豁免 DNS 缓存对灰度验证的干扰,我在切换前先在本地/etc/hosts里把域名指向新集群,验证核心链路。之后再改 DNS 的 A 记录权重,先切 10%,观察告警和接口错误率,稳定后切到 50%,最后全部切过去。
6.2 数据校验必需的清单
切换完成后一定要按清单逐项验证,而不是简单看 Pod 状态。我用的是下面这个表:
| 检查项 | 检查方法 |
|---|---|
| Pod 状态 | 所有业务容器 Running,无 CrashLoopBackOff |
| 服务发现 | kubectl get endpoints确认对应 Pod IP 已注册 |
| PVC 挂载 | kubectl get pvc状态为 Bound,Pod 可读写测试 |
| 数据库连接 | 业务日志无 connection refused 报错 |
| 定时任务 | 到点后检查任务执行记录和输出日志 |
| 监控告警 | 基础监控无新增 P1/P2 告警 |
6.3 回退方案和原集群下线时机
灰度期间保留原 Rancher 集群不关停,数据库有新数据写入时通过增量同步或者业务双向写入过渡。等新集群稳定跑一周以上,再把数据库的写流量切过去,旧库改成只读模式,业务侧验证无异常后才彻底下线。
下线前要把旧集群里的 PVC 数据做一次最终备份,以防后续审计或追查需要。Rancher 控制面本身不急于删除,建议保留到所有业务验证通过,并且旧集群的证书、监控、日志等附属系统不再被依赖以后再清理。
7. 迁移之后常见问题速查表
把这次迁移中实际遇到的几个典型问题整理成一张速查表,很多问题都是迁移到新 K8s 环境都会遇到的,供参考:
| 现象 | 可能原因 | 解决办法 |
|---|---|---|
| Pod 一直 Pending | PVC 未绑定或存储类不存在 | 检查 StorageClass 名称,重提 PVC 或预建 PV |
| ImagePullBackOff | 镜像在 registry 中不存在 | 导入镜像或者修改镜像标签地址为内置 registry |
| 服务间访问超时 | DNS 解析失败或集群网络策略拦截 | 检查 CoreDNS 配置,确认没有残留旧的 hosts 记录 |
| Ingress 404 | IngressClass 不匹配或证书 Secret 没同步 | 删除旧注解,设置新的ingressClassName |
| 数据库写入权限报错 | 迁移时丢失用户权限授权 | 重新执行GRANT授权,更新业务账号密码 |
| 容器写入目录无权限 | NFS 导出目录是 root squash | 调整 NFS 配置no_root_squash,或修改属主 UID |
还有一个容易被忽略的地方:Rancher 的命名空间默认带项目隔离策略,迁移到 Sealos 后如果不做 NetworkPolicy 限制,所有服务之间默认全通。从安全视角看,建议对照原 Rancher 项目空间的隔离关系,把核心业务命名空间之间用 NetworkPolicy 显式约束好,避免新环境网络面被放大。
8. 一点个人体会
这次迁移让我印象最深的一点是:真正花时间的不是安装和部署,而是对存量系统的梳理。Rancher 这种平台管理能力很强,但长期使用下来,工作负载、存储、配置之间的隐式依赖会慢慢沉淀成一个“黑箱”,你不做迁移,永远不知道里面有多少隐式关联。
如果你也在准备从 Rancher 迁到 Sealos 或者其他私有化底座,我建议先花两周时间把资产盘点做扎实,再启动扑腾。过程中多留后路,宁可批次拆碎一点,也不要追求一次切换的“英雄主义”。Sealos 在私有化场景里的交付效率确实高,但再好的工具也替代不了严谨的迁移规划和数据验证。
最后再分享一个小技巧:迁移完成后,别急着把旧集群删掉,在旧集群旁边保留一份核心服务的运行快照,比如备份关键 Deployment 和 ConfigMap 到一个 Git 仓库里。后续如果新环境有配置需要比对,随时可以拉出来对照,这个习惯在长时间运维里非常有用。