K8s学习(八):Pod健康检查

1. 介绍

强大的自愈能力是Kubernetes这类容器编排引擎的一个重要特性。自愈的默认实现方式是自动重启发生故障的容器。除此之外,用户还可以利用LivenessReadiness探测机制设置更精细的健康检查,进而实现如下需求:

  • 零停机部署。
  • 避免部署无效的镜像。
  • 更加安全的滚动升级。

2. 默认健康检查

K8s学习(七):深入了解Pod文章中,简单介绍过的Pod的重启策略,便是Kubernetes默认的健康检查机制,如果进程退出时返回码非零,则认为容器发生故障,Kubernetes就会根据restartPolicy重启容器,具体重启策略如下:

  • Always: 表示容器失效时,由kubelet自动重启该容器。
  • OnFailure: 表示容器终止运行且退出码不为0时,由kubelet自动重启该容器。
  • Nerver:表示不论容器运行状态如何,kubelet都不会重启该容器。

下面示例是Pod运行10秒后,以退出码非0的方式退出, 来模拟Pod发生故障,看看默认机制怎么处理

2.1 编写Yaml

下面以nginx pod为示例:

apiVersion: v1
kind: Pod
metadata:
  name: nginx-pod
  namespace: test
spec:
  restartPolicy: OnFailure # 设定重启策略
  containers:
    - name: health-check
      image: nginx
      args:
       - /bin/sh
       - -c
       - sleep 10; exit 1 # 模拟容器启动10秒后发生故障(退出码不为0时)

2.2 创建 & 检查

# 为了测试创建命名空间
$ kubectl create namespace test
namespace/test created
# 创建Pod
$ kubectl apply -f nginx-pod.yaml
pod/nginx-pod created
# 查看Pod 状态,发现Pod已经重启(RESTARTS)了2次
$ kubectl get pod -n test -o wide
NAME        READY   STATUS   RESTARTS      AGE   ...
nginx-pod   0/1     Error    2 (36s ago)   64s   ...

2.3 总结

在上面的例子中,容器进程返回值非零,Kubernetes则认为容器发生故障,需要重启。有不少情况是发生了故障,但进程并不会退出。

比如访问Web服务器时显示500内部错误,可能是系统超载,也可能是资源死锁,此时httpd进程并没有异常退出,在这种情况下重启容器可能是最直接、最有效的解决方案,那我们如何利用Health Check机制来处理这类场景呢? 答案:就是存活探针。

3. 存活探针

KubernetesPod的健康状态除了通过默认机制检查,还可以通过两类探针来检查:LivenessProbeReadinessProbekubelet定期执行这两类探针来诊断容器的健康状况。

3.1 LivenessProbe

用于判断容器是否存活(Running状态)

  • 如果LivenessProbe探针探测到容器不健康,则kubelet将杀掉该容器,并根据容器的重启策略做相应的处理。
  • 如果一个容器不包含LivenessProbe探针,那么kubelet认为该容器的LivenessProbe探针返回的值永远是Success

3.2 ReadinessProbe

用于判断容器服务是否可用(Ready状态),达到Ready状态的Pod才可以接收请求。

对于被Service管理的PodServicePod Endpoint的关联关系也将基于Pod是否Ready进行设置。

  • 如果在运行过程中Ready状态变为False,则系统自动将其从Service的后端Endpoint列表中隔离出去>

  • 如果在运行过程中Ready状态变为True,就会把Pod加回后端Endpoint列表。

这样就能保证客户端在访问Service时不会被转发到服务不可用的Pod实例上。

3.3 实现方式

