ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

从Pod原理到Rancher实战:K8s迁移上云与高并发压测全解析

2026/9/17 2:50:22 拓冰建站 浏览量
从Pod原理到Rancher实战:K8s迁移上云与高并发压测全解析 上周我接到一个很典型的迁移需求客户单节点 K8s 上跑着一整套若依微服务要求不停服、不丢数据地迁到阿里云 ECS迁移完还要由压测人员用配套 JMeter 脚本做高并发测试验证 2c4g 的 Pod 能扛多少并发。这种场景在中小企业里越来越常见——容器化早就不是新鲜事但随着 Rancher 这类容器管理平台普及K8s 的使用门槛虽然降下来了真正遇到 Pod 异常、调度失败、外部访问不通时能把原理讲清楚、把排查思路捋顺的人还是不多。这篇文章我把两件事串起来讲一是 Pod 的核心原理包括它由什么控制、多容器怎么协同工作、调度和抢占的底层机制二是基于 Rancher 的实战操作从安装 Rancher Desktop、部署 Nacos 到迁移上云和压测验证。适合正在学 K8s 的开发者、刚接手集群的运维以及准备把存量业务平滑迁到云上的团队参考尤其是计划在 2c4g 这种小规格 Pod 上跑业务的同学看完能少走不少弯路。1. 先把 Pod 的底层逻辑吃透1.1 Pod 到底是什么为什么 K8s 不直接调度 Docker 容器先回答一个最基础也最容易被忽略的问题K8s 的最小调度单元为什么是 Pod而不是直接调度 Docker 容器因为容器这个概念在“多个进程需要同生共死、共享网络”的场景下是不够用的。一个典型例子是业务容器加日志采集 sidecar。业务进程把日志写到本地文件sidecar 容器把文件采集走两者必须部署在同一台机器上共享同一个文件系统最好还共享同一个网络命名空间这样访问 localhost 就能通信。如果用 Docker 直接管理它们就是两个孤立的容器靠端口映射和网络插件去互联运维成本高生命周期也没办法强绑定。Pod 在 K8s 里的设计本质上是用一个“基础容器”把这些容器包在一起。这个基础容器通常是 pause也叫 infra容器它只负责创建并持有 Pod 的网络命名空间、PID 命名空间和共享卷。后面你真正跑起来的业务容器比如 Nginx、Java 服务都挂到这个 pause 容器下面。所以你在节点上用 docker ps 会看到很多名字里带 pause 或 sandbox 的容器不要以为是异常那是所有 Pod 的地基。这里有个非常关键的理解Pod 是 K8s 的调度单位但它不是进程而是一个“共享运行环境的边界”。Pod 里的所有容器共享同一个 Pod IP、同一个 hostname、同一批 volume。反过来不同 Pod 之间默认是隔离的。这个设计决定了你在使用 K8s 时不能像写 docker-compose 那样什么服务都塞进一个“Pod”而是要根据生命周期和共享粒度去划分。1.2 Pod 由谁控制控制器模型与声明式 API热搜问题里有一个特别好的问题“pod由什么控制”。直接回答Pod 通常不是你自己直接创建出来的而是由控制器Controller创建和管理的。最常见的控制器是 Deployment它管理 ReplicaSetReplicaSet 再负责维护 Pod 的副本数量。这个链路的逻辑是这样的你写一个 Deployment YAML里面声明副本数是 3镜像是什么端口是什么。kube-controller-manager 里的 deployment controller 看到期望状态后会创建或更新一个 ReplicaSetReplicaSet controller 再确保实际运行的 Pod 数量等于期望数量。如果某个 Pod 崩了控制器会马上创建一个新 Pod 顶上来目标只有一个让集群尽可能接近你声明的那份期望状态。这就是 K8s 最核心的设计哲学声明式 API 加控制器调谐。你不用告诉它“请你帮我杀掉 2 号 Pod 再启动一个”你只需要说“我要 3 个副本”剩下的事控制器自己完成。使用习惯上除非是调试场景否则不要用 kubectl run 直接创建裸 Pod。裸 Pod 没有控制器保护节点出问题它就真的没了不会自动重建。除了 Deployment还有几类控制器要认识。StatefulSet 负责有状态服务比如 Nacos、MySQL、ZooKeeper它会给每个 Pod 一个稳定的网络标识和独立的存储卷DaemonSet 保证每个节点都运行一个指定 Pod比如日志采集、节点监控Job 和 CronJob 负责一次性任务和定时任务。搞清楚这些遇到“Pod 起不来”“Pod 被删了又起来”这类问题时第一反应就应该是去看它归哪个控制器管。1.3 多容器 Pod 的同生共死启动顺序、探针与故障影响有一个热搜词问得很尖锐“一个pod中如果包含了多个容器如果其中一个容器没有成功启动可能会影响其它容器”。答案是会影响而且影响方式要看容器类型和启动阶段。首先说启动顺序。同一个 Pod 里的容器如果定义了 initContainers初始化容器那么必须等所有 init 容器全部成功退出业务容器才会开始启动。如果某个 init 容器一直失败整个 Pod 就卡在 Pending 或者反复创建业务容器根本不会启动。这就是为什么你把数据库迁移脚本放进 init 容器时数据库地址写错会导致整个 Pod 起不来。其次说运行阶段。Pod 有个 restartPolicy默认是 Always。如果主业务容器崩溃退出kubelet 会重新拉起容器这个逻辑对 Pod 里的每个容器都生效。如果 sidecar 容器崩了它也会被重启但主容器不一定受影响反过来如果主容器一直崩溃同 Pod 的 sidecar 也会跟着这个“反复重启”的节奏一直重启共享日志卷的采集任务可能变得断断续续。同生共死还体现在删除和调度上。你执行 kubectl delete pod整个 Pod 的所有容器会一起被终止节点故障时整个 Pod 被标记为失败控制器会另起一个新 Pod之前 Pod 里的所有容器都不会“单独幸存”。所以设计上要记住一条经验生命周期不同步的服务不要塞进同一个 Pod。比如应用和数据库就必须拆开应用和日志采集如果只是依赖本地文件放同一个 Pod 是可以的但也要能接受 sidecar 跟着应用一起重启。2. K8s 与 Docker 的区别及日常操作2.1 K8s 和 Docker 到底差在哪每次分享会都有人问“k8s和docker区别”这里给一个最容易记住的类比Docker 提供的是集装箱K8s 提供的是港口调度系统。Docker 负责把应用打包成镜像、在单机上把容器跑起来K8s 负责在多台机器之间统一调度这些容器包括自动扩容、故障恢复、服务发现、滚动更新。以前很多人习惯 docker run、docker-compose up觉得也能管十个八个容器。但当规模到几十上百个容器还要跨好几台机器部署时单机 Docker 就变得很难维护你要自己处理容器在哪台机器上、端口冲突了怎么办、挂掉的容器谁来拉起、流量怎么分发。这些恰恰是 K8s 的核心能力。还有一个容易混淆的点K8s 本身不是容器运行时。在早期版本里K8s 通过 Docker 来启动容器但从 1.24 版本开始kubelet 不再直接走 Docker。现在集群底层的容器运行时基本都是 containerd、CRI-O 这类符合 CRIContainer Runtime Interface的引擎。Rancher Desktop 里专门提供了 dockerd(moby) 模式是为了给那些还依赖 Docker API 的开发环境做兼容本质上是两套东西。所以如果你是刚开始接触容器建议先把 Docker 的命令学会会用 Dockerfile 构建镜像、会用 docker run 跑容器然后再进入 K8s 学习编排。如果你已经有一定经验遇到“镜像没问题、端口没冲突但容器就是起不来”的情况要记得去查节点上的容器运行时日志而不是只盯着 Docker 日志看。2.2 高频 kubectl 命令与排查姿势K8s 的日常操作九成都在 kubectl 上Rancher UI 只是把命令可视化。我列出常用的几条按排查流程排kubectl get pods -A先看全局状态哪些异常一眼能看到。kubectl describe pod -n 看事件这是排障第一步很多隐形原因只出现在 Events 里比如拉镜像失败、持久卷挂载失败、探针失败。kubectl logs -n 看业务日志如果 Pod 里有多个容器要加 -c 指定容器。kubectl exec -it -n -- /bin/sh进容器调试查看环境变量、网络连接、文件。kubectl get svc -A 和 kubectl get ep -A检查服务暴露和端点是否正常。kubectl top nodes / kubectl top pods实时看资源使用率这个需要 metrics-server 已安装。kubectl rollout restart deployment/ -n 滚动重启某个工作负载比手动删 Pod 更优雅。排查的时候有个顺序误区很多人一上来就 logs。其实应该先 describe 看事件再 logs 看应用日志再 exec 进容器确认内部状态。比如 Pod 一直 Pendinglogs 会误导你因为容器可能从来没有被创建过describe 会直接告诉你调度失败的原因比如节点资源不足、PVC 没绑定、镜像拉取认证失败。2.3 资源限制怎么配2c4g Pod 的并发极限和 preemption 报错热搜里“2c4g的pod支持的并发量”是个特别务实的词。先给结论没有一个放之四海而皆准的数字因为它取决于应用类型、框架、数据库连接池、JVM 堆配置和流量模型。但可以给两个经验参照如果只是 Nginx 静态文件服务2c4g 的 Pod 在优化参数下跑到几百甚至上千 QPS 并不稀奇如果是一个典型的 Spring Boot 微服务带 MySQL、Redis 依赖乐观估计能扛 100-200 个并发用户的稳定访问再往上就容易出现线程池排队、数据库连接池等待、GC 频繁的连锁问题。讨论并发之前先把资源声明说清楚。Deployment 里的 resources.requests 和 limits 是两回事requests 是调度器为 Pod 预留的资源Pod 被调度时节点必须有这么多可用资源limits 是运行时的硬上限超过 CPU limit 会被限流超过内存 limit 会触发 OOM Kill。如果一个容器没写 requests 只有 limits或者什么都不写调度和运行时行为会很不一样这也是很多“看起来配置了资源但还是被 OOM”的原因。“no preemption victims found for incoming pod”这个报错常见于集群资源紧张且使用了 PriorityClass 的场景。意思是调度器想把当前要调度的 Pod 排进去但节点上资源不够于是按优先级抢占低优先级的 Pod结果没有找到合适的“牺牲品”。可能原因包括现有低优先级 Pod 无法被抢占比如是系统关键组件或者抢占条件不满足。排查方向有三个先 kubectl get nodes 看资源余量再看这个 Pod 的 priorityClassName 是否设置合理最后检查是否有亲和性或污点约束导致抢占条件不成立。更实际的解法是给节点扩容或者把 Pod 的 requests 调小一点因为抢占本身是一种应急手段别指望它长期兜底。3. Rancher 实战从安装到应用发布3.1 为什么我会推荐 Rancher 做容器管理先亮明立场Rancher 不是 K8s 的替代品它是 K8s 的管理平台。我推荐它的原因很简单——当你同时管理多个集群、需要给团队不同角色开不同权限或者团队里有人不习惯命令行时Rancher 的上手成本是最低的。Rancher 的核心能力包括多集群统一纳管你可以通过同一个控制台管理云上云下多套 K8s 集群内置 RBAC 和认证对接 LDAP 或 AD 后开发、测试、运维分权很清晰提供应用商店可以一键部署 Nacos、MySQL、监控等常用中间件还有可视化的工作负载管理Deployment、Service、Ingress、ConfigMap、Secret 都可以在页面上点出来。那什么时候不需要 Rancher如果只是单集群、两三个人、全命令行流Rancher 反而是额外组件会增加维护成本。但如果是给客户交付一套环境或者要团队里非运维角色也能自助发布服务Rancher 的价值就体现出来了。我这次的迁移场景客户环境就是单节点 K8s 加 Rancher 管理迁移后还要给压测人员看服务状态Rancher 的 UI 让他们不需要碰 kubectl 就能完成基础验证。3.2 Rancher Desktop 安装与 Docker API 连接报错处理如果是本地开发机推荐用 Rancher Desktop 而不是折腾完整的 K8s 集群。它内置 K3s轻量 K8s 发行版也能切换成 dockerd(moby) 模式兼容本地 Docker 开发流程。安装时最容易踩的坑就是 Windows 上启动后执行 docker ps 报错 “failed to connect to the docker api at npipe:////./pipe/docker_engine”。这个报错的本质是Docker CLI 想连接 Windows 下的 Docker Engine 管道但 Rancher Desktop 当前使用的是 containerd 运行时或者 dockerd 模式的守护进程没有启动。解决办法分三步打开 Rancher Desktop 的 Settings选择 Container Runtime 为 dockerd(moby)应用后重启 Rancher Desktop然后执行 docker context ls确认当前 context 指向了 rancher-desktop而不是默认的 default必要时用 docker context use rancher-desktop 手工切换。如果是 macOS同样的思路只是管道名变成了 Unix Socket。还有一个隐藏坑Rancher Desktop 启动需要虚拟化支持Windows 上要开 WSL2 或 Hyper-V否则 dockerd 起不来日志里会看到虚拟机相关的报错。3.3 用 Rancher 部署 Nacos并让外部正常访问Nacos 是热搜里出现频率很高的组件因为很多微服务环境都依赖它做注册中心和配置中心。用 Rancher 部署 Nacos我拆成三块创建 Deployment、创建 Service、配置外部访问。创建 Deployment 时镜像用 nacos/nacos-server:v2.3.2 这种明确版本不要用 latest。环境变量里至少要配 MODEstandalone单机模式和 NACOS_AUTH_ENABLEfalse测试环境先关鉴权生产必须开。容器端口填 8848 和 9848。这里 9848 是 Nacos 2.x 的 gRPC 端口很多人只暴露 8848结果服务能访问网页客户端注册却一直失败就是因为 9848 没开。Service 的类型单节点 K8s 最方便的是 NodePortRancher 页面上创建 Service 时选 NodePort把 8848 映射到比如 308489848 映射到 30948然后就能通过 http://节点IP:30848/nacos 访问网页了。云上环境如果配了负载均衡可以直接用 LoadBalancer 类型给每个端口生成一个 SLB 地址。需要提醒的是这里有个隐藏问题Nacos 2.x 客户端会通过 8848 拿服务端地址然后走 9848 建 gRPC 连接所以节点防火墙、安全组都要同时放通这两个端口否则客户端注册仍然不稳定。4. 单节点 K8s 迁移实战不停服、不丢数据4.1 迁移前先想清楚方案设计与双跑策略背景是热搜里那条单节点 K8s 上的若依微服务整套环境要不停服、不丢数据地迁移到阿里云 ECS迁移后还要做高并发压测。这类需求的核心难点不在 K8s 迁移本身而在“不停服、不丢数据”这六个字上。我给的方案是“双跑 切换”。先在阿里云 ECS 上搭一套新的单节点 K8s同样装 Rancher 或不装都行把业务以影子模式部署上去数据库用全量备份加增量同步的方式追到接近实时的状态。应用和数据库都追平后选一个切换窗口把流量从旧环境切到新环境旧环境暂不销毁留作回滚。这套方案的关键是不要把切换当成“换线”而是当成“发布”。先做小流量验证再逐步放量随时能回切。对单节点集群来说没有多节点的高可用可言所以更要依赖外部负载均衡或 DNS 做流量切换而不是在集群内部玩文章。4.2 数据与配置迁移的具体操作具体操作我按四条线展开。第一条线是对象导出。在旧集群上把需要迁移的 Deployment、Service、ConfigMap、Secret 全部导出kubectl get deploy,svc,cm,secret -n ruoyi -o yaml ruoyi-backup.yaml然后在新集群里 apply -f。这里有个细节Secret 的 value 默认是 base64 编码的导出后导入一般没问题但如果你用了 cert-manager 签发的证书要注意有效期和 CA 链。第二条线是镜像同步。确认所有镜像都已经 push 到新集群能访问的 registry。如果只有一个节点最简单是 docker pull 后 docker save再传到新机器 docker load如果有 registry直接 docker tag 加 docker push 更干净。第三条线是数据库迁移。这往往是最花时间的。MySQL 可以先用 mysqldump 做一次全量备份导入到新库然后在旧库开启 binlog用 DTS 工具或者 canal 做增量同步。同步过程中要反复校验两边数据量和关键表的 checksum确认没有差异。这里一定要记录好 binlog 的位点回滚时要用。第四条线是配置适配。新环境的数据库地址、Redis 地址、Nacos 地址都变了ConfigMap 里的连接串要统一改。我的习惯是提前把 yaml 里的占位符整理成一个清单apply 前逐个核对避免漏改导致 Pod 启动后连不上库。4.3 切换、验证和回滚预案切换在业务低峰期做。如果旧环境前面有负载均衡或 DNS先将权重调成新旧 9:1 之类的小比例选一两个端口或测试账号走到新环境验证登录、菜单、查询、写库这些核心链路。确认没问题后再逐渐放大到 5:5、7:3、10:0。切换完成后验证清单至少包括微服务全部注册到新 Nacos网关能路由到各服务数据库写入能正常 commit日志能正常收集压测环境能连到新环境。一个容易被忽略的点是定时任务很多项目里 xxl-job 或 Spring 自带的 Scheduled 会在新旧两个环境同时启动造成重复执行。迁移时要把旧环境的调度任务先停掉只保留新环境的。回滚预案必须提前写。如果切换后发现问题把流量权重切回旧环境即可但要注意一个时序问题如果旧环境数据库已经被新环境的写入覆盖回滚就不仅仅是切流量还要处理数据的反向同步。所以切换窗口的“只读检查”阶段很重要确认新环境没有严重写入问题再打开完整写入访问。5. 压测验证与问题排查速查5.1 用 JMeter 做高并发压测评估 2c4g Pod 的承载能力压测人员用的是配套的 JMeter 脚本我这边配合看集群指标。压测计划我建议分五步先跑一次基线用 20-50 并发慢跑 5 分钟记录平均响应时间、错误率、Pod 的 CPU 和内存基线。阶梯加压50、100、200、400 并发每档跑 5-10 分钟观察吞吐量和错误率拐点。拐点出现的位置就是这个 Pod 当前配置下的承载上限。盯三个资源维度Pod 的 CPU 和内存kubectl top pods、数据库连接数和慢查询、JVM 的 GC 日志和堆使用。很多时候瓶颈不在容器而在数据库连接池或线程池参数。观察零散的 OOM 和重启如果 Pod 内存 limit 设得太紧压到一定阶段会被 OOM Kill表现为 JMeter 错误率突然抬升kubectl get pods 看到 RESTARTS 增长。压测完立刻收集结论写清“2c4g Pod 在 XX 场景下建议单实例承载 XX 并发超过后推荐扩副本”。如果页面上要支撑更高并发优先调 HPAHorizontalPodAutoscaler而不是盲目调大单 Pod 资源。我见过很多人只盯着并发数字实际上调节压测脚本里的 think time、是否带 cookie、是否有写库操作结果会差很多。所以一定要保证 JMeter 脚本尽量接近真实用户行为压出来的数据才有参考价值。5.2 常见报错速查表把这一两年我见过的高频问题整理成一张表现象可能原因排查与解法Pod 一直 Pending节点资源不足、污点不匹配、PVC 未绑定kubectl describe pod 看事件检查节点容量、Toleration、PV 状态Pod ContainerCreating 卡住镜像拉取失败、持久卷挂载失败、CNI 网络未就绪describe 查事件docker pull 手动验证检查 kubelet 日志CrashLoopBackOff容器启动后立刻退出kubectl logs 看启动日志检查启动命令、环境变量、数据库连接ImagePullBackOff镜像不存在、私有仓库认证失败docker pull 手动验证配置 imagePullSecretsno preemption victims found for incoming pod高优先级 Pod 抢占失败检查节点容量与 PriorityClass降低 request 或扩容Nacos 客户端无法注册9848 gRPC 端口未放通检查 Service 暴露端口、安全组、防火墙Rancher Desktop 报 Docker API 连接失败运行时不是 dockerd或 context 不对切换运行时为 dockerd(moby)docker context use rancher-desktop压测时 Pod OOM 重启内存 limit 设置过低kubectl top pods 看实际占用调整 requests 和 limits优化 JVM 堆这张表不能只当背题用要理解背后的排查顺序先 describe 看事件再 logs 看应用日志再思考网络、存储、安全组这些外围因素。5.3 实操中沉淀下来的几条经验最后分享几条不太好写进文档、但实际帮了我很多的经验。第一单节点集群虽然简单但资源竞争问题更明显。一个 Pod 没有设置 resources另一个 Pod 突然把内存吃满kubelet 会按 QoS 顺序杀掉低优先级的 Pod。所以单节点更要给每个 Deployment 写清 requests 和 limits。第二NodePort 用得很爽但高并发压测时容易撞到系统限制。比如 Linux 的端口范围、conntrack 表、文件句柄数都要提前检查。可以先执行 sysctl net.ipv4.ip_local_port_range 和 sysctl net.netfilter.nf_conntrack_max 看下默认值再决定是否需要调大。第三Rancher 的 UI 适合浏览和演示但做变更时最好还是用 kubectl apply 或 Terraform 这类 IaC 工具把 YAML 存到 Git 里。UI 上点出的配置很容易流失过几个月没人记得谁改了什么。第四也是最重要的迁移和压测这类动作一定要写操作单和回滚步骤。你面对的不是“K8s 容器管理”这一个孤立任务而是一整个业务系统的可靠性承诺。有了明确步骤和回滚预案出问题时至少不会慌。我个人的体会是容器技术本身并不复杂复杂的是在真实场景里做取舍而取舍的底气就来自你对 Pod 原理和 Rancher 这类工具的熟悉程度。