技术文章2026 年 6 月 4 日约 3.3 分钟

Kubernetes Deployment 配置:副本、探针与资源限制

从探针分工、资源配额到滚动更新与优雅退出,把 Deployment 配成生产可用的状态。

Deployment 大概是 Kubernetes 里被用得最多的对象,但也是配置翻车重灾区。我见过最典型的事故:镜像版本写错,readinessProbe 路径配错,流量全部打到不健康的 Pod 上,线上接口 50% 超时。这篇文章把 Deployment 里每个生产必配的字段讲透。

#先分清三个探针

很多人 readiness 和 liveness 混着写,这是大忌:

  • readinessProbe:告诉我「能不能接流量」。失败时 Pod 不健康,但不会被杀掉,只是从 Service 的 Endpoints 里摘掉。就绪。
  • livenessProbe:告诉我「是不是死透了」。连续失败会被 kubelet 杀掉并重启。存活。
  • startupProbe:给启动慢的应用用的。它成功之前,liveness 不生效,防止启动 30 秒的应用因为 liveness 超时被反复杀。
yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: demo-api
spec:
  replicas: 3
  strategy:
    type: RollingUpdate
    rollingUpdate:
      maxUnavailable: 0
      maxSurge: 1
  selector:
    matchLabels:
      app: demo-api
  template:
    metadata:
      labels:
        app: demo-api
    spec:
      terminationGracePeriodSeconds: 30
      containers:
        - name: api
          image: ghcr.io/acme/demo-api:1.2.0
          ports:
            - containerPort: 8080
          resources:
            requests: {cpu: 100m, memory: 128Mi}
            limits: {cpu: 500m, memory: 512Mi}
          readinessProbe:
            httpGet: {path: /healthz, port: 8080}
            initialDelaySeconds: 5
            periodSeconds: 10
            timeoutSeconds: 2
            failureThreshold: 3
          livenessProbe:
            httpGet: {path: /healthz, port: 8080}
            periodSeconds: 10
            timeoutSeconds: 2
            failureThreshold: 3

两个高频翻车点:

  • 探针路径用业务接口当健康检查。健康检查应该只依赖应用自身状态,去查数据库的 /healthz 在 DB 抖动时会连带把 Pod 全摘掉,形成雪崩。我见过有人把 /orders 当探针,压测时探针超时、Pod 重启、流量再打进新 Pod、再次超时——无限循环。
  • liveness 和 readiness 用同一个 /healthz。慢启动的应用(比如 Java 冷启动 40 秒),liveness 会在就绪前就把 Pod 杀掉。要么用 startupProbe 兜底,要么 liveness 的 failureThreshold 放宽。

#resources:requests 和 limits 各管一件事

  • requests:调度依据。kubelet 保证这个量,Pod 只会被调度到「剩余资源 ≥ requests」的节点。
  • limits:运行上限。CPU 是压缩型(超限被限流,进程变慢但活着),内存是杀死型(超限直接 OOMKilled)。
bash
kubectl top pod demo-api-xxxxx        # 看实际占用
kubectl describe pod demo-api-xxxxx   # 看 Events 里有没有 OOMKilled

事故场景:开发随手写 limits: {memory: 256Mi},Java 应用实际要 512Mi,上线后 Pod 反复 OOMKilled,kubectl describe 的 Events 里全是 OOMKilled,业务却以为「K8s 不稳定」。limits 必须先压测再定,内存类应用尤其如此;宁可先只设 requests 不设 limits(让 cgroup 兜底),也不要拍脑袋设一个偏小的 limits。

#滚动更新:maxUnavailable 和 maxSurge

maxUnavailable: 0 + maxSurge: 1 的意思是:先多起一个新的,确认就绪后再停一个旧的,任意时刻至少有一个旧 Pod 在服务,更新过程中容量不下降。代价是高峰时期会短暂占用双倍资源。

发布时观察:

bash
kubectl rollout status deployment/demo-api --timeout=120s
kubectl rollout history deployment/demo-api
kubectl get pods -w -l app=demo-api

滚动更新只有在前一个 Pod readiness 通过后才继续下一个,所以探针配错,滚动更新就会卡死——新版 Pod 永远不就绪,旧版被 maxUnavailable: 0 保着,更新挂起但线上没挂,这个状态很迷惑人。

#优雅退出:terminationGracePeriodSeconds

K8s 停 Pod 的顺序:先发 SIGTERM,等 terminationGracePeriodSeconds(默认 30 秒),超时再 SIGKILL。应用必须监听 SIGTERM 做优雅退出(关连接、刷缓冲),否则等于强制断电。我建议在容器里加 preStop 钩子做最后的流量摘除和延迟:

yaml
lifecycle:
  preStop:
    exec:
      command: ["/bin/sh", "-c", "sleep 5"]

sleep 5 给 Service 端点的摘除留时间,避免「Pod 已经退出,请求还在往里打」。这个 5 秒 + 应用自身的优雅退出,是生产发布「零抖动」的最简单保障。

#经验

  • 探针、资源、优雅退出这三样配齐,Deployment 才算「生产可用」,缺一个都迟早出事。
  • 每次发布前 kubectl apply --dry-run=server -f deploy.yaml 先让 API Server 校验一遍,能拦住绝大部分语法错误。
  • 出问题先看 kubectl describe pod 的 Events 和 kubectl logs --previous,一半的 Deployment 事故都能从这里定位。

写于 2026 年 6 月 4 日

栏目
技术文章
约
3.3 分钟
字数
2.7K
阅读
297

本文为原创记录,转载请注明出处。如果这篇替你省了时间,欢迎留言说说你踩到的坑。

同题 · related

留言 · remarks

00 条

还没有留言,来说点什么吧。

Kubernetes Deployment 配置:副本、探针与资源限制 · LXH·BLOG