一、迁移之前:先搞清楚你的微服务"健不健康"
很多团队上来就干——先把服务打包成镜像再说。结果呢?镜像构建失败、启动报错、服务间通信不通——三天下来,进度为零。
上云之前,必须先做资产梳理和兼容性评估。
1.1 梳理微服务依赖全景
你需要一张完整的服务调用关系图:
| 梳理项 | 为什么重要 |
|---|---|
| 每个服务的上下游依赖 | 决定迁移顺序,先迁移无依赖的叶子节点 |
| 端口占用情况 | 容器内端口不能冲突,硬编码端口是大忌 |
| 资源需求(CPU/内存峰值) | 决定节点规格和弹性伸缩策略 |
| 数据存储方式 | 本地文件?数据库?缓存?决定持久化方案 |
| 启动脚本与配置位置 | 影响Dockerfile编写和ConfigMap挂载 |
1.2 排查"容器不友好"因素
这一步直接决定迁移成败。重点标记以下问题:
| 问题类型 | 典型表现 | 后果 |
|---|---|---|
| 硬编码配置 | 数据库IP、端口写死在代码里 | 容器启动即失败 |
| 本地文件持久化 | 上传文件写到/local/upload | 容器删除,数据丢失 |
| 依赖宿主机服务 | 本地Redis、本地Nacos | 容器内连不上 |
| 后台运行脚本 | nohup、&启动 | 容器启动后立即退出 |
| 固定端口硬编码 | 每个服务都写死8080 | 同一节点端口冲突 |
一句话:先治好"病",再上"云"。带病上云,云也救不了你。
二、Spring Cloud迁移:六大核心改造点
Spring Cloud是微服务界的"老大哥",但它的很多设计是为虚拟机时代量身定做的。搬到容器平台,必须做六项核心改造。
2.1 配置外部化:剥离一切硬编码
这是最关键的一步。Spring Cloud应用里那些写死的数据库地址、Redis地址、第三方接口URL——全部要剥离。
改造原则:配置与镜像分离,运行时注入。
具体做法:将所有硬编码配置迁移到配置文件,用环境变量占位符替换。数据库地址不再是jdbc:mysql://192.168.1.100:3306,而是${MYSQL_HOST}:${MYSQL_PORT}。容器启动时,通过环境变量或ConfigMap动态注入。
这一步不做,你的镜像在任何环境都跑不起来。
2.2 注册中心迁移:从Eureka到K8s Service
Spring Cloud默认用Eureka做服务发现。但在K8s集群里,每个Service自带服务发现能力——Pod的IP虽然会变,但Service的DNS名称是固定的。
迁移方案有三种:
| 方案 | 做法 | 适用场景 |
|---|---|---|
| 方案A:直接用K8s Service | 去掉Eureka,服务间通过K8s Service名调用 | 新项目,推荐 |
| 方案B:保留Eureka/Nacos | 在K8s里部署Eureka集群,服务仍用Eureka注册 | 渐进迁移,风险低 |
| 方案C:用ASM服务网格 | Sidecar自动接管服务发现,代码零改动 | 大规模集群,推荐 |
我的建议:不知道选什么,就选方案B。 先在K8s里部署一套Nacos或Eureka集群,让服务慢慢切换,不要一刀切。
2.3 配置中心迁移:Nacos/Apollo → K8s ConfigMap
Nacos和Apollo是Spring Cloud的标配配置中心。迁移到K8s后,有两条路:
| 方案 | 优势 | 劣势 |
|---|---|---|
| 保留Nacos/Apollo | 改动小,配置热更新依然支持 | 多一套组件要维护 |
| 迁移到ConfigMap/Secret | 原生集成,无需额外组件 | 配置变更需重启Pod |
实际操作中,建议"核心配置用ConfigMap,动态配置保留Nacos"。数据库连接、Redis地址这种不变的配置丢进ConfigMap;限流阈值、降级开关这种经常变的配置留在Nacos。
2.4 网关改造:Zuul/Gateway → K8s Ingress
Spring Cloud Gateway或Zuul是流量入口。在K8s里,这个角色由Ingress Controller(如Nginx Ingress)接管。
改造要点:
| 改造项 | 做法 |
|---|---|
| 路由规则 | 从Gateway的route配置迁移到Ingress的path规则 |
| 限流熔断 | 从Sentinel/Hystrix迁移到Ingress的限流插件或ASM |
| 灰度发布 | 从Gateway的Header路由迁移到Ingress的Canary或加权路由 |
核心原则:Gateway不再做流量治理,只做业务路由。流量治理交给Ingress或服务网格。
2.5 分布式事务:Seata的容器化适配
Seata是Spring Cloud微服务里最难迁移的组件——因为它依赖TC(事务协调器)的稳定性。
容器化注意事项:
| 事项 | 说明 |
|---|---|
| TC高可用 | 必须部署3节点以上TC集群,跨AZ分布 |
| 存储模式 | 容器环境建议用DB模式(MySQL存储 undo_log),不要用file模式 |
| 网络策略 | TC与各微服务之间需放通端口,配置NetworkPolicy |
2.6 启动方式改造:前台运行,拒绝后台
Spring Boot应用默认java -jar是前台运行,没问题。但如果你的启动脚本里有nohup、&、> /dev/null 2>&1——全部删掉。
容器的设计哲学是"一个容器一个进程,进程挂了容器就退出"。后台运行的脚本会导致容器启动后立即"假死",K8s以为它正常运行,实际上服务根本没起来。
三、Dubbo迁移:四大核心注意事项
Dubbo和Spring Cloud的迁移逻辑类似,但有几个Dubbo特有的坑。
3.1 注册中心切换:ZooKeeper → Nacos/K8s Service
Dubbo默认用ZooKeeper做注册中心。迁移时:
| 方案 | 做法 | 风险 |
|---|---|---|
| 保留ZooKeeper | 在K8s里部署ZK集群 | 维护成本高,不推荐 |
| 切换到Nacos | Dubbo 2.7+原生支持Nacos注册 | 推荐,改动小 |
| 切换到K8s Service | 用K8s原生服务发现替代注册中心 | 推荐,但需改配置 |
3.2 直连模式的陷阱
Dubbo有个"直连模式"——消费者绕过注册中心直接连提供者。在虚拟机环境里这是调试神器,但在K8s里是灾难:Pod IP随时在变,直连模式根本连不上。
迁移时必须关闭直连模式,强制走注册中心或K8s Service。
3.3 多协议支持的容器适配
Dubbo支持Dubbo、REST、gRPC等多协议。在容器环境里:
| 协议 | 容器适配建议 |
|---|---|
| Dubbo协议 | 默认端口20880,需在Service中暴露,配置ContainerPort |
| REST协议 | 直接复用K8s Ingress,最省心 |
| gRPC协议 | 需配置HTTP/2支持,Ingress需开启gRPC代理 |
3.4 超时与重试策略调整
虚拟机内网延迟通常在1ms以内,K8s集群内延迟可能到3-5ms。Dubbo的默认超时(通常3秒)可能不够。
建议:将超时时间从3秒调整到5-10秒,重试次数从3次降到2次。 避免因网络抖动导致大量超时。
四、镜像构建:多阶段构建是铁律
不管Spring Cloud还是Dubbo,镜像构建的原则完全一致。
| 最佳实践 | 说明 | 效果 |
|---|---|---|
| 多阶段构建 | 构建阶段用Maven镜像编译,运行阶段用 slim JDK镜像 | 镜像从800MB降到80MB |
| 轻量基础镜像 | 优先用alpine或slim版本,别用完整Ubuntu | 体积小、漏洞少 |
| 缓存依赖层 | 先复制pom.xml下载依赖,再复制源码 | 后续构建提速50% |
| 标签规范化 | 服务名:版本号-Git提交哈希 |
方便回滚和追溯 |
特别提醒:镜像中不要放任何敏感信息。 密码、密钥、证书全部用K8s Secret注入,不要写在Dockerfile里,不要写在环境变量里。
五、部署策略:反亲和性是生死线
微服务上云后,最怕的不是单点故障——是"一锅端"。
一个AZ挂了,如果你的3个订单服务实例全在同一个AZ,那就是全灭。
必须配置反亲和性调度策略:
| 策略 | 配置 | 效果 |
|---|---|---|
| AZ反亲和 | topologyKey: failure-domain.beta.kubernetes.io/zone |
实例分布在不同AZ |
| 主机反亲和 | topologyKey: kubernetes.io/hostname |
实例分布在不同节点 |
配置完成后,你会看到一个神奇的现象:加1个实例→自动去AZ3,再加1个→自动去AZ2。 这就是生产级的智能调度。
六、弹性伸缩:HPA + CA双引擎
微服务的流量是波动的。大促期间QPS翻10倍,平时可能只有峰值的10%。
| 层级 | 触发条件 | 响应速度 | 作用 |
|---|---|---|---|
| HPA(Pod级) | CPU > 70%持续2分钟 | 秒级 | 自动增减Pod数量 |
| CA(节点级) | Pod处于Pending状态 | 分钟级 | 自动增减节点 |
这两层配合起来的效果是:流量洪峰来了→HPA秒级加Pod→Pod数超过节点容量→CA分钟级加节点→流量退去→自动缩容→账单自动下降。
某电商团队实测:大促期间自动扩容150个节点,活动结束后自动缩回,零人工干预,运维成本降低60%。
七、安全加固:容器不是保险箱
很多团队觉得"上了云就安全了"——大错特错。容器的攻击面比虚拟机更大。
| 安全措施 | 做法 | 价值 |
|---|---|---|
| 文件系统只读 | 容器根文件系统设为只读,只在挂载卷上写入 | 防止恶意进程篡改系统文件 |
| Cap Drop | 删除不需要的Linux Capabilities(如Net_raw) | 遵循最小特权原则 |
| PID限制 | 限制每个容器的进程数(如不超过50) | 防止fork炸弹和横向移动 |
| 密钥管理 | 用K8s Secret或密钥管理工具,禁止环境变量存密码 | 敏感信息不明文 |
| 镜像扫描 | 推送镜像前自动扫描CVE漏洞 | 上线前拦截高危漏洞 |
八、写在最后:微服务上云,是一场"有准备的战役"
回顾一下,微服务上云的核心不是技术问题——是规划问题。
| 阶段 | 核心任务 | 常见翻车点 |
|---|---|---|
| 迁移前 | 资产梳理+兼容性评估 | 不评估就干,三天回到原点 |
| 改造期 | 配置外部化+注册中心迁移 | 硬编码没清干净,镜像跑不起来 |
| 构建期 | 多阶段构建+镜像扫描 | 镜像800MB,拉取超时 |
| 部署期 | 反亲和性+健康检查 | 单AZ部署,一挂全挂 |
| 运行期 | 弹性伸缩+安全加固 | 不限PID,被横向移动 |
Spring Cloud也好,Dubbo也好,上云不是终点,是新起点。 你的代码还是那些代码,但运行它的底座变了——从虚拟机变成了容器,从手动运维变成了自动编排,从单打独斗变成了集群作战。
别等出了事故才想起来上云。现在就去梳理你的微服务依赖,做一次兼容性评估,改掉那些硬编码配置。
你的微服务值得跑在一个更稳、更快、更安全的平台上。而这一切,从今天的第一次评估开始。