LivenessProbeReadinessProbe均可配置以下三种实现方式。

  • Exec:在容器内部执行一个命令,如果该命令的返回码为0`,则表明容器健康。
  • TCPSocket:通过容器的IP地址和端口号执行TCP检查,如果能够建立TCP连接,则表明容器健康。
  • HTTPGet:通过容器的IP地址、端口号及路径调用HTTP Get方法,如果响应的状态码大于等于200且小于400,则认为容器健康。

4. LivenessProbe实践

Liveness探测让用户可以自定义判断容器是否健康的条件。如果探测失败,Kubernetes就会重启容器。

4.1 Exec 方式

a. 编辑yaml

文件:gin-hello-deploy.yaml

apiVersion: apps/v1 # 配置格式版本
kind: Deployment   #资源类型,Deployment
metadata:
  name: gin-hello-deploy #Deployment 的名称
spec:
  replicas: 3   # 副本数量
  selector:   #标签选择器,
    matchLabels: 
      app: gin_hello_pod
  template:    #Pod信息
    metadata:
      labels:  #Pod的标签
        app: gin_hello_pod
    spec:
      containers:
      - name: gin-hello #pod的名称
        image: docker.io/liuqinghui/gin-hello:v3
        args:
         - /bin/sh
         - -c
         - touch /tmp/health; sleep 30; rm -rf /tmp/health;sleep 600 # 指定命令
        livenessProbe:
         exec:
          command:
           - cat 
           - /tmp/health
         initialDelaySeconds: 10 #容器启动10s之后,开始执行Liveness探测
         periodSeconds: 5 #每5秒执行一次Liveness探测

启动进程首先创建文件/tmp/health,30秒后删除,在我们的设定中,如果/tmp/health文件存在,则认为容器处于正常状态,反之则发生故障。

相关字段说明:

  • initialDelaySeconds:10; 指定容器启动10秒之后开始执行Liveness探测,我们一般会根据应用启动的准备时间来设置。比如某个应用正常启动要花30秒,那么initialDelaySeconds的值就应该大于30。
  • periodSeconds:5; 指定每5秒执行一次Liveness探测。Kubernetes如果连续执行3次Liveness探测均失败,则会杀掉并重启容器。

b.创建 & 观察

# 创建资源
$ kubectl apply -f gin-hello-deploy.yaml
# 查看是否重启
$ kubectl get pod -o wide
NAME                                READY   STATUS    RESTARTS     AGE   ...
gin-hello-deploy-7ffb6bbdf9-fm7h7   1/1     Running   1 (5s ago)   81s   ...
gin-hello-deploy-7ffb6bbdf9-wz4t2   1/1     Running   1 (5s ago)   81s   ...
gin-hello-deploy-7ffb6bbdf9-xrjjk   1/1     Running   1 (5s ago)   81s   ...

4.2 HTTPGet 方式

基于HTTP的探测(HTTPGetAction)向目标容器发起一个HTTP请求,根据其响应码进行结果判定,响应码形如2xx3xx时表示检测通过.

下面通过go项目,实现随机出现系统错误500,来验证

a. 修改go代码

package main

import (
 "github.com/gin-gonic/gin"
 "math/rand"
 "os"
 "time"
)

func main() {
 engine := gin.Default()
 engine.GET("/", func(context *gin.Context) {
  // 显示主机名字
  hostName, _ := os.Hostname()
  // 正常响应
  context.JSON(200, gin.H{
   "version":  "v4",
   "hostName": hostName,
   "time":     time.Now().Format("2006-01-02 15:04:05"),
  })
 })
 // 健康检查
 engine.GET("/health", func(context *gin.Context) {
  // 模拟服务故障
  rand.Seed(time.Now().Unix())
  if rand.Intn(10) > 3 {
   panic("模拟服务故障~")
  }
  // 响应
  context.JSON(200, gin.H{})
 })
 _ = engine.Run(":8081")
}

打包成镜像docker.io/liuqinghui/gin-hello:v4

b. 编辑yaml

文件:http-livenessprobe-deploy.yaml

apiVersion: apps/v1 # 配置格式版本
kind: Deployment   #资源类型,Deployment
metadata:
  name: http-liveness-deploy #Deployment 的名称
spec:
  replicas: 3   # 副本数量
  selector:   #标签选择器,
    matchLabels: 
      app: http-liveness-pod
  template:    #Pod信息
    metadata:
      labels:  #Pod的标签
        app: http-liveness-pod
    spec:
      containers:
      - name: http-liveness #pod的名称
        imagePullPolicy: Always
        image: docker.io/liuqinghui/gin-hello:v4
        livenessProbe: # 设置http探针
          httpGet:
            path: /health
            port: 8081

c. 创建 & 观察

# 创建资源
$ kubectl apply -f http-livenessprobe-deploy.yaml
# 查看某一个pod日志
$ kubectl logs -f http-liveness-deploy-558c8b48cf-28d95
[GIN] 2022/08/16 - 03:11:19 | 200 |       82.66µs | 192.168.148.131 | GET      "/health"
[GIN] 2022/08/16 - 03:11:29 | 500 |     178.466µs | 192.168.148.131 | GET      "/health"

2022/08/16 03:11:29 [Recovery] 2022/08/16 - 03:11:29 panic recovered:
GET /health HTTP/1.1
Host: 10.244.166.155:8081
Connection: close
Accept: */*
Connection: close
User-Agent: kube-probe/1.24

模拟服务故障~
/workspace/main.go:27 (0x73688d)
/go/pkg/mod/github.com/gin-gonic/gin@v1.8.1/context.go:173 (0x730a61)
/go/pkg/mod/github.com/gin-gonic/gin@v1.8.1/recovery.go:101 (0x730a4c)
/go/pkg/mod/github.com/gin-gonic/gin@v1.8.1/context.go:173 (0x72fb46)
/go/pkg/mod/github.com/gin-gonic/gin@v1.8.1/logger.go:240 (0x72fb29)
/go/pkg/mod/github.com/gin-gonic/gin@v1.8.1/context.go:173 (0x72ec10)
/go/pkg/mod/github.com/gin-gonic/gin@v1.8.1/gin.go:616 (0x72e878)
/go/pkg/mod/github.com/gin-gonic/gin@v1.8.1/gin.go:572 (0x72e53c)
/usr/local/go/src/net/http/server.go:2916 (0x61801a)
/usr/local/go/src/net/http/server.go:1966 (0x6145b6)
/usr/local/go/src/runtime/asm_amd64.s:1571 (0x465080)
# 查看重启次数
$ kubectl get pod -o wide
NAME                                    READY   STATUS    RESTARTS      ...
http-liveness-deploy-558c8b48cf-28d95   1/1     Running   1 (30s ago)   ...
http-liveness-deploy-558c8b48cf-fqzcd   1/1     Running   1 (30s ago)   ...
http-liveness-deploy-558c8b48cf-wwvbp   1/1     Running   1 (30s ago)   ...

5. ReadinessProbe实践

除了Liveness探测,Kubernetes Health Check机制还包括Readiness探测。用户通过Liveness探测可以告诉Kubernetes什么时候通过重启容器实现自愈;Readiness探测则是告诉Kubernetes什么时候可以将容器加入到Service负载均衡池中,对外提供服务。

Readiness探测的配置语法与Liveness探测完全一样,只需要把livenessProbe换成readinessProbe

因为使用方法和Liveness一样,这里只实践命令行(Exec)方式。

5.1 编写yaml

文件:readiness-deploy.yaml

apiVersion: apps/v1 # 配置格式版本
kind: Deployment   #资源类型,Deployment
metadata:
  name: readiness-deploy #Deployment 的名称
spec:
  replicas: 1   # 副本数量
  selector:   #标签选择器,
    matchLabels:
      app: gin_readiness_pod
  template:    #Pod信息
    metadata:
      labels:  #Pod的标签
        app: gin_readiness_pod
    spec:
      containers:
      - name: readiness-pod #pod的名称
        image: docker.io/liuqinghui/gin-hello:v3
        args:
         - /bin/sh
         - -c
         - touch /tmp/health; sleep 30; rm -rf /tmp/health;sleep 600 # 指定命令
        readinessProbe:  # 把livenessProbe换成readinessProbe
         exec:
          command:
           - cat
           - /tmp/health
         initialDelaySeconds: 10 #容器启动10s之后,开始执行Readiness探测
         periodSeconds: 5 #每5秒执行一次Rreadiness探测

5.2 创建 & 观察

# 创建 & 观察 -w:代表实时观察pod状态变更
$ kubectl apply -f readiness-deploy.yaml & kubectl get pod -o wide -w
deployment.apps/readiness-deploy created
NAME                               READY   STATUS    RESTARTS    AGE   
...
# READY:不可用
readiness-deploy-c6cb95448-7t9lz   0/1     Running     0          2s   ...
# READY:可用
readiness-deploy-c6cb95448-7t9lz   1/1     Running     0          15s  ...
# READY:不可用
readiness-deploy-c6cb95448-7t9lz   0/1     Running     0          45s  ...

# 查看deploy状态
$ kubectl get deploy
NAME               READY   UP-TO-DATE   AVAILABLE   AGE
readiness-deploy   0/1     1            0           7m15s

# 查看Pod日志
$ kubectl describe pod readiness-deploy-c6cb95448-ltkx9
Name:         readiness-deploy-c6cb95448-ltkx9
...
Events:
  Type     Reason     Age                From               Message
  ----     ------     ----               ----               -------
  ...
  # 这里看到执行readiness探针时报错
  Warning  Unhealthy  1s (x15 over 66s)  kubelet            Readiness probe failed: cat: can't open '/tmp/health': No such file or directory

5.3 Ready状态经历

Pod readinessREADY状态经历了如下变化:

  • 刚被创建时,READY状态为不可用。
  • 15秒后(initialDelaySeconds + periodSeconds),第一次进行Readiness探测并成功返回,设置READY为可用。
  • 30秒后,/tmp/healthy被删除,连续3次Readiness探测均失败后,READY被设置为不可用。

6. Liveness和Readiness 对比

  • Liveness探测和Readiness探测是两种Health Check机制,如果不特意配置,Kubernetes将对两种探测采取相同的默认行为,即通过判断容器启动进程的返回值是否为零来判断探测是否成功.

  • 两种探测的配置方法完全一样,支持的配置参数也一样。不同之处在于探测失败后的行为

  • Liveness探测: 重启容器;

  • Readiness探测: 将容器设置为不可用,不接收Service转发的请求。

  • Liveness探测和Readiness探测是独立执行的,二者之间没有依赖,所以可以单独使用,也可以同时使用。

  • Liveness探测判断容器是否需要重启以实现自愈;

  • Readiness探测判断容器是否已经准备好对外提供服务。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 215,245评论 6 497
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,749评论 3 391
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 160,960评论 0 350
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,575评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,668评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,670评论 1 294
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,664评论 3 415
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,422评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,864评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,178评论 2 331
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,340评论 1 344
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,015评论 5 340
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,646评论 3 323
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,265评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,494评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,261评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,206评论 2 352

推荐阅读更多精彩内容