记一次k8s pod频繁重启的优化之旅

1.背景

最近有运维反馈某个微服务频繁重启,客户映像特别不好,需要我们尽快看一下。

听他说完我立马到监控平台去看这个服务的运行情况,确实重启了很多次。对于技术人员来说,这既是压力也是动力,大多数时候我们都是沉浸在单调的业务开发中,对自我的提升有限,久而久之可能会陷入一种舒适区,遇到这种救火案例一时间会有点无所适从,但是没关系,毕竟…

“我只是收了火,但没有熄炉”,借用电影中的一句话表达一下此时的心情。

2.初看日志

我当即就看这个服务的运行日志,里面有大量的oom异常,如下:

org.springframework.web.util.NestedServletException: Handler dispatch failed;
nested exception is java.lang.OutOfMemoryError: GC overhead limit exceeded

整个服务基本可以说处于不可用状态,任何请求过来几乎都会报oom,但是这跟重启有什么关系呢?是谁触发了重启呢?这里我暂时卖个关子,后面进行解答。

3.k8s健康检查介绍

我们的服务部署在k8s中,k8s可以对容器执行定期的诊断,要执行诊断,kubelet 调用由容器实现的 Handler (处理程序)。有三种类型的处理程序:

  • ExecAction:在容器内执行指定命令。如果命令退出时返回码为 0 则认为诊断成功。

  • TCPSocketAction:对容器的 IP 地址上的指定端口执行 TCP 检查。如果端口打开,则诊断被认为是成功的。

  • HTTPGetAction:对容器的 IP 地址上指定端口和路径执行 HTTP Get 请求。如果响应的状态码大于等于 200 且小于 400,则诊断被认为是成功的。

每次探测都将获得以下三种结果之一:

  • Success(成功):容器通过了诊断。

  • Failure(失败):容器未通过诊断。

  • Unknown(未知):诊断失败,因此不会采取任何行动。

针对运行中的容器,kubelet 可以选择是否执行以下三种探针,以及如何针对探测结果作出反应:

  • livenessProbe:指示容器是否正在运行。如果存活态探测失败,则 kubelet 会杀死容器, 并且容器将根据其重启策略决定未来。如果容器不提供存活探针, 则默认状态为 Success。

  • readinessProbe:指示容器是否准备好为请求提供服务。如果就绪态探测失败, 端点控制器将从与 Pod 匹配的所有服务的端点列表中删除该 Pod 的 IP 地址。初始延迟之前的就绪态的状态值默认为 Failure。如果容器不提供就绪态探针,则默认状态为 Success。

  • startupProbe: 指示容器中的应用是否已经启动。如果提供了启动探针,则所有其他探针都会被 禁用,直到此探针成功为止。如果启动探测失败,kubelet 将杀死容器,而容器依其 重启策略进行重启。如果容器没有提供启动探测,则默认状态为 Success。

以上探针介绍内容来源于https://kubernetes.io/zh/docs/concepts/workloads/pods/pod-lifecycle/#container-probes

看完探针的相关介绍,可以基本回答上面的疑点**“oom和重启有什么关系?”,**是livenessProbe的锅,简单描述一下为什么:

  1. 服务启动;

  2. k8s通过livenessProbe中配置的健康检查Handler做定期诊断(我们配置的是HttpGetAction);

  3. 由于oom所以HttpGetAction返回的http status code是500,被k8s认定为Failure(失败)-容器未通过诊断;

  4. k8s认为pod不健康,决定“杀死”它然后重新启动。

这是服务的Deployment.yml中关于livenessProbe和restartPolicy的配置

在 Kubernetes 中,Pod 频繁重启是一个常见的运维问题,通常由以下几个方面引发: ### 1. 应用程序崩溃或异常退出 如果容器内的应用程序在运行过程中发生异常退出(例如内存溢出、未捕获的异常、逻辑错误等),Kubernetes 会根据重启策略(`restartPolicy`)重新启动容器。频繁重启通常表现为 `CrashLoopBackOff` 状态,即容器在短时间内多次崩溃并进入等待重启的状态。这种情况可以通过查看容器日志进行排查: ```bash kubectl logs <pod名称> -n <命名空间> ``` 如果容器被反复重启,可以指定容器名称查看历史日志: ```bash kubectl logs <pod名称> -n <命名空间> --previous ``` ### 2. 探针失败导致重启 Kubernetes 提供了两种健康检查探针:`livenessProbe`(存活探针)和 `readinessProbe`(就绪探针)。如果 `livenessProbe` 探测失败,Kubernetes 会认为容器已经不可用,并触发容器重启频繁重启可能是因为应用响应时间过长、健康检查接口不稳定或配置不合理(如超时时间过短)[^3]。 常见的排查方式包括: - 检查探针配置,确认 `initialDelaySeconds` 和 `failureThreshold` 是否合理。 - 验证 `/actuator/health` 或其他健康检查接口的响应时间和稳定性。 ### 3. 资源限制不足 Kubernetes 允许为容器设置资源限制(如 CPU 和内存)。如果容器使用的资源超过限制,Kubernetes 会终止容器并录 `OOMKilled`(内存溢出)或 CPU 限制被强制终止的情况。这种情况可以通过以下命令查看事件信息: ```bash kubectl describe pod <pod名称> -n <命名空间> ``` 如果发现事件中包含 `Out of memory` 或 `OOMKilled`,则说明容器可能因资源不足被终止。可以通过调整资源请求和限制来缓解该问题: ```yaml resources: requests: memory: "256Mi" cpu: "100m" limits: memory: "512Mi" cpu: "500m" ``` ### 4. 初始化容器失败 如果 Pod 中包含初始化容器(Init Container),并且初始化容器未能成功完成,主容器将不会启动。在这种情况下,Pod 会处于 `Pending` 状态或不断尝试重新启动。可以通过以下命令查看 Init Container 的状态: ```bash kubectl describe pod <pod名称> -n <命名空间> ``` 如果发现 Init Container 失败,可以查看其日志进行排查: ```bash kubectl logs <pod名称> -n <命名空间> -c <init-container名称> ``` ### 5. 镜像拉取失败 如果容器镜像无法拉取(例如镜像名称错误、私有镜像未授权、网络问题等),Kubernetes 会尝试多次拉取镜像并导致 Pod 无法正常启动。可以通过以下命令查看事件信息: ```bash kubectl describe pod <pod名称> -n <命名空间> ``` 如果发现事件中包含 `ErrImagePull` 或 `ImagePullBackOff`,则说明镜像拉取失败。可以检查镜像名称、镜像仓库权限和网络配置。 ### 6. 配置错误或环境问题 Pod 的配置错误(如环境变量配置错误、卷挂载失败、依赖服务不可用等)也可能导致容器启动失败。可以通过以下方式排查: - 检查 Pod 的 YAML 配置文件,确认环境变量、卷挂载等配置是否正确。 - 检查依赖服务(如数据库、API 服务)是否可用。 ### 7. 资源竞争或调度问题 在某些情况下,Pod 可能因资源竞争或调度问题无法正常运行。例如,节点资源不足、Pod 优先级较低、节点故障等。可以通过以下命令查看节点状态和事件信息: ```bash kubectl describe node <节点名称> ``` ### 8. 应用性能瓶颈 如果应用性能瓶颈导致请求处理延迟,可能会触发 Kubernetes 的健康检查失败,从而导致容器重启。例如,Spring Boot 应用的默认线程数为 200,如果请求超过线程处理能力,会导致请求排队并最终超时,触发 Kubernetes 杀死容器并重启[^3]。可以通过调整线程池配置或优化应用性能来缓解该问题。 --- ###
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值