前言
前面几篇主要讲了 Kubernetes 里的核心对象、访问链路、配置和存储。但真正把服务放到 k8s 里跑,最容易出问题的往往不是“能不能启动”,而是发布和排障。
一个应用第一次部署成功,只能说明基础链路通了。后面每次升级、扩缩容、节点故障、依赖抖动,才是在考验整套运行体系。
这篇就把发布、探针、资源限制、日志和常见排障路径放在一起梳理。它们看起来分散,但都是生产可用性的一部分。
滚动发布依赖 readiness
Deployment 默认支持滚动更新。更新镜像后,k8s 会逐步创建新 Pod,再逐步下掉旧 Pod。
但滚动发布能不能稳,很大程度取决于 readinessProbe。
readinessProbe 表示这个 Pod 是否已经准备好接收流量。如果新 Pod 只是进程启动了,但数据库连接还没初始化、缓存还没预热、Spring Boot 还没完成启动,就不应该接流量。
一个常见配置:
readinessProbe:
httpGet:
path: /actuator/health/readiness
port: 8080
initialDelaySeconds: 20
periodSeconds: 10
timeoutSeconds: 3
failureThreshold: 3
对于 Java 服务,启动时间可能受类加载、JIT、配置中心、数据库连接影响。探针时间不能照搬 demo,要按真实启动情况调整。
如果 readiness 太激进,新 Pod 还没准备好就接流量。如果太保守,发布速度会变慢。这里没有固定答案,要结合服务启动曲线和业务可接受时间。
liveness 不要滥用
livenessProbe 表示容器是否还活着。如果 liveness 失败,k8s 会重启容器。
它适合处理进程卡死、死锁、主线程异常但进程未退出这类问题。
但不要把所有依赖检查都塞进 liveness。比如数据库短暂不可用,如果 liveness 也检查数据库,那应用可能会被反复重启。重启本身不能修复数据库,反而会扩大故障。
我的理解是:
1.readiness 负责决定能不能接流量。 2.liveness 负责判断进程是否需要重启。 3.startupProbe 负责给慢启动应用更多启动时间。
这三个探针职责不同,混在一起会让故障处理变得不可控。
资源限制要认真写
k8s 调度和稳定性都依赖资源配置。只写镜像和副本,不写 requests 和 limits,在测试环境可能没事,在生产环境很容易出问题。
requests 表示调度时需要预留的资源,limits 表示容器最多能使用的资源。
resources:
requests:
cpu: 500m
memory: 768Mi
limits:
cpu: "1"
memory: 1024Mi
对于 Java 服务,内存尤其要小心。容器 memory limit、JVM 最大堆、元空间、线程栈、直接内存都要一起考虑。
如果 limit 设得太小,可能出现 OOMKilled。如果 requests 设得太低,调度器会把太多 Pod 放到同一节点,节点压力上来以后延迟会很难看。
资源配置不是一次写完就结束。上线后要结合真实指标调整,而不是长期沿用模板里的默认值。
日志要从第一天按容器方式处理
在 k8s 里,应用日志最好输出到 stdout 和 stderr,再由节点或集群日志系统采集。
不要继续按传统方式把日志写到容器内部某个文件目录,然后指望 SSH 到机器上看。Pod 会迁移,容器会重启,本地文件也不一定还在。
排查时可以先用:
kubectl logs deploy/order-service
kubectl logs pod/order-service-xxx --previous
--previous 很有用,可以查看上一个崩溃容器实例的日志。很多 CrashLoopBackOff 问题,当前容器已经重新启动,真正的异常在 previous 日志里。
长期看,还是要接集中式日志平台。至少能按 namespace、app、pod、traceId 查询。否则线上问题只能临时翻 kubectl,效率会很低。
describe 比 get 更能说明问题
kubectl get 适合快速看状态,但排障时经常不够。
比如 Pod 一直 Pending,get 只能看到没启动。真正原因可能是资源不足、节点选择器不匹配、PVC 绑定失败、镜像拉取权限问题。
这时候要看 describe:
kubectl describe pod order-service-xxx
重点看 Events。调度失败、拉镜像失败、探针失败、挂载失败,通常都会在事件里留下线索。
如果 Pod 已经 Running,但服务不可用,再继续看日志、探针、Service endpoints 和应用自身健康接口。
排障不要一上来就重启。重启可能会掩盖现场,而且不一定解决根因。
常见状态要会读
几个常见状态值得记住:
Pending:Pod 还没调度或还没完成准备。常见原因是资源不足、镜像拉取等待、PVC 未绑定。
ImagePullBackOff:镜像拉取失败。检查镜像名、tag、仓库地址、镜像仓库凭证。
CrashLoopBackOff:容器启动后不断崩溃。优先看日志和 previous 日志。
Running 但 NotReady:进程在跑,但 readinessProbe 没通过。检查健康接口、依赖、启动时间、探针路径和端口。
OOMKilled:容器超过内存限制被杀。检查 JVM 参数、内存 limit、流量变化和最近代码改动。
这些状态不是最终答案,但能指明排查方向。
发布前最好有最小检查清单
我比较喜欢给 k8s 应用准备一个很短的发布检查清单:
1.Deployment 副本数是否合理。
2.Service selector 是否能匹配 Pod。
3.readinessProbe 是否能真实反映可接流量状态。
4.resources requests 和 limits 是否写了。
5.镜像 tag 是否可追踪,不用 latest。
6.日志是否输出到 stdout。
7.关键环境变量和 Secret 是否存在。
8.回滚命令和上一版本镜像是否明确。
这份清单不需要很复杂,但应该成为习惯。很多事故不是因为 Kubernetes 特别难,而是因为基础项没有检查。
小结
Kubernetes 的价值不只是把应用跑起来,还包括持续发布、自动恢复、弹性扩缩和统一观测。
但这些能力不会凭空生效。readiness、liveness、resources、日志、事件、监控指标,都需要在应用接入时一起设计。
我的经验是,先把最普通的 Deployment + Service + Ingress 做稳,再补齐探针、资源、日志和排障手段。等这些基础能力可靠以后,再谈灰度、自动扩缩容和更复杂的平台能力,才比较踏实。
If you enjoyed this, leave a comment~