k8s 中的存活和就绪检测

发布于 2024-06-22 19:26:49 字数 5233 浏览 23 评论 0

最近生产环境 k8s 部署的应用出现了因未启用存活和就绪检测而导致的接口成功率下降的问题,这里记录分享一下。

问题原因很简单,就是应用未配置就绪检测(readiness)导致未正常启动的应用被分配到流量而产生 502 错误。解决方案虽然很简单,但是还是有必要梳理清楚 k8s 中的存活和就绪检测,以及他们应该何时被启用。

虽然官方给了 存活和就绪检测的文档 说明,但更多是如何配置,对于是否应该启用,哪些场景下启用并未给出详细解释,下面我们就对这两个选项做详细解释。

0x01 就绪检测

readiness 字面意思是准备就绪(ready 的进行时状态),因此就绪检测本质上是探测应用是否运行正常,以决定是否将负载加入 service 中并为其分配流量。

与之相似的有负载均衡中的健康检测概念,当配置健康检测后,负载均衡会定时调用健康检测配置的地址进行探测,以 便确认应用是否存活,一旦发现异常,则会将后端 RS(服务)设置为异常,并不在向该 RS 转发任何流量。与此同时,负载均衡还会不断向后端 RS 发出健康检测 的请求,来探测应用是否恢复正常,如果探测发现应用已经恢复正常,则将后端 RS 设置为正常,并恢复流量转发。

负载均衡均衡的健康检测配置

健康检测异常导致的 RS 异常

因此,对于一个生产环境运行的服务,必须要配置 liveness 存活检测(健康检测),避免在服务未启动完成或服务进程出现异常时,依然有请求进来而导致错误响应(或者不响应)。

0x02 存活检测

存活检测属于应用运行中的一种检测,当它发现应用异常时,会尝试重启容器,在超过一定时间后( terminationGracePeriodSeconds ,默认 30s)时间,服务依然异常,会尝试重启 Pod 来达到自愈的效果。

类比非容器部署,一般我们会在机器上部署一个脚本,脚本会监控服务进程、端口或者健康检测接口来判定服务是否异常,如果异常,脚本会尝试重启服务恢复服务正常。

相比于就绪检测只能剔除 Pod 流量,存活检测可以通过尝试重启来实现服务的自愈,一般服务推荐增加此配置,避免大量 Pod 异常流量被调度走而导致剩下 Pod 负载过高产生雪崩。当然,如果服务是有状态的,并且不能简单暴力重启,那就需要斟酌一下是否启用该配置。

Liveness 检测异常事件

Liveness 检测异常会导致容器重启

0x03 如何配置

就绪检测和存活检测配置相同,支持TCP 端口检测HTTP 请求检测以及执行命令检测的方式。

检测三种方式

三种方式的说明如下:

  1. TCP 端口检测:通过探测端口是否可以联通(TCP 三次握手成功)来判断服务是否正常,适用于纯 TCP 服务。
  2. HTTP 请求检测:通过发送一个 HTTP GET 请求(可以指定请求的 host、path 和 header,但是指定请求 method),然后通过响应状态码来判定,状态码在 200 到 400 之间表示服务正常。
  3. 执行命令检测:通过在容器内执行一个命令,命令返回状态码为 0 表示服务正常。

对于 web 服务,一般推荐使用HTTP 请求检测,并且应用本身最好添加一个健康检测路由(如: /ping/healthz 等),例如 Golang 可以指定一下路由:

http.HandleFunc("/ping", func(w http.ResponseWriter, r *http.Request) {
	w.WriteHeader(200)
	w.Write([]byte("pong"))
}

详细的配置选项, 官方文档 中有详细说明,下面分别是TCP 端口检测HTTP 请求检测示例:

-- TCP 端口检测示例
apiVersion: v1
kind: Pod
metadata:
  name: goproxy
  labels:
    app: goproxy
spec:
  containers:
  - name: goproxy
    image: registry.k8s.io/goproxy:0.1
    ports:
    - containerPort: 8080
    readinessProbe:
      tcpSocket:
        port: 8080
      initialDelaySeconds: 10
      periodSeconds: 3
      successThreshold: 1
      timeoutSeconds: 2
      failureThreshold: 3
    livenessProbe:
      tcpSocket:
        port: 8080
      initialDelaySeconds: 10
      periodSeconds: 3
      successThreshold: 1
      timeoutSeconds: 2
      failureThreshold: 6
-- HTTP 请求检测示例
apiVersion: v1
kind: Pod
metadata:
  name: goproxy
  labels:
    app: goproxy
spec:
  containers:
  - name: goproxy
    image: registry.k8s.io/goproxy:0.1
    ports:
    - containerPort: 8080
    readinessProbe:
      httpGet:
        path: /ping
        port: 8080
        httpHeaders:
        - name: Custom-Header
          value: Awesome
      initialDelaySeconds: 10
      periodSeconds: 3
      successThreshold: 1
      timeoutSeconds: 2
      failureThreshold: 3
    livenessProbe:
      httpGet:
        path: /ping
        port: 8080
        httpHeaders:
        - name: Custom-Header
          value: Awesome
      initialDelaySeconds: 10
      periodSeconds: 3
      successThreshold: 1
      timeoutSeconds: 2
      failureThreshold: 6

这里需要注意的点是一定要指定 initialDelaySeconds (启动延迟)、 periodSeconds (间隔时间)以及 failureThreshold (不健康阈值)这些配置选项到一个合理的值。通过指定 initialDelaySeconds 保证应用已经启动,而 periodSeconds 值不可设置太大,避免故障时间过长。 failureThreshold 的值需要合理设置,设置过大会导致异常服务长时间在线,过小则会误判,导致服务频繁剔除,建议设置为 3。

另外一点需要注意的是,同时启用就绪检测存活检测的时候,建议将存活检测中的 failureThreshold (不健康阈值)设置稍大一些,这样在应用异常时,可以由就绪检测先剔除流量,然后再由存活检测重启容器和 Pod。

最后再总结一下:

检测类型说明失败动作
就绪检测类似健康检测,将 Pod 设置为异常,从而从 Service 中剔除流量。剔除 Pod 流量
存活检测类似服务存活检测脚本,帮助服务自动愈合。重启故障容器, terminationGracePeriodSeconds 重启 Pod

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据

关于作者

文章
评论
28 人气
更多

推荐作者

櫻之舞

文章 0 评论 0

弥枳

文章 0 评论 0

m2429

文章 0 评论 0

野却迷人

文章 0 评论 0

我怀念的。

文章 0 评论 0

    我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
    原文