跳转到内容
123xiao | 无名键客

《Docker Compose 到 Kubernetes 迁移实战:中型项目的容器编排改造与避坑指南》

字数: 0 阅读时长: 1 分钟

Docker Compose 到 Kubernetes 迁移实战:中型项目的容器编排改造与避坑指南

很多团队都是这样起步的:本地开发先用 Docker Compose,把 webapidbredis 一把拉起来,简单直接,效率很高。
但项目一旦进入多人协作、测试环境、灰度发布、资源隔离、自动恢复这些阶段,Compose 往往就开始吃力了。

这篇文章不讲“概念大全”,而是从中型项目迁移的视角,带你把一个典型的 Compose 系统拆解、映射到 Kubernetes,并且把常见坑一并说清楚。你可以把它理解成一份“从能跑到能稳定跑”的迁移手册。


背景与问题

先看一个常见的中型项目场景:

  • 一个 nginx 网关
  • 一个 app 应用服务
  • 一个 worker 异步任务服务
  • 一个 redis
  • 一个 postgres

在 Docker Compose 里,大家通常这么干:

  • 用一个 docker-compose.yml 启动所有服务
  • 通过服务名互相访问,比如 redis:6379
  • depends_on 控制启动顺序
  • 用宿主机目录或命名卷持久化数据库
  • .env 管理配置

这在单机上很好用,但迁移到 Kubernetes 时,问题会集中冒出来:

  1. Compose 的“启动顺序”不等于服务就绪
  2. Kubernetes 不接受“我先启动数据库再启动应用”这种简单逻辑
  3. 本地卷挂载模式不能直接照搬
  4. 环境变量、密钥、配置文件需要拆分治理
  5. 一个 Compose 文件里揉在一起的关注点,在 K8s 里会被拆成多个资源对象
  6. 服务发现、暴露方式、健康检查、滚动更新都要重做

我当时第一次迁移时,最大的误区就是:以为只是把 Compose 翻译成 YAML
后来发现,真正的迁移不是“语法转换”,而是编排模型转换


前置知识与环境准备

建议你至少具备这些基础:

  • 会写基本的 Dockerfile
  • 理解容器端口、卷、环境变量
  • 知道 Kubernetes 的这些对象:
    • Pod
    • Deployment
    • Service
    • ConfigMap
    • Secret
    • PersistentVolumeClaim

本地实验环境可以这样准备:

  • Docker Desktop + Kubernetes
  • 或者 minikube
  • 或者 kind

本文默认你有:

  • kubectl
  • 一个可用的 Kubernetes 集群
  • 可以构建并推送镜像到仓库

验证命令:

kubectl version --client
kubectl get nodes

一个典型 Compose 项目长什么样

先给一个可运行的 Compose 示例,后面我们就围绕它迁移。

version: "3.8"

services:
  nginx:
    image: nginx:1.25
    ports:
      - "8080:80"
    volumes:
      - ./nginx/default.conf:/etc/nginx/conf.d/default.conf:ro
    depends_on:
      - app

  app:
    image: myregistry.example.com/demo-app:1.0.0
    environment:
      APP_ENV: production
      DB_HOST: postgres
      DB_PORT: "5432"
      DB_NAME: demo
      DB_USER: demo
      DB_PASSWORD: demo123
      REDIS_HOST: redis
      REDIS_PORT: "6379"
    ports:
      - "3000:3000"
    depends_on:
      - postgres
      - redis

  worker:
    image: myregistry.example.com/demo-app:1.0.0
    command: ["python", "worker.py"]
    environment:
      APP_ENV: production
      DB_HOST: postgres
      DB_PORT: "5432"
      DB_NAME: demo
      DB_USER: demo
      DB_PASSWORD: demo123
      REDIS_HOST: redis
      REDIS_PORT: "6379"
    depends_on:
      - postgres
      - redis

  redis:
    image: redis:7
    ports:
      - "6379:6379"

  postgres:
    image: postgres:15
    environment:
      POSTGRES_DB: demo
      POSTGRES_USER: demo
      POSTGRES_PASSWORD: demo123
    ports:
      - "5432:5432"
    volumes:
      - pgdata:/var/lib/postgresql/data

volumes:
  pgdata:

这个文件在开发环境没有问题,但放到 Kubernetes 里,不能直接“一键翻译”。


核心原理

迁移前,先把 Compose 和 Kubernetes 的思维差异理顺。这个部分非常关键,后面的 YAML 才不会写成“看起来像 K8s,实际上还是 Compose 思维”。

