Kubernetes Deployment 配置:副本、探针与资源限制
从探针分工、资源配额到滚动更新与优雅退出,把 Deployment 配成生产可用的状态。
Deployment 大概是 Kubernetes 里被用得最多的对象,但也是配置翻车重灾区。我见过最典型的事故:镜像版本写错,readinessProbe 路径配错,流量全部打到不健康的 Pod 上,线上接口 50% 超时。这篇文章把 Deployment 里每个生产必配的字段讲透。
#先分清三个探针
很多人 readiness 和 liveness 混着写,这是大忌:
- readinessProbe:告诉我「能不能接流量」。失败时 Pod 不健康,但不会被杀掉,只是从 Service 的 Endpoints 里摘掉。就绪。
- livenessProbe:告诉我「是不是死透了」。连续失败会被 kubelet 杀掉并重启。存活。
- startupProbe:给启动慢的应用用的。它成功之前,liveness 不生效,防止启动 30 秒的应用因为 liveness 超时被反复杀。
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)。
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 在服务,更新过程中容量不下降。代价是高峰时期会短暂占用双倍资源。
发布时观察:
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 钩子做最后的流量摘除和延迟:
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
- ArgoCD 实战:用 GitOps 把 CI/CD 部署搬进 Kubernetes从安装、Application 声明到 App of Apps 与 CI 配合,把 GitOps 部署体系在 Kubernetes 上落地,并复盘真实事故。
- 把博客从 docker-compose 搬到 k3s:单机迁移与上线全过程在 4C3.6G 的云主机上把博客从 docker-compose 迁到单机 k3s。附完整 YAML 清单(Deployment、Service、PVC、Ingress、Certificate 等)与全部配置命令,记录部署、数据迁移与切换上线的踩坑过程。
- 5 台服务器,从二进制装 K8s 到 Spring Cloud 上线用 5 台服务器把整套平台搭出来:二进制部署 Kubernetes 1.36 高可用集群,装完 Harbor、Gitea、Nexus、Jenkins、Argo CD、监控日志链路追踪,最后把 Spring Cloud 微服务项目发布上线。每一步都给可执行命令。
留言 · remarks
00 条还没有留言,来说点什么吧。