从 Docker 到 Kubernetes 的路线

大家好,今天想跟你们聊聊我是怎么从只会 docker run 的小白,一步步走到在生产环境里跑 Kubernetes 集群的。这条路说长不长,说短不短,中间踩过不少坑,也收获了很多成长。下面把我的经验整理成一篇“路线图”,希望能帮到正在准备迁移的朋友。

---

1. Docker 入门:从容器化开始

一开始接触容器,是因为公司要做微服务拆分,大家都在讨论 Docker。最初的学习其实很直接:

1. 安装 Docker Desktop(或直接在 Linux 上装 docker-ce)。

2. 写一个 Dockerfile,把业务代码、依赖全部打包进去。

   FROM golang:1.20 AS builder

WORKDIR /src

COPY . .

RUN go build -o myapp .

FROM alpine:latest

COPY --from=builder /src/myapp /usr/local/bin/

CMD ["myapp"]

3. 构建、运行

   docker build -t myapp .

docker run -d -p 8080:8080 myapp

这几步走完,你的应用已经在容器里跑起来了。刚开始的成就感真的很高——一次构建,随处运行,完全不担心环境差异。

随后我们用 Docker Compose 来管理多容器:

version: '3'

services:

web:

image: myapp

ports:

- "8080:8080"

depends_on:

- db

db:

image: postgres:13

volumes:

- pgdata:/var/lib/postgresql/data

volumes:

pgdata:

docker-compose up 一键启动所有服务,调试本地开发环境变得异常轻松。那时候我甚至觉得 Docker 已经足够解决所有问题了。 ---

2. Docker Compose 的局限与集群需求

随着业务规模逐渐扩大,我们遇到了几个棘手的问题:

- 水平扩展:手动 docker-compose up --scale web=3 只能在本机玩,跨机器部署根本不可行。

- 服务发现:容器 IP 会变,Compose 只能靠 depends_on 做简单的启动顺序,无法动态感知新实例。

- 滚动更新:想平滑升级版本,需要自己写脚本控制旧容器停止、新容器启动,一不小心就会导致服务中断。

- 健康检查与自愈:容器挂了不会自动拉起,需要外部监控脚本配合。

这些问题在单机开发环境里不明显,但到了测试、预生产乃至正式生产环境就成了瓶颈。于是我们开始把目光投向 容器编排——Kubernetes(K8s)几乎是行业标准。

---

3. Kubernetes 基础概念:Pod、Service、Deployment

如果你刚接触 K8s,可能会被一堆新名词吓到。其实只要把下面几个概念弄清楚,后面的迁移就会顺畅很多。

- Pod:K8s 的最小调度单元,通常是一个或多个共享网络的容器。我们的 myapp 容器在 K8s 里会以 Pod 的形式运行。

- Deployment:用来管理 Pod 的副本数、滚动升级、回滚等策略。可以把它想象成 Docker Compose 的 replicas + restart_policy

- Service:为一组 Pod 提供稳定的访问入口(ClusterIP),还支持 LoadBalancer、NodePort 等类型,用来对外暴露服务。

- Ingress:基于 HTTP/HTTPS 的七层路由,适合把多个域名或路径映射到不同的 Service。

下面是一个最常见的 Deployment + Service 示例:

apiVersion: apps/v1

kind: Deployment

metadata:

name: myapp

spec:

replicas: 3

selector:

matchLabels:

app: myapp

template:

metadata:

labels:

app: myapp

spec:

containers:

- name: myapp

image: myapp:latest

ports:

- containerPort: 8080

livenessProbe:

httpGet:

path: /health

port: 8080

resources:

limits:

memory: "256Mi"

cpu: "500m"

---

apiVersion: v1

kind: Service

metadata:

name: myapp

spec:

selector:

app: myapp

ports:

- port: 80

targetPort: 8080

type: LoadBalancer

把这段 YAML 保存为 myapp.yaml,执行 kubectl apply -f myapp.yaml,K8s 会自动创建 3 个 Pod,并通过一个 LoadBalancer 类型的 Service 对外暴露。滚动升级时只需要改镜像 tag,kubectl set image deployment myapp myapp=myapp:v1.1.0,K8s 会平滑替换旧 Pod,保证服务不中断。

---

4. 迁移实战:从 docker‑compose 到 Kubernetes

把已有的 Compose 项目搬到 K8s,一般会经历下面几个步骤。我把每一步都列出来,配上常见的要点,帮助你快速落地。

4.1 容器化检查

