- 约1860字
- 技术
- 2026年4月24日
3天迁移踩坑,我最后后悔没早点做这件事。
上周把项目的部署方案从Docker Compose切到了Kubernetes。本以为花半天就能搞定,结果整整搞了3天。现在回想起来,有些坑完全可以避免。今天把经验教训分享出来,希望对你有帮助。
坑1:环境变量传递方式完全不同
在Docker Compose里,环境变量直接写在docker-compose.yml里,或者通过.env文件加载,简单直观。
# docker-compose.yml
services:
api:
image: myapp/api
environment:
- DATABASE_URL=postgres://db:5432/myapp
- REDIS_URL=redis://cache:6379
到了Kubernetes,你得先创建ConfigMap或者Secret,然后用env或者envFrom注入:
# api-deployment.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: myapp-config
data:
DATABASE_URL: "postgres://db:5432/myapp"
REDIS_URL: "redis://cache:6379"
---
apiVersion: apps/v1
kind: Deployment
spec:
template:
spec:
containers:
- name: api
envFrom:
- configMapRef:
name: myapp-config
当时我没搞懂这区别,第一个Deployment部署上去,Pod一直报" DATABASE_URL undefined"。排查了半天才发现环境变量根本没注入进来。
教训:K8s的配置管理是独立的,需要先创建ConfigMap/Secret,再在Pod里引用。两者不是一套东西。
坑2:健康检查配置差太多
Docker Compose的健康检查很简单:
services:
api:
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:3000/health"]
interval: 30s
timeout: 10s
retries: 3
Kubernetes的健康检查分两种: readinessProbe(就绪探针)和 livenessProbe(存活探针)。而且配置字段完全不一样:
containers:
- name: api
readinessProbe:
httpGet:
path: /health
port: 3000
initialDelaySeconds: 5
periodSeconds: 10
livenessProbe:
httpGet:
path: /health
port: 3000
initialDelaySeconds: 15
periodSeconds: 20
我一开始只配了livenessProbe,没配readinessProbe,结果服务刚启动就被流量砸死——Pod还没就绪就开始处理请求了。
教训:readinessProbe控制是否接收流量,livenessProbe控制是否重启。两者用途不同,都很重要。
坑3:网络访问从直接变间接
Docker Compose里,同一个compose文件里的服务可以直接用服务名访问:
# docker-compose.yml
services:
api:
depends_on:
- db
# 直接用 db 访问数据库服务
environment:
- DATABASE_URL=postgres://db:5432/myapp
Kubernetes里,每个Pod有独立的IP,Service才是入口。而且跨namespace的话,还要加上namespace前缀:
# 同namespace
DATABASE_URL: postgres://myapp-db:5432/myapp
# 跨namespace
DATABASE_URL: postgres://myapp-db.default.svc.cluster.local:5432/myapp
当时数据库服务叫db,我直接配postgres://db:5432,结果Pod一直连不上。查了好半天日志才发现K8s里的服务名格式完全不同。
教训:K8s的服务发现用的是DNS,格式是{service-name}.{namespace}.svc.cluster.local。简单场景可以用{service-name}.{namespace},同namespace才能直接用服务名。
坑4:存储卷挂载不是一回事
Docker Compose的卷挂载很简单:
volumes:
- ./data:/app/data
- db-data:/var/lib/postgresql/data
Kubernetes要用PersistentVolumeClaim:
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: db-data
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 10Gi
---
apiVersion: apps/v1
kind: Deployment
spec:
template:
spec:
containers:
- name: db
volumeMounts:
- name: db-data
mountPath: /var/lib/postgresql/data
volumes:
- name: db-data
persistentVolumeClaim:
claimName: db-data
临时存储可以用emptyDir,但持久化数据必须用PVC。我一开始不知道有这个区别,数据全存emptyDir里,Pod一重启数据全丢。
教训:K8s的存储模型比Docker Compose复杂很多。开发环境可以用emptyDir,生产环境一定要用PVC。
坑5:服务暴露方式变了
Docker Compose用ports:
services:
api:
ports:
- "3000:3000"
Kubernetes要用Service:
apiVersion: v1
kind: Service
metadata:
name: myapp-api
spec:
selector:
app: myapp-api
ports:
- port: 80
targetPort: 3000
type: LoadBalancer
而且如果用的是Ingress,还要额外配置Ingress规则。我当时配完Service就以为能访问了,结果外部根本访问不了,最后发现是没配Ingress或者LoadBalancer。
教训:K8s里Pod是调度的最小单位,Service是入口,Ingress是七层路由。三层结构要想清楚。
迁移建议
如果你的项目也要从Docker Compose迁移到Kubernetes,有几点建议:
先在测试环境跑通:不要直接在生产环境尝试,K8s的学习曲线比较陡,测试环境多踩坑没关系。
用Helm或Kustomize:手写yaml太痛苦了,Helm有模板,Kustomize可以分环境管理配置。
逐步迁移:不要一次性把所有服务都搬过去,先迁一个无状态服务试试水。
做好监控和日志:K8s的调试比Docker Compose复杂很多,没有监控和日志寸步难行。
准备好回滚方案:迁移出问题能快速回滚到Docker Compose,别把自己逼到绝路。
迁移虽然踩了坑,但K8s的弹性伸缩和自愈能力确实香。忍过这3天,后面部署确实省心很多。值不值?个人角度来说,如果是小项目,Docker Compose够用就不用折腾;如果团队大了要上规模,K8s是必经之路,早踩坑早成长。