《从传统部署到云原生:华为云 CCE 容器化改造实战记录》
从传统部署到云原生:华为云 CCE 容器化改造实战记录
一、背景
笔者所在团队此前维护一套 Java Spring Boot + MySQL 的业务系统,部署在自建机房的三台物理服务器上。随着业务量增长,传统部署方式的痛点越来越明显:扩容需要手动装环境、版本回滚靠人工切换、服务器故障恢复耗时数小时。经过调研对比,我们最终选择华为云 CCE(云容器引擎)作为容器化改造的目标平台,本文记录整个改造过程中的关键步骤和踩坑经验,供有同样需求的同行参考。
二、改造前的架构
改造前的部署架构比较简单:
- 3 台物理服务器,通过 Nginx 做负载均衡
- 每台服务器上手动部署 JDK 1.8、JAR 包、Nginx
- MySQL 主从部署在其中两台服务器上
- 配置文件散落在各服务器本地,修改需逐台登录
这种方式最大的问题是环境不一致:某台服务器上的 JDK 小版本不同,导致一次线上故障排查了三个小时。这也是推动我们容器化的直接原因。
三、容器化改造步骤
3.1 应用 Docker 化改造
第一步是将 Spring Boot 应用容器化。编写 Dockerfile 如下:
FROM openjdk:8-jre-slim
WORKDIR /app
COPY target/app.jar /app/app.jar
ENV JAVA_OPTS="-Xms512m -Xmx512m -Dspring.profiles.active=prod"
EXPOSE 8080
ENTRYPOINT ["sh", "-c", "java $JAVA_OPTS -jar /app/app.jar"]
几个关键点:
- 选用 jre-slim 而非完整 JDK 镜像,镜像体积从 600MB 降到 180MB
- JAVA_OPTS 通过环境变量传入,方便在 CCE 中按需调整
- spring.profiles.active 设为 prod,配合外部化配置
3.2 创建 CCE 集群
在华为云控制台创建 CCE 集群时,有以下经验:
- 集群版本:选择 1.25 及以上的稳定版本,避免选最新版本踩兼容性坑
- 网络模型:选择 VPC 网络模型,容器网络与 VPC 子网打通,便于和 RDS 等云服务通信
- 节点规格:业务节点选用 s6.large.2(2核4G),初始 2 个节点,配合弹性伸缩应对流量高峰
- 可用区:节点分布在两个可用区,避免单可用区故障导致服务不可用
3.3 数据库迁移至 RDS
将 MySQL 从自建迁移到华为云 RDS,主要考量:
- 自建 MySQL 的备份、主从同步、安全补丁都需要人工维护,迁移到 RDS 后这些由云平台托管
- RDS 默认开启自动备份,支持按时间点恢复,比自建方案可靠得多
- 通过内网 VPC 与 CCE 集群通信,数据库不暴露公网,安全性大幅提升
迁移过程使用 DRS(数据复制服务)做在线迁移,业务停机时间控制在 5 分钟以内。
3.4 编写 Kubernetes 部署清单
应用部署使用 Deployment,配合 Service 对外暴露:
apiVersion: apps/v1
kind: Deployment
metadata:
name: app-deployment
spec:
replicas: 3
selector:
matchLabels:
app: business-app
template:
metadata:
labels:
app: business-app
spec:
containers:
- name: app
image: swr.cn-north-4.myhuaweicloud.com/namespace/app:v1
ports:
- containerPort: 8080
env:
- name: SPRING_DATASOURCE_URL
valueFrom:
secretKeyRef:
name: db-secret
key: url
resources:
requests:
cpu: 500m
memory: 512Mi
limits:
cpu: 1000m
memory: 1024Mi
livenessProbe:
httpGet:
path: /actuator/health
port: 8080
initialDelaySeconds: 60
periodSeconds: 10
readinessProbe:
httpGet:
path: /actuator/health
port: 8080
initialDelaySeconds: 30
periodSeconds: 10
几个实践要点:
- 资源请求和限制必须设置:不设 limits 的容器可能吃光节点内存,导致其他 Pod 被驱逐
- 存活探针和就绪探针都要配:就绪探针确保只有启动完成的 Pod 接收流量,存活探针在应用卡死时自动重启
- 数据库密码用 Secret 管理:不要明文写在配置清单里
- 镜像存放在 SWR(容器镜像服务):私有镜像仓库,拉取速度快,内网传输不计流量
3.5 配置弹性伸缩
为应对业务流量波动,配置了 HPA(水平 Pod 自动伸缩):
- 最小副本数 3,最大副本数 10
- 基于 CPU 利用率伸缩,目标值 70%
- 在 CCE 控制台可以直接创建,也可以用 YAML 声明式管理
实际运行中,工作日早高峰流量增长时,Pod 从 3 个自动扩到 6 个,整个过程约 30 秒完成,相比以前手动加机器节省了大量时间。
3.6 对外暴露与负载均衡
使用 CCE 自带的 ELB(弹性负载均衡)Ingress 对外暴露服务:
- 创建 Ingress 关联 Service,自动创建 ELB 实例
- 配置健康检查路径 /actuator/health
- 开启会话保持,保证同一用户的请求路由到同一 Pod
四、踩坑记录
坑一:Pod 启动慢被误杀
Spring Boot 应用启动需要约 50 秒,但最初 livenessProbe 的 initialDelaySeconds 只设了 30 秒,导致 Pod 还没启动完就被判定为不健康而重启,形成无限重启循环。
解决:将 livenessProbe 的 initialDelaySeconds 调到 60 秒,readinessProbe 设 30 秒。就绪探针先通过,存活探针后生效,问题解决。
坑二:节点资源不足导致 Pod 调度失败
2 核 4G 的节点上跑了 3 个 Pod,每个请求 500m CPU、512Mi 内存,看起来够用。但节点本身有系统组件开销,实际可用资源只有约 1.5 核 3G,导致第三个 Pod 一直 Pending。
解决:调整节点规格到 s6.xlarge.2(4核8G),或在节点上开启资源超卖(不太推荐生产环境)。最终我们选择了升级节点规格。
坑三:镜像拉取失败
最初将镜像推送到公网镜像仓库,拉取速度慢且偶尔超时失败。
解决:将镜像迁移到华为云 SWR,与 CCE 集群在同一 Region,内网拉取速度从分钟级降到秒级,再未出现拉取失败。
五、改造效果
改造完成后,对比改造前的主要变化:
| 对比项 | 改造前 | 改造后 |
|---|---|---|
| 扩容方式 | 手动装环境,约 1 小时 | HPA 自动伸缩,约 30 秒 |
| 版本回滚 | 手动切换 JAR 包,约 15 分钟 | kubectl rollout undo,约 30 秒 |
| 故障恢复 | 重新部署,数小时 | Pod 自动重启,秒级 |
| 环境一致性 | 各服务器环境可能不一致 | 容器镜像保证完全一致 |
| 数据库运维 | 人工备份、补丁 | RDS 托管,自动备份 |
六、总结与建议
这次容器化改造的核心收益不是技术本身,而是运维方式的转变——从"手动操作每台机器"变成"声明式配置、自动伸缩、自愈恢复"。
给准备做类似改造的同行几点建议:
- 先容器化再上 K8s:先把应用打成 Docker 镜像,在本地用 docker compose 跑通,再迁移到 CCE,降低一次性改动的风险
- 数据库不要急着容器化:有状态服务容器化的复杂度远高于无状态服务,建议先用 RDS 托管,应用层先容器化
- 资源限制一定要设:不设 limits 的容器是集群不稳定的最大隐患
- 探针配置要匹配应用启动时间:initialDelaySeconds 要大于应用实际启动时间,否则会反复重启
- 善用 CCE 控制台的可视化功能:对于 K8s 经验不多的团队,控制台的可视化部署、监控、日志功能能大幅降低学习曲线
以上是本次改造的完整记录,希望对正在考虑容器化迁移的同行有所帮助。云原生不是一蹴而就的,但每一步的改进都是实实在在的。
- 点赞
- 收藏
- 关注作者
评论(0)