- 确保所有依赖都已经写在镜像里(不要把本地目录 volumes 直接映射进容器)。

- 确认入口进程是前台运行(PID 1),不要使用 supervisordsystemd 这类后台进程。

- 加上健康检查探针(livenessProbe / readinessProbe),K8s 会根据探测结果决定是否把流量转发给 Pod。

4.2 编写 Deployment 与 Service

- 先把原来的 docker-compose.yml 里的每个服务对应成一个 Deployment。

- 使用 labels 把 Pod 与 Service 关联起来,如 app: myapp

- 对于需要持久化的数据,使用 PersistentVolumeClaim(PVC)代替 Compose 里的 volumes。如果使用云厂商的存储,只需要声明 storageClassName 即可。

4.3 配置 ConfigMap 与 Secret

- 环境变量、配置文件放到 ConfigMap,敏感信息(密码、Token)放到 Secret。

- 在 Pod 模板里通过 envFromvolumeMounts 引入。

apiVersion: v1

kind: ConfigMap

metadata:

name: myapp-config

data:

DATABASE_HOST: "postgres.default.svc.cluster.local"

LOG_LEVEL: "info"

---

apiVersion: v1

kind: Secret

metadata:

name: myapp-secret

data:

# echo -n "password" | base64

DB_PASSWORD: cGFzc3dvcmQ=

4.4 部署 Ingress(可选)

如果需要根据域名或路径把请求分到不同的 Service,只需写一个 Ingress 资源:

apiVersion: networking.k8s.io/v1

kind: Ingress

metadata:

name: myapp-ingress

annotations:

nginx.ingress.kubernetes.io/rewrite-target: /

spec:

rules:

- host: myapp.example.com

http:

paths:

- path: /

pathType: Prefix

backend:

service:

name: myapp

port:

number: 80

4.5 CI/CD 自动化

- 在 GitLab CI、GitHub Actions 或者 Jenkins 中加入 kubectl 步骤:

  kubectl set image deployment/myapp myapp=${DOCKER_IMAGE} -n production
  

- 推荐使用 Argo CDFlux 实现 GitOps,代码提交后自动同步到集群,省去手动 apply 的繁琐。

---

5. 常见坑与调试技巧

迁移过程中,下面几个问题几乎每个人都遇到过,提前了解可以省不少时间。

- 镜像拉取失败:很多公共镜像在国内访问慢,建议使用阿里云、DaoCloud 的镜像加速,或者直接在私有仓库里维护。

- 资源配额不足:Pod 启动时如果 resources.limits 设得太低,会被 OOMKilled。可以通过 kubectl describe pod 查看事件定位。

- 网络不通:K8s 内部 DNS 解析规则是 service-name.namespace.svc.cluster.local,如果跨 namespace 调用一定要写全名。

- 滚动更新卡住:检查 readinessProbe 是否配置错误,导致新 Pod 永远不 ready,Deployment 只能一直等待。可以临时把 maxSurge 调大一点来排查。

- 持久化数据丢失:记得把 storageClass 与实际的 PV/PVC 绑定好,别把数据写在节点的临时目录里。

调试时常用的几条命令:

kubectl get pods -n <ns>          # 查看 Pod 状态

kubectl logs -f <pod> -n <ns> # 实时日志

kubectl exec -it <pod> -- /bin/sh # 进入容器

kubectl describe pod <pod> -n <ns> # 查看事件

kubectl top nodes / pods # 资源使用情况(需要 metrics-server)

---

总结与展望

从 Docker 到 Kubernetes 的迁移,其实是一条从“把应用装进容器”到“把容器交给集群管理”的自然演进。最初的 Docker 让我们体验到了环境一致性的甜头;随后 Compose 解决了本地多服务的协同;但当业务需要弹性伸缩、滚动升级、故障自愈时,K8s 才是最终的归宿。

迁移的过程并不需要一次性把所有服务全部搬过去。可以先选一个非核心业务做试点,验证 CI/CD、健康检查、滚动更新等流程是否顺畅,再逐步迁移其他服务。期间肯定会遇到网络、存储、权限等各种细节问题,但只要把 Pod → Deployment → Service 这条链路跑通,后面的 Ingress、ConfigMap、Secret、PV 等概念都是顺理成章的扩展。

希望这篇文章能帮你梳理出一条清晰的路线,少走弯路。如果还有其他具体场景想要了解,欢迎在评论区留言,我们一起交流。祝你的 K8s 之旅顺利愉快!