单节点K8s若依微服务平滑迁移至阿里云ECS实战与压测验证

发布时间:2026/9/18 6:06:25
单节点K8s若依微服务平滑迁移至阿里云ECS实战与压测验证 我先理一下这个项目的来龙去脉。一个单节点的 k8s 环境上面跑着若依微服务整套需求是几乎不停服、不丢数据地迁到阿里云 ECS迁完还要上 jmeter 压测验证承载能力。这种项目最怕的不是技术难而是细节多迁移过程中的状态同步、容器编排的资源配置、压测时的瓶颈定位哪一个环节不考虑清楚后面就是连环坑。先把整套东西的脉络按我在实际项目里的操作顺序捋一遍方便大家对照自己手头的情况。1. 项目背景与企业选型思路说实话刚接这个需求的时候我也没觉得简单。单节点 k8s 本身就是个偏 Demo 的形态但偏偏承载的是若依微服务整套业务环境这就让“单节点”三个字变得不那么轻松了。更别提后面还有一条硬指标迁移过程中业务几乎不停服、数据不能丢迁完还要扛得住高并发压测。所以这个项目本质上不是“把容器搬到另一台机器”而是“怎么让一套正在跑的业务像一个整体一样平滑挪窝”。1.1 k8s 和 docker 到底啥关系很多刚入门的人会问 k8s 是不是就是 docker 的升级版其实两者根本不是一回事。docker 解决的是“怎么把应用打包成一个标准镜像、怎么跑起来”的问题它提供的是容器运行时能力而 k8s 解决的是“一堆容器怎么调度、怎么保证数量、怎么对外暴露、怎么发现彼此、怎么滚动更新”的问题它是容器编排系统。可以这么理解docker 是集装箱它把货物应用标准化封箱k8s 是整个港口管理系统它决定哪个集装箱放哪个泊位、什么时候装船、船坏了怎么换。没有 dockerk8s 也能靠 containerd、CRI-O 之类的运行时跑容器没有 k8sdocker 只能手动一个一个启动容器规模小还撑得住微服务一多就乱套了。在这个项目里我用的是 containerd 作为容器运行时配合 kubeadm 部署的 k8s。选 containerd 而不是 docker-shim主要是考虑到新版本 k8s 对 dockershim 已经移除与其后面再折腾迁移不如从开始就用 containerd 干净利落。1.2 为什么企业项目更倾向 k8s 而不是直接裸机部署若依微服务整套环境拆开来看涉及的模块有认证授权、系统管理、定时任务、监控、网关等多个服务每个服务至少两个副本才敢谈高可用。如果直接在 ECS 上手动部署需要解决的问题是服务之间的调用地址怎么维护、某个实例挂了怎么自动拉起、流量怎么在多个副本之间负载均衡、配置变更怎么批量生效。这些问题 k8s 都能给出标准答案。Deployment 保证副本数量Service 做服务发现和负载均衡ConfigMap 统一管理配置探针机制自动摘除故障实例。用 k8s 之后运维的关注点从“怎么管理一个个进程”变成了“怎么描述想要的最终状态”这中间的效率差距在故障处理和版本发布时体现得尤其明显。2. 单节点 k8s 集群搭建与初始化准备很多教程喜欢一上来就敲 kubeadm init但企业项目不行。准备工作没做好后面所有步骤都会变成补救工作。迁移到阿里云 ECS 之前我在本地和云上分别反复验证过一套完整的单节点搭建流程这里把关键步骤和踩过的坑都写清楚。2.1 服务器规划与基础环境调整先说配置。单节点 k8s 跑若依微服务整套加上监控、日志、镜像仓库这些辅助组件建议最低 8核16G条件允许就上 16核32G。ECS 建议选通用型 g7 或 g8i 这类实例云盘用 ESSD性能比高效云盘强不少后面压测的时候 IO 不会拖后腿。操作系统我选的 CentOS 7.9虽然已经接近生命周期尾声但企业存量环境里还是比较常见如果你们用的是 Ubuntu 或者 Anolis 也没问题核心操作一样只是包管理命令有差异。基础环境调整要处理四件事关闭 swap。kubelet 默认要求关闭 swap否则会报错。swapoff -a之后还需要注释掉 /etc/fstab 里的 swap 挂载行否则重启又回来了。加载内核模块。overlay和br_netfilter需要手动加载前者是容器镜像分层存储的基础后者是 iptables 对 bridge 流量做 NAT 的依赖。修改内核参数。net.bridge.bridge-nf-call-iptables设为 1net.ipv4.ip_forward设为 1vm.swappiness设为 0这些参数直接关系到 Pod 网络通信和宿主机转发能力。配置 hosts 解析。虽然是单节点也建议把节点的主机名和 IP 写进 /etc/hosts后续加节点的时候能少踩很多 DNS 解析的坑。2.2 containerd 与 kubeadm 安装部署容器运行时我选的是 containerd 1.7.x。安装方式直接用 yum 配置阿里云镜像源就行这里有一个细节containerd 装完默认配置文件是空的需要执行containerd config default /etc/containerd/config.toml生成默认配置然后把SystemdCgroup改成true。这个参数非常关键。kubelet 默认使用 cgroupfs 驱动但系统里的 systemd 也管理 cgroup两边不一致会导致资源统计错乱甚至节点不稳定。标准做法是 containerd 的 SystemdCgroup 和 kubelet 的 cgroup-driver 都统一用 systemd两边对齐之后整个节点的资源管理才正常。kubeadm、kubelet、kubectl 三个组件的版本要一致我用的是 1.28.2一个比较稳定的版本。安装完成后先不要急着 init把镜像源切到国内加速通过kubeadm config images pull预先拉取镜像确认镜像能拉下来再进行初始化。2.3 集群初始化的关键参数与网络插件选型初始化命令如下几个参数值得单独说明kubeadm init \ --apiserver-advertise-address192.168.1.100 \ --image-repository registry.aliyuncs.com/google_containers \ --kubernetes-version v1.28.2 \ --service-cidr10.96.0.0/12 \ --pod-network-cidr10.244.0.0/16--apiserver-advertise-address要填 ECS 的内网 IP不要填公网 IP集群内部组件之间的通信走内网速度快也安全。--service-cidr是 Service 的虚拟 IP 网段--pod-network-cidr是 Pod 的网段这两个网段不能和 VPC 网段冲突否则后面路由会出问题。网络插件我选的 Flannel以 VXLAN 模式运行。单节点环境用 Flannel 足够简洁不需要像 Calico 那样引入 BGP 和复杂的网络策略如果后续要扩展成多节点并且对网络策略有要求再换 Calico 也不迟两者在 k8s 里的切换成本并不高。初始化成功后按提示把 kubeconfig 复制到 ~/.kube/config然后安装 Flannelkubectl apply -f https://raw.githubusercontent.com/flannel-io/flannel/master/Documentation/kube-flannel.yml等所有 Pod 都处于 Running 状态kubectl get nodes看到节点状态为 Ready单节点集群的基础部分就算完成了。有一点要提醒单节点集群默认不会在控制平面节点上调度业务 Pod需要去掉这个污点kubectl taint nodes --all node-role.kubernetes.io/control-plane-否则若依微服务的 Pod 永远处于 Pending 状态这是新手最容易踩的一个坑。3. 若依微服务容器化与资源编排实践集群搭好只是第一步真正的难点是把若依微服务整套环境搬进 k8s 里跑起来还要保证各个模块之间能互相通信。这一步做不好后面迁移就是空中楼阁。3.1 若依微服务模块拆分与镜像制作若依微服务版的模块不少我在这个项目里实际用到的核心服务包括ruoyi-gateway统一网关入口负责路由转发和鉴权ruoyi-auth认证授权中心处理登录和 token 签发ruoyi-system系统管理模块用户、角色、菜单等基础数据ruoyi-job定时任务模块ruoyi-monitor服务监控模块ruoyi-file文件上传下载模块ruoyi-gen代码生成模块以及对应的 Nacos 注册中心、Redis、MySQL 等基础组件镜像制作遵循一个原则用多阶段构建把依赖安装和编译打包阶段与最终运行阶段分开。以 ruoyi-gateway 为例Dockerfile 的核心结构是先在 maven 镜像里做构建再用 jre 镜像做运行最终镜像只保留一个可执行 jar 包体积小、启动快、安全风险也低。这里有一个实操上的细节若依的多个微服务的基础镜像最好统一我用的是 eclipse-temurin:8-jre-alpine 或类似的精简 JRE 镜像。统一基础镜像的好处是集群里每个节点只需要缓存有限的几层镜像磁盘占用少启动时镜像拉取速度也快很多。3.2 资源清单的核心结构Deployment、Service、ConfigMap每个微服务我基本都维护三份配置Deployment 描述运行状态Service 提供稳定的访问入口ConfigMap 存放外部化配置。以 ruoyi-auth 为例Deployment 的关键字段包括replicas设置副本数若依的每个服务我给 2 个副本网关给了 3 个。单节点下副本数受限于机器配置但 2 个副本能保证单台节点上某个 Pod 被误删时有快速替换的余地。resources.requests和resources.limits必须设置。requests 是调度时的依据limits 是运行时资源上限。JVM 服务尤其要注意limits.memory 不能设置得太死否则 JVM 还没触发 GC 就被 cgroup OOM Kill 了。我给每个服务设置了requests.memory: 512Milimits.memory: 1Gi同时通过 JAVA_OPTS 环境变量控制 JVM 堆大小在 512MB 以内。readinessProbe和livenessProbe是迁移能不能做到不停服的关键。就绪探针用 HTTP 请求服务的 actuaor/health 接口只有接口返回 200Service 才把流量转发到这个 Pod存活探针负责发现异常并重启容器。两个探针的initialDelaySeconds要结合服务实际启动时间设置若依的服务一般 30 到 60 秒能起来我设的是initialDelaySeconds: 60给它充足的预热时间。Service 的资源清单则比较简单核心是通过 selector 关联到对应的 Pod暴露端口 8080 等业务端口。网关的 Service 需要额外设置为 NodePort 类型方便外部访问和后续的流量切换。3.3 有状态组件的高可用与数据持久化方案MySQL 和 Redis 这类有状态组件和上面的无状态微服务不一样。它们不能随便调度、不能随便重启数据丢了就是事故。所以在整个集群设计里我不建议把 MySQL、Redis 直接以普通 Deployment 方式运行而是采用 StatefulSet 配合持久化存储。单节点环境没有云盘动态供给需要手动创建 PV 和 PVC。大致流程是在 ECS 上挂载一块独立的数据盘格式化为 ext4然后以 hostPath 或 local 类型作为 PV 的存储后端再创建 PVC 绑定到这个 PV 上。MySQL 的 StatefulSet 挂载这个 PVC数据就落在宿主机磁盘上Pod 重建后数据还在。这种方式虽然不如云数据库省心但考虑到若依环境本身的定位这么做既控制了成本也能保证数据安全。如果你们后续有条件可以考虑将 MySQL 迁到云数据库 RDSRedis 迁到云 Redis但那是另一个话题了。就本项目而言把数据目录挂到独立数据盘上已经足够支撑从 A 环境到阿里云 ECS 的不丢数据迁移。4. 不停服、不丢数据的迁移方案设计迁移是整个项目里最让我谨慎的一块。所谓“不停服”是相对的实际上要做的是把可感知的中断时间压缩到秒级甚至更低。方案上我采用的是典型的“双跑-切换-校验-回收”四步法每一步都有明确的验证标准。4.1 迁移前的基线信息采集与同步策略第一步是把源环境的所有状态信息摸清楚。我整理了一个信息采集清单主要包括所有微服务的镜像版本和启动参数MySQL 中所有数据库的列表和每个库的大小Redis 中的缓存数据是否有持久化要求Nacos 中的配置文件列表ECS 上已有的定时任务脚本这些信息决定了同步策略。以数据库为例短时间内业务写入量如果较大就选用不停机同步方案我用的工具组合是 mysqldump 做全量备份 通过 binlog 增量同步。操作前确认源 MySQL 开启了 binlog且格式为 ROW 模式这样才能精确追平增量数据。全量导出的命令大致如下mysqldump -uroot -p --single-transaction --routines --events --triggers --databases ruoyi ruoyi_full.sql注意--single-transaction参数很关键它在 InnoDB 引擎下通过事务快照导出数据过程中不会锁表也就不会打断源端的业务写入。4.2 按从属关系分批迁移先基础组件再微服务整个迁移的顺序不是从第一个服务开始逐个迁而是按照依赖关系从底层往上迁。我定的迁移顺序是先迁基础设施MySQL、Redis、Nacos再迁无状态的业务微服务最后切网关入口。基础设施迁移完成后将目标环境的 Nacos 配置更新为指向新环境的基础组件地址然后启动第一批迁移的微服务比如 ruoyi-system、ruoyi-auth。此时它们访问的是新环境的数据库和 Redis但业务仍在源环境运行两边同时跑只是新环境还没有流量进来。等所有微服务在新环境都正常启动服务之间注册发现也正常了再启动网关。网关启动后仍然是 NodePort 方式不直接接管对外流量只在内网验证各服务之间的调用链路。这个过程里有一个常被忽略的地方微服务里如果嵌入了对 IP 或域名的硬编码迁移后需要全部检查一遍。若依的配置里有 nacos 地址、redis 地址、mysql 地址这些都要通过 ConfigMap 或环境变量变成配置项避免打散在 jar 包里改不动。4.3 流量切换DNS 切换与分钟级回滚预案流量切换是整个迁移的临门一脚我的策略是尽可能利用 DNS TTL 做平滑切换。提前将业务域名的 TTL 改小比如从默认的 600 秒降到 60 秒。在切换时间窗口内将 DNS 解析从源环境负载均衡切换到新 ECS 的公网 IP 或负载均衡地址。切换之后不要马上回收源环境资源至少要观察一两个业务周期确认新环境没有报错、数据库数据持续正常增长之后再关停源环境。同时准备回滚预案一旦新环境出现短时间内无法解决的问题立刻把 DNS 切回源环境整个过程理论上不超过两分钟。这套方案我实测下来业务感知的真正中断时间只有 DNS 解析生效的那几十秒配合 TTL 提前调小几乎可以做到用户无感知。数据层面的校验则靠对比两侧 MySQL 的增量位点和关键业务表的数据量确认一致后再回收旧环境。5. 监控告警与高并发压测验证迁移完成只是项目交付的上半场下半场必须用数据证明新环境扛得住业务量。我在目标环境搭建了 Prometheus Grafana 监控体系并用配套的 jmeter 脚本做了多轮压测。5.1 Prometheus 监控集群与业务指标监控体系分三层节点层、容器层、应用层。节点层用 node-exporter 采集 CPU、内存、磁盘、网络等宿主机指标容器层用 kubelet 内置的 cAdvisor 接口采集每个 Pod 的 CPU、内存使用情况应用层则在若依的每个微服务里暴露 actuator 端点通过 micrometer 将 JVM 指标接入 Prometheus。Prometheus 的部署我直接用了 kube-prometheus-stack 这个 Helm Chart它会把 Prometheus、Grafana、Alertmanager、node-exporter 一次性装好并且自带一整套告警规则。装完之后需要调整 Service 的访问方式把 Grafana 的端口暴露出来登录后导入 Kubernetes 相关的官方 Dashboard就能直观看到集群的整体水位。在压测之前我为关键的告警项设置了阈值。比如节点内存使用率超过 85% 持续 5 分钟报警Pod 频繁重启报警JVM 堆内存使用率超过 90% 报警。这些告警在压测过程中至关重要一旦触发可以马上停止压测诊断资源瓶颈避免把业务搞挂。5.2 jmeter 压测脚本设计与执行策略压测脚本是压测人员项目中自称 peseman配套提供的但作为运维方不能完全不管。我的做法是先拆解脚本里的请求链路搞清楚压测覆盖了哪些接口、预期并发量是多少、有没有依赖登录态或前置数据避免上来就盲目拉高并发结果把压测机自己先打挂了。压测执行我一般分三个阶段单接口压测分别压测网关、认证、系统管理等核心接口确认每个服务的独立瓶颈比如 MySQL 的 max_connections、Redis 的连接数、某个服务的线程池配置。混合场景压测按照业务比例同时压多个接口模拟真实用户操作链路。峰值压测将并发逐步拉高到预期的 1.5 倍观察系统在过载状态下的表现同时也验证弹性扩容能力。压测过程中要重点盯几个指标TPS、响应时间曲线、错误率、节点 CPU 内存、JVM GC 频率。如果 TPS 上不去先看是不是网关成了瓶颈如果响应时间不规律地抖动多半是 GC 停顿或者数据库连接池满了。压测时命令行的输出结合 Prometheus 的实时曲线一起看定位问题的效率会高很多。5.3 几次值得记录的压测问题和调优这个项目压测过程中遇到过一个典型的 JVM 问题。第一次压测到 200 并发的时候部分接口的响应时间从 200ms 左右飙升到 2 秒以上但节点 CPU 并没有跑满。通过 Grafana 看 JVM 指标发现老年代内存持续走高GC 频率快速增加Full GC 平均停顿超过 1 秒典型的 GC 瓶颈。排查原因limits.memory 设的是 1Gi但我一开始通过 JAVA_OPTS 设置的堆大小是 768M加上 Metaspace、线程栈和 JVM 本身的开销容器内存吃紧JVM 只能频繁触发 Full GC 来腾空间。后来把堆大小调到 512M并开启了-XX:UseG1GC参数压测结果立刻好转响应时间恢复稳定。另外一次是 MySQL 连接数被打满的问题。若依的服务每个实例都配置了自己的数据库连接池多副本加上压测的高并发直接把 MySQL 的 max_connections 耗尽了。解决思路是两层一是调整连接池上限结合服务实际 QPS 合理设置二是提高 MySQL 的 max_connections 并调整对应的 open_files_limit。这里要提醒连接数不是越大越好连接池设太大会互相争抢数据库资源需要结合压测结果反复调优。6. 常用命令、管理工具与面试高频考点项目收尾之后我把这段时间里最常用到的命令、工具和面试中高频出现的问题都整理了一遍。这些内容既方便自己做知识沉淀也给团队做了一次内部分享。6.1 生产环境必会的 kubectl 命令清单kubectl 是操作 k8s 最核心的命令行工具但实际用到的命令其实很集中。我整理了一份高频清单:# 查看集群节点状态和事件 kubectl get nodes -o wide kubectl describe node node-name # 查看 Pod 状态和日志 kubectl get pods -A -o wide kubectl logs -f pod-name -n namespace # 进入 Pod 容器排障 kubectl exec -it pod-name -n namespace -- /bin/sh # 查看 Service 和 Endpoints kubectl get svc -A kubectl get endpoints -A # 查看资源占用 kubectl top nodes kubectl top pods -A # 调试网络转发 kubectl get svc service-name -o yaml排查问题有一个标准顺序先看 Pod 状态Pending 大概率是资源不足或调度失败CrashLoopBackOff 大概率是启动即崩溃ImagePullBackOff 大概率是镜像拉取失败然后看 describe 和 events最后才有针对性地看日志和网络。大多数问题都是通过 events 找到根因的所以kubectl describe是一个非常好用的诊断工具。6.2 提升效率的 k8s 管理工具命令行虽强但企业日常巡检和维护还是需要更直观的工具。我给团队推荐的是 k9s一个终端下的可视化工具直接通过 kubectl 读取集群信息以列表形式展示 Node、Pod、Service、Deployment 等资源支持快捷键切换资源类型、查看日志、进入容器、编辑资源效果比反复敲 kubectl 高效太多。Web 控制台方面Kubernetes Dashboard 依然是覆盖面最广的选择。装好之后通过 token 登录可以看到集群所有资源的运行状态并且支持在界面上直接查看 Pod 日志、编辑 Deployment。生产环境建议把它暴露给运维专用网络不要直接绑定公网安全第一。更专业的场景比如需要给业务方提供自服务能力可以考虑 Rancher。它自带多集群管理、权限控制和应用商店适合团队的 long-term 管理但对单节点场景来说引入 Rancher 会显得重。我的建议是单节点环境用 Dashboard k9s 足够多集群再上 Rancher 不迟。6.3 关于 k8s 面试的高频考点在项目部招人的时候我也会问几个 k8s 相关的问题来快速判断候选人水平。维护这篇文章的读者如果有面试需求可以参考这些考点。第一个是 k8s 和 docker 的区别。答案不是简单的“一个是容器一个是编排”而要落到运行时与调度的分层关系上能说清楚 containerd、CRI、kubelet 之间如何协作基本就能筛掉一半人。第二个是 Pod 的生命周期和服务发现原理。Pod 里的容器共享同一个网络命名空间所以 localhost 就能互相访问Service 通过 selector 选择一组 Pod然后通过 kube-proxy 维护的 iptables/IPVS 规则做负载均衡。能把这个链路解释透彻的人基础理论不会差。第三个是探针的区别。readinessProbe 决定流量是否分发到 PodlivenessProbe 决定容器是否被重启startupProbe 用于启动缓慢的应用。很多候选人只背概念但说不清三者之间的先后关系和典型场景实际操作经验就会露馅。第四个是资源限制和调度的关系。requests 和 limits 分别影响调度决策和运行限制QoS 类别的划分以及 Deployment 滚动更新策略这些都是生产环境的加分项。7. 常见问题速查与最终的几点心得整个项目踩过的坑不少我把典型的几个问题整理成了速查表方便大家直接收藏备用。现象常见原因排查与解决思路Pod 一直 Pending节点资源不足、污点未移除、PVC 未绑定kubectl describe pod查看事件检查节点资源、污点、PV/PVC 状态镜像拉取失败 ImagePullBackOff镜像地址错误、仓库需要认证、网络不通kubectl describe pod查看具体错误码配置 imagePullSecrets 或切换镜像源服务之间访问不通Service 选择器不匹配、网络插件问题检查 endpoints 是否有地址kubectl get endpoints为空则说明 selector 没对上 Pod节点 NotReady网络插件异常、kubelet 停止、资源耗尽查看 kubelet 日志重启 kubelet检查网络插件容器状态压测时响应时间飙高JVM 堆过小、连接池满、数据库锁竞争看 Grafana 中 JVM GC 指标、数据库连接数、慢查询日志滚动更新长时间不完成就绪探针失败、新副本无法接收流量查看新副本日志检查 readinessProbe 的端口和路径、initialDelaySeconds 是否过短SVC 无法从外部访问NodePort 端口被安全组拦截、宿主机防火墙检查安全组是否放行端口宿主机 firewalld 状态访问节点的内网 IP 而非公网 IP关于 k8s 面试的高频考点我前面已经整理了四个实际操作过程中还会遇到很多边界问题这里就不再展开大家的关注点放在原理和实操结合上基本都能应对。最后再分享一点个人体会。这次从单节点若依微服务环境到阿里云 ECS 的迁移本质上是一次“容器化 平台化”的重塑。整个过程下来我对 k8s 的认知也从“会敲命令”提升到了“理解设计”深刻体会到探针、资源配额、滚动更新这些机制设计的精妙之处。如果你也正准备把一个业务系统迁到 k8s 上我的建议是不要一上来就追求多节点高可用先把单节点环境里的每个细节吃透理解 Pod 是怎么调度、服务是怎么发现、数据是怎么持久化的再逐步扩展规模。磨刀不误砍柴工这套底层认知积累到后面价值会远超想象。

关于本文作者

来自尧图内容编辑团队

尧图内容编辑团队 内容团队

尧图内容编辑团队

本文由尧图网络内容编辑团队执笔。团队由资深项目经理、前端工程师与设计师组成,所有内容均来自亲手交付的真实项目,先讲清问题、再给出可落地的解法。尧图深耕北京网站建设十年,服务过京华建材集团、智造科技等各行业客户,把一线经验沉淀为可复用的行业观察。

  • 十年建站经验,覆盖建材、制造、服务、文创等
  • 项目经理把关选题与事实准确性
  • 工程师与设计师联合撰写专业细节
  • 统一编辑规范,保证文风与排版一致
  • 每月复盘转化数据,迭代选题方向

延伸阅读

相关资讯与近期热门内容

深度阅读推荐

建站决策前值得细读的三篇

网站改版的5个关键决策
2024-08-12

网站改版的5个关键决策

什么时候该改版、改到什么程度、如何避免流量掉光,京华建材集团改版复盘给出答案。

获取专属建站方案

看完文章,把您的行业与预算告诉我们,免费获取一份量身定制的官网建设方案与报价。

立即免费咨询