1. 对象模型映射

Compose 和 Kubernetes 常见映射关系如下:

Docker ComposeKubernetes
serviceDeployment / StatefulSet / Job
portsService / Ingress / containerPort
environmentConfigMap / Secret / env
volumesPVC / PV / emptyDir / hostPath
depends_onreadinessProbe / initContainer / 重试机制
restartDeployment 控制器 / Pod 重启策略
scaleDeployment replicas / HPA

关键点在于:
Compose 的 service 是一种“应用描述单元”,Kubernetes 则把“运行、访问、配置、存储”拆成多个资源对象。


2. 为什么 depends_on 在 Kubernetes 里不好使

Compose 的 depends_on 只是让容器按顺序启动,不保证服务真的能用。
Kubernetes 更强调:

  • 应用要能处理依赖暂时不可用
  • 用健康检查表达“我准备好了”
  • 必要时用 initContainer 做前置等待
  • 更推荐业务层自己做重试

下面这张图能说明差异。

flowchart TD
  A[Compose 启动 service] --> B[按 depends_on 顺序拉起容器]
  B --> C[容器已启动]
  C --> D[但数据库未必可连接]

  E[Kubernetes 创建 Pod] --> F[容器启动]
  F --> G[readinessProbe 检查]
  G --> H[通过后才接收流量]
  F --> I[依赖未就绪时应用需重试]

3. Kubernetes 更关注“声明式期望状态”

在 Compose 里,我们经常是在描述“怎么启动”。
在 Kubernetes 里,我们更像是在声明:

  • 我要几个副本
  • 我要暴露什么端口
  • 我要什么配置
  • 健康状态如何判断
  • 资源上限和下限是什么

控制器会不断把实际状态拉回期望状态。

sequenceDiagram
  participant U as 用户
  participant A as API Server
  participant C as Controller
  participant N as Node/Kubelet
  participant P as Pod

  U->>A: kubectl apply -f deployment.yaml
  A-->>C: 存储期望状态
  C->>N: 调度并创建 Pod
  N->>P: 拉镜像并启动容器
  P-->>A: 上报运行状态
  C-->>A: 持续对比实际与期望

4. 中型项目迁移的推荐拆分方法

不要一口气把所有服务全迁走。比较稳妥的顺序是:

  1. 无状态服务先迁移appworkernginx
  2. 依赖改为集群内 Service 名称
  3. 配置和密钥分离
  4. 健康检查补齐
  5. 再处理有状态服务:如 postgres
  6. 最后加 Ingress、自动扩缩容、监控告警

如果数据库已经有稳定的托管方案,通常建议优先使用云数据库或外部托管数据库,而不是在第一阶段就把数据库也塞进集群。


迁移设计:先定边界,再写 YAML

为了让实践更清晰,我这里采用一个比较现实的迁移边界:

  • appworkernginx 迁移到 Kubernetes
  • redispostgres 先在 Kubernetes 内运行,便于演示
  • 生产上可以替换为托管 Redis/Postgres

资源规划如下:

  • Namespace: demo
  • ConfigMap: 非敏感配置
  • Secret: 数据库密码
  • Deployment: appworkernginx
  • Service: appredispostgresnginx
  • PVC: postgres 数据卷

实战代码(可运行)

下面给出一套可以直接 kubectl apply 的示例。
建议你按文件拆分保存。


1. Namespace

apiVersion: v1
kind: Namespace
metadata:
  name: demo

保存为 00-namespace.yaml


2. ConfigMap 与 Secret

apiVersion: v1
kind: ConfigMap
metadata:
  name: demo-config
  namespace: demo
data:
  APP_ENV: "production"
  DB_HOST: "postgres"
  DB_PORT: "5432"
  DB_NAME: "demo"
  DB_USER: "demo"
  REDIS_HOST: "redis"
  REDIS_PORT: "6379"
---
apiVersion: v1
kind: Secret
metadata:
  name: demo-secret
  namespace: demo
type: Opaque
stringData:
  DB_PASSWORD: "demo123"
  POSTGRES_PASSWORD: "demo123"

保存为 01-config.yaml

这里为了演示用了 stringData,生产里建议接入外部密钥系统,后面会讲。


3. Redis Service + Deployment

apiVersion: v1
kind: Service
metadata:
  name: redis
  namespace: demo
spec:
  selector:
    app: redis
  ports:
    - port: 6379
      targetPort: 6379
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: redis
  namespace: demo
