摘要K8s 滚动更新频繁失败服务中断、请求 502、回滚手忙脚乱我踩过所有坑。从 readinessProbe 配置到资源限制从更新策略到优雅关闭分享一套完整的生产级发布方案。实测发布事故率从 30% 降到 0零停机成为常态。配置模板已开源。开篇Kubernetes介绍Kubernetes简称 K8s 是由 Google开源的容器编排管理平台用于自动化部署、扩展和管理容器化应用现由云原生计算基金会CNCF托管维护 。它有以下特点一、核心运维能力一自动化部署与回滚Kubernetes 支持分步骤完成应用及配置的上线操作全程监控应用运行状态避免实例被同时终止若上线过程出现异常可自动触发版本回滚保障应用服务稳定性。二自我修复能力重启故障容器节点故障时自动替换并重新调度容器资源终止未通过用户定义健康检查的容器待容器就绪后再对外提供服务混合部署关键业务与尽力而为类工作负载平衡资源利用率与服务可用性。三弹性扩缩机制支持通过命令、UI 手动触发应用扩缩可基于 CPU 使用率等指标自动完成应用水平扩缩适配不同流量场景。二、网络与存储能力一网络管理服务发现与负载均衡为容器分配独立 IP 地址与 DNS 名称无需修改应用代码即可实现服务发现与跨容器负载均衡双协议栈支持为 Pod 和 Service 同时分配 IPv4 与 IPv6 地址适配多网络环境。二存储编排自动挂载各类存储系统覆盖存储类型如下本地存储公有云存储如 AWS、GCP 提供的存储服务网络存储如 NFS、iSCSI、Ceph、Cinder。三、配置与安全能力一配置与密钥管理支持部署及更新 Secret密钥与应用配置无需重新构建容器镜像避免敏感配置信息暴露在软件堆栈中。二资源智能调度根据容器资源需求与各类限制条件自动完成容器节点分配最大化集群资源利用率。四、扩展与批量作业能力一批量作业管理除常规服务类工作负载外可统一管理批处理任务与 CI 工作负载支持自动替换失效容器保障批量任务执行效率。二集群扩展性基于开源生态设计无需修改上游源码即可实现 Kubernetes 集群的自定义扩展适配多样化业务场景需求。去年双十一前夕我们有个核心服务要上线新版本。测试环境一切正常结果生产环境一发布直接炸了。滚动更新过程中旧 Pod 被杀掉新 Pod 还没 ready流量打过来全是 502。监控告警响成一片老板在群里我怎么回事用户投诉电话都打爆了。最后紧急回滚但已经影响了大概 5 分钟。虽然时间不长但那天订单量巨大损失不小。事后复盘问题其实很低级readinessProbe 没配资源限制没设更新策略用的默认值。三个坑我全踩了。接下来一个月我把 K8s 滚动更新的配置从头到尾研究了一遍参考了官方文档、社区最佳实践还有大厂开源的配置模板。最后整理出一套生产级方案之后半年多发布再也没出过事故。今天就把这套配置完整分享出来。如果你也在用 K8s 做服务发布希望能帮你避开我踩过的坑。问题出在哪先搞懂滚动更新的流程很多人配 K8s就是把 YAML 复制粘贴改个镜像 tag 就完事了。结果一发布就翻车。得先搞明白滚动更新到底发生了什么1. 创建新 PodReplicaSet 扩容 2. 新 Pod 启动通过 readinessProbe 检查 3. 新 Pod ready 后流量切过来 4. 旧 Pod 被标记为 Terminating 5. 旧 Pod 执行 preStop hook如果有 6. 旧 Pod 收到 SIGTERM开始优雅关闭 7. 宽限期结束后SIGKILL 强制杀掉问题往往出在 2、5、6 这三个环节。新 Pod 没 ready 就接流量旧 Pod 没处理完请求就被杀掉中间就会出现服务中断。第一招readinessProbe 必须配而且得配对2.1 为什么 livenessProbe 不够用很多人只配 livenessProbe觉得 Pod 活着就能接流量。大错特错。livenessProbe 只管 Pod 死活readinessProbe 才管 Pod 能不能接客。我见过太多案例Pod 启动了但应用初始化还没完成比如连数据库、加载缓存这时候 livenessProbe 过了但 readinessProbe 应该没过。# 错误示范只配了 livenessProbe livenessProbe: httpGet: path:/health port:8080 initialDelaySeconds:10 periodSeconds:10 # 正确做法两个都配且 readinessProbe 更严格 readinessProbe: httpGet: path:/ready port:8080 initialDelaySeconds:5 periodSeconds:5 failureThreshold:3 timeoutSeconds:2 livenessProbe: httpGet: path:/health port:8080 initialDelaySeconds:30 periodSeconds:10 failureThreshold:3这里有个细节/ready和/health应该是两个不同的端点。/ready检查应用是否准备好接客依赖是否就绪/health检查应用是否还活着进程是否在。2.2 初始化依赖的检查如果应用启动时需要连数据库、Redis、消息队列别在代码里重试就算了直接把依赖检查写进 readinessProbereadinessProbe: exec: command: - /bin/sh - -c - | curl -f http://localhost:8080/ready || exit 1 # 额外检查数据库连接 pg_isready -h $DB_HOST -p $DB_PORT || exit 1 initialDelaySeconds: 10 periodSeconds: 5说实话这个配置救过我很多次。有次数据库升级新 Pod 启动后连不上旧版数据库readinessProbe 一直失败流量就没切过去避免了线上事故。第二招更新策略得调优默认值真的不行3.1 maxSurge 和 maxUnavailableK8s 滚动更新有两个关键参数maxSurge最多可以超出期望副本数的 Pod 数量maxUnavailable最多可以不可用的 Pod 数量默认值是 25%但这个值对于不同场景可能不合适。strategy: type: RollingUpdate rollingUpdate: maxSurge: 1 # 每次多创建 1 个 Pod maxUnavailable: 0 # 不允许有 Pod 不可用对于核心服务我建议maxUnavailable设为 0保证发布过程中始终有足够容量的 Pod 在处理流量。maxSurge设为 1 或 2控制发布速度。如果集群资源紧张可以反过来maxSurge: 0, maxUnavailable: 1但这样发布期间容量会下降得评估是否能承受。3.2 minReadySeconds这个参数很多人不知道但非常有用。它指定 Pod ready 后至少要等多久才算真正可用。minReadySeconds: 30为什么需要这个因为 readinessProbe 通过后应用可能还在做最后的初始化比如预热缓存、建立连接池。给它 30 秒缓冲避免流量进来时应用还没完全准备好。我有个服务readinessProbe 过了之后还需要大概 20 秒加载热点数据到内存。一开始没配 minReadySeconds结果发布后前几秒请求延迟很高。加上这个配置问题解决了。第三招优雅关闭必须做SIGTERM 不是摆设4.1 preStop hook旧 Pod 被杀掉之前会先收到 SIGTERM 信号。但很多应用根本没处理这个信号直接硬死。更好的做法是加 preStop hook在收到 SIGTERM 后主动停止接收新请求lifecycle: preStop: exec: command: - /bin/sh - -c - | # 先从服务发现中摘除自己 curl -X POST http://localhost:8080/admin/deregister # 等 10 秒让已有请求处理完 sleep 10这个 sleep 很关键。因为 K8s 从把 Pod 标记为 Terminating 到实际发送 SIGTERM中间有时间差。preStop 执行期间Endpoint 会被移除流量不会再进来。sleep 这段时间专门用来处理已经在处理的请求。4.2 terminationGracePeriodSeconds这个参数指定从发送 SIGTERM 到强制 SIGKILL 的宽限期。默认 30 秒对于很多应用可能不够。terminationGracePeriodSeconds: 60我建议根据应用实际情况设置。如果有关键事务处理比如订单支付给长一点如果是无状态服务30 秒可能够了。但注意这个时间不是越长越好。设置太长会导致发布变慢而且如果应用真的卡死了长时间无法释放资源。资源限制别省这点内存发布翻车的另一个常见原因新 Pod 因为资源不足启动失败或者启动后被 OOMKilled。resources: requests: cpu: 500m memory: 512Mi limits: cpu: 1000m memory: 1Girequests 是调度时预留的资源limits 是硬上限。我建议requests 设为应用正常运行时的实际用量可以通过监控历史数据得出limits 设为 requests 的 1.5-2 倍给突发流量留缓冲千万别把 limits 设得太紧。有次我把内存 limits 设为 512Mi结果某个版本代码有内存泄漏Pod 反复被 OOMKilled滚动更新卡住了。踩坑记录坑 1健康检查端点实现不对很多人/health端点直接返回 200什么都不检查。这等于没配。正确的健康检查应该包括数据库连接是否正常关键依赖服务是否可达应用内部状态是否正常比如线程池是否耗尽坑 2镜像拉取策略如果用imagePullPolicy: Always每次发布都要拉镜像发布时间会很长。建议用 tag 版本管理配合IfNotPresent。坑 3忽略节点资源发布时新 Pod 调度失败因为集群资源不足。建议设置 PodDisruptionBudget保证发布期间至少有 N 个 Pod 可用apiVersion: policy/v1 kind:PodDisruptionBudget metadata: name:myapp-pdb spec: minAvailable:2 selector: matchLabels: app:myapp完整配置模板apiVersion: apps/v1 kind:Deployment metadata: name:myapp spec: replicas:3 strategy: type:RollingUpdate rollingUpdate: maxSurge:1 maxUnavailable:0 minReadySeconds:30 template: spec: terminationGracePeriodSeconds:60 containers: -name:myapp image:myapp:v1.2.3 imagePullPolicy:IfNotPresent ports: -containerPort:8080 readinessProbe: httpGet: path:/ready port:8080 initialDelaySeconds:5 periodSeconds:5 failureThreshold:3 timeoutSeconds:2 livenessProbe: httpGet: path:/health port:8080 initialDelaySeconds:30 periodSeconds:10 failureThreshold:3 lifecycle: preStop: exec: command:[/bin/sh,-c,curl -X POST http://localhost:8080/admin/deregister sleep 10] resources: requests: cpu:500m memory:512Mi limits: cpu:1000m memory:1Gi这个模板我们用了半年多发布过几十次零事故。写在最后K8s 滚动更新这个功能本身很强大但默认配置真的不适合生产环境。我分享的这些配置每个都是用事故换来的经验。如果你也在用 K8s 做服务发布建议对照检查一下readinessProbe 配了吗更新策略调了吗优雅关闭做了吗觉得有用的话点个赞或者在看让我知道这篇文章帮到你了。也欢迎关注后续会分享更多 K8s 实战经验。配置模板我整理成了 GitHub 仓库有需要的可以自取。评论区也可以聊聊你们在 K8s 发布中遇到过什么坑。官方网站https://kubernetes.p2hp.com/