Docker Compose 项目迁移至 K8s 实战
前言
这周核心工作就是把原有 Docker Compose 维护的整套业务服务完整迁移到 Kubernetes 集群。平时开发测试用 Compose 非常顺手,配置少、上手快,但真正往 K8s 迁移的时候,会发现概念变多、配置项成倍增加,很多人直接复制转换后的 YAML 上线直接踩坑。我这边把完整思考、实操、排错全部整理出来,尽量还原真实迁移的完整流程。
一、迁移前思考:什么场景才需要从 Compose 迁移 K8s
Docker Compose 仅仅适用于单机环境,所有服务都运行在同一台服务器。
- Compose 优势:配置简单,几行 yaml 就可以启动一整套服务,本地开发、小测试环境非常友好。
- Compose 短板:没有多节点调度、没有故障自愈、没有滚动更新,机器一旦故障所有服务全部停止;无法实现弹性扩缩容;没有统一健康检查机制。
K8s 能力:跨多台机器调度 Pod,节点故障自动把业务调度到正常节点;支持灰度滚动更新;可以配置 CPU 内存限制;探针检测业务存活;配合 Service 实现服务发现。
重要提醒:不是所有项目都必须上 K8s。如果只是单机小工具,访问量很低,Compose 完全够用,不要为了技术而强行上 K8s,会增加维护成本。适合迁移场景:业务需要多机器部署、需要高可用、后续会扩容实例、生产业务。
二、Docker Compose 和 K8s 资源对应关系
搞懂两者的映射,迁移就有思路,不会对着大量 yaml 无从下手。
| Docker Compose | K8s 对应资源 | 说明 |
|---|---|---|
| services | Deployment | 无状态业务,web、后端服务,可多副本 |
| services | StatefulSet | 有状态服务:MySQL、Redis,需要稳定网络标识、持久存储 |
| ports | Service(NodePort/ClusterIP/LoadBalancer) | 负责流量转发,pod 销毁重建 IP 会变,service 提供稳定访问入口 |
| volumes | PV + PVC | Compose 数据卷,K8s 通过 PVC 申请存储资源,PV 后端对接存储 |
| environment | spec.containers[].env | 容器环境变量,配置账号、地址、时区等 |
| restart: always | Deployment 默认 restartPolicy:Always | 容器异常自动重启 |
| depends_on | 无直接等价 | Compose 仅控制启动顺序,K8s 没有该能力,需要应用层做等待 |
原始 docker‑compose.yaml 示例
version: '3.8'
services:
web:
image: nginx:1.24
ports:
- "8080:80"
environment:
- TZ=Asia/Shanghai
restart: always
volumes:
- ./nginx.conf:/etc/nginx/conf.d/default.conf
redis:
image: redis:6
restart: always
volumes:
- redis-data:/data
volumes:
redis-data:
上面这个 Compose 配置,直接复制到 K8s 是无法运行,格式完全不兼容,需要转换、手动优化。
三、完整实操迁移步骤
步骤 1:安装 kompose 转换工具
kompose 可以读取 compose 文件,自动输出 K8s 的 Deployment、Service、PVC 模板,只做模板生成,绝对不能直接部署上线,必须人工修改。
Linux 安装命令:
#下载二进制程序
curl -L https://github.com/kubernetes/kompose/releases/download/v1.31.2/kompose-linux-amd64 -o kompose
#赋予执行权限
chmod +x kompose
#移动到全局命令目录
mv ./kompose /usr/local/bin/kompose
#验证是否安装成功
kompose version
执行转换:
#基于docker‑compose.yaml生成k8s yaml文件
kompose convert -f docker-compose.yaml
执行完毕,当前目录会生成:web-deployment.yaml、web-service.yaml、redis-deployment.yaml、redis-persistentvolumeclaim.yaml等文件。
运维小提示:kompose 工具的缺陷:不会自动添加资源限制、存活 / 就绪探针;镜像拉取策略默认 Always;存储部分配置经常不符合内网生产规范,全部需要人工修改。
步骤 2:优化无状态服务 Deployment(web 示例)
kompose 生成出来的配置缺少生产必备配置,需要手动补齐resources资源配额、livenessProbe存活探针、readinessProbe就绪探针、imagePullPolicy镜像拉取策略。
web-deployment.yaml 优化后完整内容
apiVersion: apps/v1
kind: Deployment
metadata:
name: web
namespace: default #指定命名空间,业务建议分开不同namespace隔离
spec:
replicas: 2 #业务副本数量,根据业务压力调整
selector:
matchLabels:
app: web
template:
metadata:
labels:
app: web
spec:
containers:
- name: web
image: nginx:1.24
#内网环境节点本地已有镜像,优先使用本地镜像,避免外网拉取失败
imagePullPolicy: IfNotPresent
env:
- name: TZ
value: Asia/Shanghai
#资源限制,防止单个服务占满节点CPU内存,导致集群雪崩
resources:
limits:
cpu: 500m
memory: 512Mi
requests:
cpu: 100m
memory: 128Mi
#就绪探针:判断服务是否准备好接收流量,没就绪不会接入service流量
readinessProbe:
httpGet:
path: /
port: 80
initialDelaySeconds: 5
periodSeconds: 10
timeoutSeconds: 3
#存活探针:业务卡死无响应,自动重启Pod
livenessProbe:
httpGet:
path: /
port: 80
initialDelaySeconds: 15
periodSeconds: 15
volumeMounts:
- name: nginx-conf
mountPath: /etc/nginx/conf.d/default.conf
subPath: default.conf
volumes:
- name: nginx-conf
configMap:
name: nginx-conf
items:
- key: default.conf
path: default.conf
注意:Compose 直接挂载本地配置文件,K8s 不支持直接挂载宿主机文件(不推荐),需要转为 ConfigMap 存放配置。
步骤 3:Service 配置,暴露业务访问入口
apiVersion: v1
kind: Service
metadata:
name: web-svc
namespace: default
spec:
type: NodePort #内网测试使用NodePort;生产建议LoadBalancer或者Ingress
selector:
app: web #标签匹配pod,流量转发给标签app=web的pod
ports:
- port: 80 #service内部端口
targetPort: 80 #pod容器端口
nodePort: 30080 #节点对外端口,范围30000‑32767
步骤 4:有状态服务注意事项(示例 Redis)
像 Redis、MySQL 这类带持久化数据的服务,不要直接使用 Deployment,优先使用StatefulSet。关键点:
- PVC 持久存储一定要规划,数据不能存 Pod 内部,Pod 重建数据直接丢失。
- StatefulSet 的 Pod 拥有稳定网络身份,适合数据库类组件。
步骤 5:部署到 K8s 集群,执行命令
#一次性应用所有yaml资源,创建deployment、service、configmap、pvc
kubectl apply -f web-deployment.yaml -f web-service.yaml -f nginx-configmap.yaml
#查看deployment部署状态
kubectl get deployments
#查看pod运行状态,STATUS全部Running代表启动正常
kubectl get pods -o wide
#查看service端口
kubectl get svc
#查看pod详细事件,排错使用,如果pod异常,看事件报错
kubectl describe pod pod‑name
#查看容器业务日志
kubectl logs ‑f pod‑name
步骤 6:业务验证
访问集群节点 IP:30080,确认 nginx 页面正常打开;查看日志确认没有报错;模拟杀死 Pod,测试集群是否自动新建 Pod,验证自愈能力。
#删除pod,测试自愈,删除之后deployment会自动重新拉起pod
kubectl delete pod web‑7f9685798‑xxxx
四、真实踩坑记录,全部是迁移时实际遇到的问题
坑 1:直接使用 kompose 输出的 yaml 直接部署,Pod 反复重启,节点资源占满
现象:Pod 不断 CrashLoopBackOff,节点 CPU 内存打满,影响集群其他业务。排查:kompose 生成配置不会自带 resources 资源限制,容器可以无限制占用节点 CPU 内存。解决:手动补充requests(申请资源)和limits(最大占用资源)。
坑 2:容器日志时间相差 8 小时,时区不对
现象:业务日志打印的时间是 UTC 时间,和实际北京时间差 8 小时。排查:镜像默认 UTC 时区,没有配置东八区。解决:Pod 环境变量增加TZ=Asia/Shanghai;部分基础镜像不识别环境变量时区,需要挂载宿主机/etc/localtime。
坑 3:ImagePullBackOff 镜像拉取失败
现象:Pod 状态 ImagePullBackOff,无法启动。** 排查场景 1:内网离线环境,镜像不在镜像仓库,节点本地存在镜像。kompose 默认imagePullPolicy: Always,每次都去远程拉取镜像。解决:修改imagePullPolicy: IfNotPresent,本地有镜像优先使用本地镜像。排查场景 2:镜像名称写错、镜像版本不存在、私有仓库没有配置 secret 密钥。
坑 4:Compose 的 depends_on 迁移后,服务启动顺序错乱
现象:web 服务启动比数据库早,业务直接报错连不上数据库。排查: Compose 的 depends_on 仅仅控制容器启动先后,不会等待数据库就绪;K8s 没有等价字段。解决: 在业务容器启动脚本增加等待逻辑,等待数据库端口可连通之后,再启动主程序,不能依赖编排工具控制顺序。
坑 5:配置文件挂载直接写宿主机路径,Pod 启动失败
现象:kompose 把 compose 挂载本地文件直接生成为 hostPath;集群多节点时,其他节点没有这个文件,Pod 启动异常。排查: hostPath 只适合单节点调试,多节点集群严禁大量使用 hostPath。解决: 普通配置转为 ConfigMap;密钥信息使用 Secret。
坑 6:没有配置探针,流量发送给还没启动完成的 Pod
现象:业务刚启动需要初始化十几秒,Pod 刚启动就接收请求,大量请求报错 502。排查: 缺少 readinessProbe 就绪探针,Pod 容器进程起来就被加入 Service 流量池,但业务程序还没初始化完成。解决: 配置就绪探针,业务真正就绪之后才接入流量。
五、迁移的整体流程建议(生产环境流程)
- 先在测试 K8s 集群完成整套迁移,完整跑通业务所有接口,验证读写、存储、重启、故障自愈。
- 有状态组件优先做数据备份,迁移前备份数据库。
- 不要直接删除原有 Compose 业务,两套环境并行运行一段时间,确认新集群无问题,再下线旧环境。
- 命名空间做业务隔离,不同项目放到不同 namespace,方便权限、资源配额管理。
- 存储提前规划,不要把业务数据存于 Pod 内部,Pod 重建数据丢失。
- 资源配额提前评估,根据业务实际负载设置 CPU 内存,不要全部写很大,浪费服务器资源。
六、本周总结
Docker Compose 迁移 K8s,不是简单工具一键转换就完事。kompose 只能帮我们节省手写基础模板的时间,生产环境必须人工二次优化资源限制、探针、镜像策略、配置管理、存储。
优先迁移无状态 web、后端服务,把整套流程摸清楚之后,再处理 MySQL、Redis 这类有状态组件,循序渐进,不容易出现大规模故障。完成业务迁移之后,业务已经跑在 K8s 集群,但是我们看不到集群资源使用率、看不到业务日志,下周就开始搭建 K8s 监控与日志体系。
个人运维小感悟:很多新人学习 K8s 会记大量参数命令,一上来直接折腾数据库这种复杂有状态服务,很容易心态崩掉。建议先拿简单 Nginx、测试后端服务练习完整迁移流程,理解 Pod、Deployment、Service、探针、存储概念,再去处理复杂业务。
更多推荐


所有评论(0)