spec:
  replicas: 1
  selector:
    matchLabels:
      app: redis
  template:
    metadata:
      labels:
        app: redis
    spec:
      containers:
        - name: redis
          image: redis:7
          ports:
            - containerPort: 6379
          resources:
            requests:
              cpu: "100m"
              memory: "128Mi"
            limits:
              cpu: "300m"
              memory: "256Mi"
          livenessProbe:
            tcpSocket:
              port: 6379
            initialDelaySeconds: 10
            periodSeconds: 10
          readinessProbe:
            tcpSocket:
              port: 6379
            initialDelaySeconds: 5
            periodSeconds: 5

保存为 02-redis.yaml


4. Postgres Service + PVC + Deployment

apiVersion: v1
kind: Service
metadata:
  name: postgres
  namespace: demo
spec:
  selector:
    app: postgres
  ports:
    - port: 5432
      targetPort: 5432
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: postgres-pvc
  namespace: demo
spec:
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 5Gi
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: postgres
  namespace: demo
spec:
  replicas: 1
  selector:
    matchLabels:
      app: postgres
  template:
    metadata:
      labels:
        app: postgres
    spec:
      containers:
        - name: postgres
          image: postgres:15
          ports:
            - containerPort: 5432
          env:
            - name: POSTGRES_DB
              valueFrom:
                configMapKeyRef:
                  name: demo-config
                  key: DB_NAME
            - name: POSTGRES_USER
              valueFrom:
                configMapKeyRef:
                  name: demo-config
                  key: DB_USER
            - name: POSTGRES_PASSWORD
              valueFrom:
                secretKeyRef:
                  name: demo-secret
                  key: POSTGRES_PASSWORD
          volumeMounts:
            - name: postgres-data
              mountPath: /var/lib/postgresql/data
          resources:
            requests:
              cpu: "200m"
              memory: "256Mi"
            limits:
              cpu: "500m"
              memory: "512Mi"
          livenessProbe:
            tcpSocket:
              port: 5432
            initialDelaySeconds: 20
            periodSeconds: 10
          readinessProbe:
            exec:
              command:
                - sh
                - -c
                - pg_isready -U "$POSTGRES_USER" -d "$POSTGRES_DB"
            initialDelaySeconds: 10
            periodSeconds: 5
      volumes:
        - name: postgres-data
          persistentVolumeClaim:
            claimName: postgres-pvc

保存为 03-postgres.yaml

严格来说,数据库更适合 StatefulSet,本文先用最容易理解的最小可运行方案。
如果是正式生产,推荐使用 StatefulSet + Headless Service + 备份策略,或者直接上托管数据库。


5. App Service + Deployment

这里模拟一个 Python Web 服务。

apiVersion: v1
kind: Service
metadata:
  name: app
  namespace: demo
spec:
  selector:
    app: app
  ports:
    - port: 3000
      targetPort: 3000
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: app
  namespace: demo
spec:
  replicas: 2
  selector:
    matchLabels:
      app: app
  template:
    metadata:
      labels:
        app: app
    spec:
      initContainers:
        - name: wait-for-postgres
          image: busybox:1.36
          command:
            - sh
            - -c
            - >
              until nc -z postgres 5432;
              do echo waiting for postgres;
              sleep 2;
              done
        - name: wait-for-redis
          image: busybox:1.36
          command:
            - sh
            - -c
            - >
              until nc -z redis 6379;
              do echo waiting for redis;
              sleep 2;
              done
      containers:
        - name: app
          image: myregistry.example.com/demo-app:1.0.0
          ports:
            - containerPort: 3000
          envFrom:
            - configMapRef:
                name: demo-config
          env:
            - name: DB_PASSWORD
              valueFrom:
                secretKeyRef:
                  name: demo-secret
                  key: DB_PASSWORD
          resources:
            requests:
              cpu: "200m"
              memory: "256Mi"
            limits:
              cpu: "500m"
              memory: "512Mi"
          livenessProbe:
            httpGet:
              path: /healthz
              port: 3000
            initialDelaySeconds: 20
            periodSeconds: 10
          readinessProbe:
            httpGet:
              path: /readyz
              port: 3000
            initialDelaySeconds: 10
            periodSeconds: 5

保存为 04-app.yaml

这里有两个迁移重点:

  • depends_on 换成了 initContainers + readinessProbe
  • 应用自身最好仍然有数据库/Redis 重试逻辑

6. Worker Deployment

