从 Docker Compose 迁到华为云 CCE:一次真实迁移记录
项目原本跑在一台 4C8G 的 ECS 上,用 docker compose 编排五个服务。用户涨上来之后单机扛不住,迁到了 CCE(云容器引擎)。这篇记录迁移过程里真正花时间的部分。
为什么迁
单机 compose 的问题:
- 无法水平扩容,CPU 打满只能升配
- 单点故障,机器挂了全站不可用
- 滚动更新要手动停服务,有 downtime
- 配置散落在 docker-compose.yml 里,多环境要复制多份
CCE 是托管的 Kubernetes,能解决上面这些,代价是复杂度和成本上升。
迁移前的准备
第一步:把所有配置外置
compose 里的 environment 全部抽到 ConfigMap,敏感信息(数据库密码、API key)抽到 Secret。
原先是:
environment:
- DB_HOST=127.0.0.1
- DB_PASSWORD=xxx
迁移后 ConfigMap 存非敏感项,Secret 存密码。
第二步:确认镜像能推送到 SWR
CCE 拉镜像从华为云容器镜像服务(SWR)最快。要先建组织、建命名空间,然后:
docker tag myapp:v1 swr.cn-north-4.myhuaweicloud.com/myorg/myapp:v1
docker login -u cn-north-4@XXXX -p **** swr.cn-north-4.myhuaweicloud.com
docker push swr.cn-north-4.myhuaweicloud.com/myorg/myapp:v1
登录用户名不是账号名,是「区域@AK」这种格式,在 SWR 控制台的「客户端上传」里能看到准确的命令。
第三步:梳理服务依赖
compose 里靠 depends_on 保证启动顺序。K8s 没有这个机制,需要用 readinessProbe + initContainer 或者应用自己重试。
我们做法是:所有服务启动时对数据库连接做重试(最多 30 次,间隔 2 秒),不依赖启动顺序。
关键配置怎么写
Deployment 最小可用版本:
apiVersion: apps/v1
kind: Deployment
metadata:
name: api-server
spec:
replicas: 2
selector:
matchLabels:
app: api-server
template:
metadata:
labels:
app: api-server
spec:
containers:
- name: api
image: swr.cn-north-4.myhuaweicloud.com/myorg/api:v1
ports:
- containerPort: 8080
envFrom:
- configMapRef:
name: app-config
- secretRef:
name: app-secret
resources:
requests:
cpu: 200m
memory: 256Mi
limits:
cpu: 1000m
memory: 512Mi
livenessProbe:
httpGet:
path: /healthz
port: 8080
initialDelaySeconds: 30
periodSeconds: 10
readinessProbe:
httpGet:
path: /ready
port: 8080
initialDelaySeconds: 10
periodSeconds: 5
resources 必须写。不写的话调度器无法合理分配,节点容易 OOM。
两个 probe 的区别:
livenessProbe失败 → 重启容器readinessProbe失败 → 从 Service 摘掉,不接流量
健康检查接口要分开:/healthz 只检查进程活着,/ready 检查依赖(数据库、缓存)是否可用。如果把依赖检查放进 liveness,数据库抖一下会导致所有 Pod 重启,雪崩。
Service 暴露:
apiVersion: v1
kind: Service
metadata:
name: api-server
spec:
selector:
app: api-server
ports:
- port: 80
targetPort: 8080
type: ClusterIP
集群内访问用 ClusterIP,对外暴露用 Ingress 或 LoadBalancer。生产建议 Ingress + ELB,一个 ELB 带多个域名。
Ingress:
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: main-ingress
annotations:
kubernetes.io/ingress.class: nginx
spec:
rules:
- host: api.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: api-server
port:
number: 80
踩过的坑
坑一:镜像拉取失败
报 ImagePullBackOff。原因通常是:
- SWR 的组织/命名空间不对
- 没配 imagePullSecrets
- 镜像 tag 写错
CCE 节点如果和 SWR 同区域,内网拉取免密。跨区域要配 secret。
坑二:Pod 一直 Pending
看 kubectl describe pod 的 Events。常见原因:
- 资源不足(节点剩余 CPU/内存不够 requests)
- 节点亲和性规则不满足
- PVC 挂载失败
坑三:时区不对
容器默认 UTC,日志时间差 8 小时。解决:
env:
- name: TZ
value: Asia/Shanghai
或者挂载宿主机的 /etc/localtime。
坑四:日志看不到
kubectl logs 只能看 stdout/stderr。应用如果写文件,要挂载 emptyDir 或者接日志服务(LTS)。
坑五:滚动更新时 502
默认滚动更新是「先起新的再停旧的」,但如果 readinessProbe 配得太宽松,新 Pod 还没真正就绪就接流量,会报错。
解决:readinessProbe 的 initialDelaySeconds 给够,failureThreshold 设小一点(比如 2),让不健康的 Pod 快速摘除。
成本控制
CCE 的费用分三块:集群管理费(按集群收)、节点费用(ECS)、ELB/带宽。
省钱要点:
- 节点池用弹性伸缩。低峰缩到最小节点数,高峰自动扩。
- 按需 + 抢占式混合。无状态服务可以用抢占式实例,成本低很多,但会被回收,要能容忍。
- 集群管理费按规模阶梯。小集群(50 节点以下)费用固定,大集群反而单位成本低。
我们目前是 3 节点固定 + 弹性伸缩到 6,月成本比原来单台高配 ECS 贵约 40%,但可用性和扩容能力提升明显。
值不值得迁
适合迁:
- 服务多、需要独立扩缩容
- 要求零停机发布
- 团队有 K8s 经验或者愿意投入学习
不适合迁:
- 单服务、流量稳定
- 团队没人懂 K8s
- 预算敏感
五个服务以下的单体应用,用两台 ECS + 负载均衡 + 手动滚动更新,成本低得多,故障率也未必更高。容器编排的收益在规模上来之后才明显。
小结
迁移本身不难,难的是「把 compose 的隐式依赖显式化」——启动顺序、配置注入、健康检查、日志收集,这些在单机上靠约定,在 K8s 里必须写进配置。
最值得花时间的是两个 probe 的设计。配好了滚动更新无感,配坏了频繁重启或者流量打到没准备好的实例上。
- 点赞
- 收藏
- 关注作者
评论(0)