Worker 通常不需要 Service,因为它不对外接流量。

apiVersion: apps/v1
kind: Deployment
metadata:
  name: worker
  namespace: demo
spec:
  replicas: 1
  selector:
    matchLabels:
      app: worker
  template:
    metadata:
      labels:
        app: worker
    spec:
      initContainers:
        - name: wait-for-postgres
          image: busybox:1.36
          command:
            - sh
            - -c
            - >
              until nc -z postgres 5432;
              do echo waiting for postgres;
              sleep 2;
              done
        - name: wait-for-redis
          image: busybox:1.36
          command:
            - sh
            - -c
            - >
              until nc -z redis 6379;
              do echo waiting for redis;
              sleep 2;
              done
      containers:
        - name: worker
          image: myregistry.example.com/demo-app:1.0.0
          command: ["python", "worker.py"]
          envFrom:
            - configMapRef:
                name: demo-config
          env:
            - name: DB_PASSWORD
              valueFrom:
                secretKeyRef:
                  name: demo-secret
                  key: DB_PASSWORD
          resources:
            requests:
              cpu: "100m"
              memory: "128Mi"
            limits:
              cpu: "300m"
              memory: "256Mi"

保存为 05-worker.yaml


7. Nginx ConfigMap + Service + Deployment

先准备 Nginx 配置,通过集群内 Service 访问 app:3000

apiVersion: v1
kind: ConfigMap
metadata:
  name: nginx-config
  namespace: demo
data:
  default.conf: |
    server {
      listen 80;
      location / {
        proxy_pass http://app:3000;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
      }
    }
---
apiVersion: v1
kind: Service
metadata:
  name: nginx
  namespace: demo
spec:
  type: NodePort
  selector:
    app: nginx
  ports:
    - port: 80
      targetPort: 80
      nodePort: 30080
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx
  namespace: demo
spec:
  replicas: 1
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
        - name: nginx
          image: nginx:1.25
          ports:
            - containerPort: 80
          volumeMounts:
            - name: nginx-config-volume
              mountPath: /etc/nginx/conf.d/default.conf
              subPath: default.conf
          readinessProbe:
            httpGet:
              path: /
              port: 80
            initialDelaySeconds: 5
            periodSeconds: 5
      volumes:
        - name: nginx-config-volume
          configMap:
            name: nginx-config

保存为 06-nginx.yaml


8. 一次性部署

kubectl apply -f 00-namespace.yaml
kubectl apply -f 01-config.yaml
kubectl apply -f 02-redis.yaml
kubectl apply -f 03-postgres.yaml
kubectl apply -f 04-app.yaml
kubectl apply -f 05-worker.yaml
kubectl apply -f 06-nginx.yaml

检查资源状态:

kubectl get all -n demo
kubectl get pvc -n demo

查看 Pod 启动细节:

kubectl describe pod -n demo <pod-name>
kubectl logs -n demo <pod-name>

逐步验证清单

迁移时我很推荐按下面的顺序验,不要“全部 apply 完了再一起看”。

第一步:存储是否就绪

kubectl get pvc -n demo

确认 postgres-pvc 已经 Bound


第二步:基础依赖是否可达

kubectl get svc -n demo
kubectl get pods -n demo -o wide

确认:

  • redis Service 在 6379
  • postgres Service 在 5432
  • Pod 状态为 Running

第三步:应用是否通过 readiness

kubectl get pods -n demo

READY 列是否正常,比如 1/1

如果一直是 0/1,通常是:

  • readinessProbe 配错
  • 应用接口 /readyz 没实现
  • 应用依赖外部服务超时

第四步:应用服务发现是否正确

进入 nginx Pod 内测试:

kubectl exec -it -n demo deploy/nginx -- sh

然后执行:

wget -qO- http://app:3000/healthz

如果能返回结果,说明集群内服务发现正常。


第五步:从集群外访问

kubectl get svc nginx -n demo

拿到 NodePort 后访问:

curl http://<node-ip>:30080/

本地 minikube 的话可以这样:

minikube service nginx -n demo

从 Compose 到 Kubernetes 的迁移对照表

这部分适合边迁边查。

1. 端口暴露

Compose:

ports:
  - "8080:80"

Kubernetes 中通常拆为:

  • 容器端口:containerPort
  • 集群内暴露:Service
  • 集群外暴露:NodePort / LoadBalancer / Ingress

2. 配置文件挂载

Compose:

volumes:
  - ./nginx/default.conf:/etc/nginx/conf.d/default.conf:ro

Kubernetes:

  • ConfigMap
  • 再通过 volumeMounts + subPath 挂载

3. 环境变量

Compose:

environment:
  DB_HOST: postgres
  DB_PASSWORD: demo123

Kubernetes:

  • 普通配置放 ConfigMap
  • 密码放 Secret

这一步不要偷懒。我见过不少迁移项目把所有密码直接塞到 Deployment 里,短期省事,长期一定出事。


4. 卷

Compose:

volumes:
  pgdata:

Kubernetes:

  • PersistentVolumeClaim
  • 底层由集群存储类提供 PersistentVolume

注意:K8s 里的存储不是“宿主机目录换个写法”那么简单
尤其跨节点调度时,本地目录方案很容易出问题。


常见坑与排查

这是迁移里最值钱的部分。下面这些问题,我几乎都见过。


坑 1:Pod 已启动,但服务不可用

现象

  • kubectl get pods 显示 Running
  • 但是访问应用报 502、连接超时、数据库连接失败

原因

  • 容器启动 != 应用准备好
  • 没配 readinessProbe
  • 依赖服务虽然启动了,但还没就绪

排查

kubectl describe pod -n demo <pod-name>
kubectl logs -n demo <pod-name>

重点看:

  • readiness probe 失败信息
  • 应用日志里的连接错误
  • initContainer 是否卡住

建议

  • 所有对外服务都配 readinessProbe
  • 数据库、缓存等依赖尽量让应用有重试逻辑
  • initContainer 适合做简单等待,但别把它当万能药

坑 2:Service 名字对了,还是连不上

现象

应用里配置了 DB_HOST=postgres,但就是解析失败或连接不到。

常见原因

  1. Service selector 不匹配 Pod label
  2. Pod 不在同一 namespace
  3. 容器监听地址错误,只监听 127.0.0.1
  4. 应用实际使用了旧环境变量

排查

查看 Service 和 Endpoints:

kubectl get svc -n demo
kubectl get endpoints -n demo

如果 Endpoints 为空,基本就是 selector 没匹配上。


坑 3:配置更新了,Pod 没生效

现象

修改了 ConfigMap,但应用行为没变。

原因

  • 环境变量注入到 Pod 后,不会自动更新
  • 有些文件挂载会更新,但应用未热加载

建议

修改后主动滚动重启:

kubectl rollout restart deployment app -n demo
kubectl rollout restart deployment nginx -n demo

检查发布状态:

kubectl rollout status deployment app -n demo

坑 4:数据库数据丢了

现象

Pod 重建后,Postgres 里的数据没了。

原因

  • 没有使用 PVC
  • 误用了 emptyDir
  • 重建资源时删掉了 PVC 或底层卷

建议

  • 数据库务必确认持久卷绑定成功
  • 先做备份,再做迁移
  • 正式环境尽量使用托管数据库或成熟的数据库 Operator

坑 5:镜像拉取失败

现象

Pod 一直 ImagePullBackOff

排查

kubectl describe pod -n demo <pod-name>

常见原因:

  • 镜像地址写错
  • 镜像标签不存在
  • 私有仓库缺少拉取凭证

私有仓库凭证示例:

kubectl create secret docker-registry regcred \
  --docker-server=myregistry.example.com \
  --docker-username=myuser \
  --docker-password=mypass \
  --docker-email=my@example.com \
  -n demo

Deployment 中引用:

spec:
  template:
    spec:
      imagePullSecrets:
        - name: regcred

坑 6:资源限制没配,集群被“吃爆”

现象

  • 某个应用突然占满 CPU/内存
  • 节点不稳定
  • Pod 被驱逐

原因

  • 没有 requests/limits
  • 没做容量预估
  • Worker 类服务无限抢资源

建议

哪怕是初版,也至少为每个服务配基础资源边界。


安全最佳实践

迁移到 Kubernetes 后,安全边界会比 Compose 更复杂,但也更可控。

1. 不要把密钥直接写进 Deployment

错误方式:

env:
  - name: DB_PASSWORD
    value: "demo123"

更好的方式:

  • Secret
  • 外部密钥管理系统,如 Vault、云厂商 KMS/Secrets Manager

2. 给容器最小权限

如果镜像支持,尽量使用非 root 用户运行:

securityContext:
  runAsNonRoot: true
  runAsUser: 10001
  allowPrivilegeEscalation: false
  readOnlyRootFilesystem: true

当然,readOnlyRootFilesystem 不是所有镜像都兼容,像某些需要写临时文件的程序要先验证。


3. 限制命名空间内的网络访问

中型项目迁移后,服务变多,建议逐步引入 NetworkPolicy,避免“谁都能访问数据库”。

一个最简思路是:

  • 只允许 appworker 访问 postgres
  • 只允许 appworker 访问 redis

4. 镜像固定版本,不用 latest

坏习惯:

image: myapp:latest

更稳妥的方式:

image: myapp:1.0.0

更进一步可以使用 digest。


性能最佳实践

Compose 迁移到 Kubernetes 后,性能问题常常不是代码变慢,而是资源与探针配置不合理

1. requests 和 limits 要分开考虑

  • requests 决定调度基础
  • limits 决定资源上限

经验上:

  • Web 应用先保守估计
  • Worker 根据任务峰值做单独配置
  • 数据库不要套用 Web 服务模板

2. readinessProbe 不要太激进

我踩过一个坑:应用启动要 15 秒,readinessProbe 5 秒开始查,连续失败后上游不断摘流量、重试、告警,看起来像“服务抖动”。

建议:

  • 根据真实启动时间设置 initialDelaySeconds
  • 探针接口尽量轻量,不要在 /readyz 里做重 SQL

3. 无状态服务优先水平扩展

app 这种服务,适合先做多副本:

replicas: 2

后续可以再配 HPA。
但数据库、带本地状态的服务不要随便“加副本”。


4. 用 Ingress 代替自建网关暴露

本文为了最小示例保留了 nginx + NodePort
如果你已经进入正式环境,通常建议:

  • 使用 Ingress Controller
  • 让流量入口、TLS、路由规则统一治理
  • nginx 是否还保留,要看你是否有自定义反向代理逻辑

一个更稳妥的迁移路线

如果是中型项目,我推荐下面这个顺序,风险更低:

flowchart LR
  A[梳理 Compose 服务] --> B[拆分配置/密钥/存储]
  B --> C[先迁移无状态服务]
  C --> D[补齐探针与资源限制]
  D --> E[迁移缓存和异步任务]
  E --> F[最后迁移数据库或接入托管服务]
  F --> G[接入 Ingress 监控 告警 自动扩缩容]

这条路线的核心是:
优先把“应用编排能力”迁过去,再处理最敏感的状态数据。


边界条件:什么时候不该急着迁 Kubernetes

这点也很重要。不是所有 Compose 项目都值得立刻迁 K8s。

如果你的项目符合下面情况,可以先别急:

  • 只有 2~3 个服务
  • 单机部署已稳定
  • 没有弹性扩缩容需求
  • 团队缺少 Kubernetes 运维经验
  • 监控、日志、CI/CD 还没建立

因为 Kubernetes 会带来额外复杂度:

  • YAML 增多
  • 调试链路变长
  • 网络、存储、权限模型更复杂

我的建议是:
当你已经明显被 Compose 的单机边界、恢复能力、发布治理卡住时,再迁最值。


总结

把 Docker Compose 迁移到 Kubernetes,真正难的不是 YAML 语法,而是这几个观念切换:

  • 从“启动顺序”转向“就绪与自恢复”
  • 从“单文件描述一切”转向“资源对象拆分治理”
  • 从“本地卷和环境变量”转向“PVC、ConfigMap、Secret”
  • 从“能跑”转向“可观测、可扩缩、可回滚”

如果你准备开始动手,我建议按下面的最小步骤执行:

  1. 先列出 Compose 中所有服务及依赖
  2. 标记哪些是无状态、哪些是有状态
  3. 先迁 app/worker/nginx 这类无状态服务
  4. 补齐 readinessProbelivenessProbe、资源限制
  5. 配置拆到 ConfigMap/Secret
  6. 数据库优先考虑托管方案,实在要上集群再谨慎做持久化
  7. 每迁一个服务就做连通性和回滚验证

最后给一句很实在的建议:
不要追求一次性“完美迁移”,而要追求“每一步都能验证、能回退”。
中型项目最怕的不是迁得慢,而是看起来迁完了,结果一上线问题成串。

如果你愿意,这篇文章里的 YAML 就可以作为你第一个迁移基线,从一个最小可运行版本开始,逐步演进。


分享到:

上一篇
《Docker Compose 实战:为中型项目构建可复用的多环境本地开发与部署方案》
下一篇
《微服务架构中分布式事务的实战落地:基于 Saga 模式的设计、补偿与故障排查》