𝑰’𝒎 𝒉𝒉𝒈, 𝑰 𝒂𝒎 𝒂 𝒈𝒓𝒂𝒅𝒖𝒂𝒕𝒆 𝒔𝒕𝒖𝒅𝒆𝒏𝒕 𝒇𝒓𝒐𝒎 𝑵𝒂𝒏𝒋𝒊𝒏𝒈, 𝑪𝒉𝒊𝒏𝒂.

  • 🏫 𝑺𝒉𝒄𝒐𝒐𝒍: 𝑯𝒐𝒉𝒂𝒊 𝑼𝒏𝒊𝒗𝒆𝒓𝒔𝒊𝒕𝒚
  • 🌱 𝑳𝒆𝒂𝒓𝒏𝒊𝒏𝒈: 𝑰’𝒎 𝒄𝒖𝒓𝒓𝒆𝒏𝒕𝒍𝒚 𝒍𝒆𝒂𝒓𝒏𝒊𝒏𝒈 𝒅𝒆𝒔𝒊𝒈𝒏 𝒑𝒂𝒕𝒕𝒆𝒓𝒏, 𝑳𝒆𝒆𝒕𝒄𝒐𝒅𝒆, 𝒅𝒊𝒔𝒕𝒓𝒊𝒃𝒖𝒕𝒆𝒅 𝒔𝒚𝒔𝒕𝒆𝒎, 𝒎𝒊𝒅𝒅𝒍𝒆𝒘𝒂𝒓𝒆 𝒂𝒏𝒅 𝒔𝒐 𝒐𝒏.
  • 💓 𝑯𝒐𝒘 𝒕𝒐 𝒓𝒆𝒂𝒄𝒉 𝒎𝒆:𝑽𝑿
  • 📚 𝑴𝒚 𝒃𝒍𝒐𝒈: 𝒉𝒕𝒕𝒑𝒔://𝒉𝒉𝒈𝒚𝒚𝒅𝒔.𝒃𝒍𝒐𝒈.𝒄𝒔𝒅𝒏.𝒏𝒆𝒕/
  • 💼 𝑷𝒓𝒐𝒇𝒆𝒔𝒔𝒊𝒐𝒏𝒂𝒍 𝒔𝒌𝒊𝒍𝒍𝒔:𝒎𝒚 𝒅𝒓𝒆𝒂𝒎

1 重启策略

Pod的重启策略(RestartPolicy)应用于Pod内的所有容器,并且仅在Pod所处的Node上由kubelet进行判断和重启操作。当某个容器异常退出或者健康检查(详见下节)失败时,kubelet将根据RestartPolicy的设置来进行相应的操作。

1.1 三种策略

Pod的重启策略包括Always、OnFailure和Never,默认值为Always。

  • Always:当容器失效时,由kubelet自动重启该容器。(默认是always)
  • OnFailure:当容器终止运行且退出码不为0时,由kubelet自动重启该容器。
  • Never:不论容器运行状态如何,kubelet都不会重启该容器。

  看上去always 和OnFailure 没有太大的区别,但是其实always的范围比OnFailure 要大,因为always 不管以什么方式退出,都会重启,即使是正常退出也会重启。

  kubelet重启失效容器的时间间隔以sync-frequency乘以2n来计算,例如1、2、4、8倍等,最长延时5min,并且在成功重启后的10min后重置该时间。

1.2 Pod状态转换图

在这里插入图片描述
主要就是pod里面有两个以上的容器时候要注意下,核心原因就是pod是pod,pod里面有好些容器,它不止管理一个。

2 健康检查

2.1 探针的作用

2.1.1 LivenessProbe

   kubelet 使用存活探测器来确定什么时候要重启容器。 例如,存活探测器可以探测到应用死锁(应用程序在运行,但是无法继续执行后面的步骤)情况。 重启这种状态下的容器有助于提高应用的可用性,即使其中存在缺陷。

   说的通透一点就是,当程序里面出现情况,比如无限死锁挂起,程序并没有挂掉,还在运行,但是已经无法响应了,这个时候就可以用这个probe,让系统重新启动。

探测失败的结果是:如果存活探测失败,则 kubelet 会杀死容器,并且容器将受到其 重启策略 的影响。

2.1.2 ReadinessProbe

   kubelet 使用就绪探测器可以知道容器何时准备好接受请求流量,当一个 Pod 内的所有容器都就绪时(也就是服务可用),才能认为该 Pod 就绪。 这种信号的一个用途就是控制哪个 Pod 作为 Service 的后端。 若 Pod 尚未就绪,会被从 Service 的负载均衡器中剔除。

   说的通透一点就是,你的应用程序需要一些时间来预热,或从GitHub等某个外部源下载应用程序内容。除非完全准备好,否则你的应用程序不会接受流量。 默认情况下,一旦容器内的进程启动,Kubernetes就会开始发送流量。使用就绪探针,Kubernetes会等到应用程序完全启动后,才允许服务将流量发送到新副本。

探测失败的结果是:如果就绪探测失败,端点控制器将从与 Pod 匹配的所有 Service 的端点中删除该 Pod 的 IP 地址。

2.1.3 StartupProbe

   kubelet 使用启动探测器来了解应用容器何时启动。 如果配置了这类探测器,你就可以控制容器在启动成功后再进行存活性和就绪态检查, 确保这些存活、就绪探测器不会影响应用的启动。 启动探测器可以用于对慢启动容器进行存活性检测,避免它们在启动运行之前就被杀掉。

   启动检查机制,应用一些启动缓慢的业务,避免业务长时间启动而被上面两类探针kill掉,这个问题也可以换另一种方式解决,就是定义上面两类探针机制时,初始化时间定义的长一些即可。

2.1.4 迷惑点

   最让人迷惑的就是ReadinessProbe 和 StartupProbe,这两个probe,他们看上去似乎是一样的,但是,实际上还是ReadinessProbe 管的是,这个能不能提供服务,从启动,到运行,到就绪,这三个阶段都不能有问题。StartupProbe是管什么,它只管pod启动就行。

2.2 探针的三种实现方式

  • ExecAction:在容器内执行指定命令。如果命令退出时返回码为0则认为诊断成功
  • TCPSocketAction:对指定端口上的容器的IP地址进行TCP检查。如果端口打开,则诊断被认为是成功的。
  • HTTPGetAction:对指定的端口和路径上的容器的IP地址执行HTTPGet请求。如果响应的状态码大于等于200且小于400【2xx:成功,3xx:跳转】,则诊断被认为是成功的。

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

  • success:容器通过了诊断。
  • failed:容器未通过诊断。
  • unknown:诊断失败,因此不会采取任何行动【这将导致容器挂死,因为探针不执行成功的话,容器会一直在等待

Probe 有很多配置字段,可以使用这些字段精确地控制活跃和就绪检测的行为:

2.3 最佳实践-LivenessProbe

2.3.1 定义ExecAction

  • exec-liveness.yaml
apiVersion: v1
kind: Pod
metadata:
  labels:
    test: liveness
  name: liveness-exec
spec:
  containers:
  - name: liveness
    image: registry.cn-hangzhou.aliyuncs.com/google_containers/busybox
    args:
    - /bin/s
    - -c
    - touch /tmp/healthy; sleep 30; rm -f /tmp/healthy; sleep 600
    livenessProbe:
      exec:
        command:
        - cat
        - /tmp/healthy
      initialDelaySeconds: 5                                       # kubelet 在执行第一次探测前应该等待 5 秒
      periodSeconds: 5                                             # kubelet 应该每 5 秒执行一次存活探测

  这个配置文件中,可以看到 Pod 中只有一个 Container。 periodSeconds 字段指定了 kubelet 应该每 5 秒执行一次存活探测。 initialDelaySeconds 字段告诉 kubelet 在执行第一次探测前应该等待 5 秒。 kubelet 在容器内执行命令 cat /tmp/healthy 来进行探测。 如果命令执行成功并且返回值为 0,kubelet 就会认为这个容器是健康存活的。 如果这个命令返回非 0 值,kubelet 会杀死这个容器并重新启动它。

根据配置文件,在容器启动的时候,会执行如下的命令:

/bin/sh -c "touch /tmp/healthy; sleep 30; rm -f /tmp/healthy; sleep 600"

  这个容器生命的前 30 秒,/tmp/healthy 文件是存在的。 所以在这最开始的 30 秒内,执行命令 cat /tmp/healthy 会返回成功代码。 30 秒之后,执行命令 cat /tmp/healthy 就会返回失败代码。

在30s内,我们可以查看pod的events。

Events:
  Type     Reason     Age              From               Message
  ----     ------     ----             ----               -------
  Normal   Scheduled  7s               default-scheduler  Successfully assigned default/liveness-exec to k8s-node22
  Normal   Pulled     6s               kubelet            Successfully pulled image "registry.cn-hangzhou.aliyuncs.com/g02.620088ms
  Normal   Pulling    5s (x2 over 6s)  kubelet            Pulling image "registry.cn-hangzhou.aliyuncs.com/google_contai
  Normal   Created    4s (x2 over 6s)  kubelet            Created container liveness
  Warning  Failed     4s (x2 over 5s)  kubelet            Error: failed to start container "liveness": Error response frim task: OCI runtime create failed: runc create failed: unable to start container process: exec: "/bin/s": stat /bin/s: nown
  Normal   Pulled     4s               kubelet            Successfully pulled image "registry.cn-hangzhou.aliyuncs.com/g22.360606ms
  Warning  BackOff    2s               kubelet            Back-off restarting failed container

  35秒之后,再次查看,有信息显示存活探测器失败了(因为我们人为的把那个文件删除了,所以导致探针命令执行失败),这个失败的容器被杀死并且被重建了。

2.3.2 定义HTTPGetAction

apiVersion: v1
kind: Pod
metadata:
  labels:
    test: liveness
  name: liveness-http
spec:
  containers:
  - name: liveness
    image: registry.cn-hangzhou.aliyuncs.com/google_containers/liveness
    args:
    - /server
    livenessProbe:
      httpGet:
        path: /healthz
        port: 8080
        httpHeaders:
        - name: Custom-Header
          value: Awesome
      initialDelaySeconds: 3
      periodSeconds: 3

  在这个配置文件中,你可以看到 Pod 也只有一个容器。 periodSeconds 字段指定了 kubelet 每隔 3 秒执行一次存活探测。 initialDelaySeconds 字段告诉 kubelet 在执行第一次探测前应该等待 3 秒。 kubelet 会向容器内运行的服务(服务在监听 8080 端口)发送一个 HTTP GET 请求来执行探测。 如果服务器上 /healthz 路径下的处理程序返回成功代码,则 kubelet 认为容器是健康存活的。 如果处理程序返回失败代码,则 kubelet 会杀死这个容器并将其重启。

   容器存活期间的最开始 10 秒中,/healthz 处理程序返回 200 的状态码。 之后处理程序返回 500 的状态码。然后对比10秒前和10秒后,就可以知道了。

PS:这个例子跟着官网走的,官网的镜像,我拉不下来,配过代理了,但是意思都是一样的。
这边重要的是记录,记录下来知道这个东西的存在就好了。

  找到了解决办法: 我通过docker search了一下,找到了这个镜像,并且修改了我的yaml文件,实现了这个实验。

[root@k8s-master ~]# docker search liveness
NAME                              DESCRIPTION                                     STARS     OFFICIAL   AUTOMATED
carlziess/liveness                liveness                                        1                    [OK]
cnych/liveness                                                                    1                    
objectscale/livenessprobe                                                         0                    
openebs/liveness-mongodb                                                          0                    
seedoflife/liveness               k8s.gcr.io/liveness                             0                    [OK]
mirrorgooglecontainers/liveness                                                   0                    
yiorgos/liveness                  A small container that you can use for Kuber…   0                    
e2eteam/liveness                                                                  0                    
giantswarm/livenessprobe                                                          0                    
etccoop/liveness                  Node Liveness                                   0                    
tombokombo/liveness                                                               0                    
livenessprobe/base-node                                                           0                    
personalizeai/livenessprobe                                                       0                    
anjia0532/liveness                                                                0                    
umotif/livenessprobe                                                              0                    
claudiubelu/liveness                                                              0                    
brajesh5548/liveness              checking liveness                               0                    
fkconsultin/liveness-testing                                                      0                    
xingxb/livenessprobe               k8s.gcr.io/sig-storage/livenessprobe           0                    
ondrejsika/liveness                                                               0                    
daniledty/liveness-k8s                                                            0                    
gcrxio/liveness                                                                   0                    
ledongthuc/liveness-probe-api     API server supports routes for liveness, rea…   0                    
hugohui/liveness                                                                  0                    
liuyz/liveness                    HTTP liveness probe. Download from gcr,On N…    0       

yml文件

apiVersion: v1
kind: Pod
metadata:
  labels:
    test: liveness
  name: liveness-http
spec:
  containers:
  - name: liveness
    image: seedoflife/liveness
    args:
    - /server
    livenessProbe:
      httpGet:
        path: /healthz
        port: 8080
        httpHeaders:
        - name: Custom-Header
          value: Awesome
      initialDelaySeconds: 3
      periodSeconds: 3

输出结果:

Events:
  Type     Reason     Age                From               Message
  ----     ------     ----               ----               -------
  Normal   Scheduled  45s                default-scheduler  Successfully assigned default/liveness-http to k8s-node22
  Normal   Pulled     28s                kubelet            Successfully pulled image "seedoflife/liveness" in 16.10114506s
  Normal   Created    28s                kubelet            Created container liveness
  Normal   Started    28s                kubelet            Started container liveness
  Normal   Pulling    12s (x2 over 44s)  kubelet            Pulling image "seedoflife/liveness"
  Warning  Unhealthy  12s (x3 over 18s)  kubelet            Liveness probe failed: HTTP probe failed with statuscode: 500
  Normal   Killing    12s                kubelet            Container liveness failed liveness probe, will be restarted

  可以看到12秒的时候http probe failed 因为状态码是500了,500就是error了。

2.3.3 定义TCPSocketAction:

tcp-liveness-readiness.yaml

apiVersion: v1
kind: Pod
metadata:
  name: goproxy
  labels:
    app: goproxy
spec:
  containers:
  - name: goproxy
    image: geray/goproxy:0.1
    ports:
    - containerPort: 8080
    readinessProbe:
      tcpSocket:
        port: 8080
      initialDelaySeconds: 5
      periodSeconds: 10
    livenessProbe:
      tcpSocket:
        port: 8080
      initialDelaySeconds: 15
      periodSeconds: 20

  如你所见,TCP 检测的配置和 HTTP 检测非常相似。 下面这个例子同时使用就绪和存活探测器。kubelet 会在容器启动 5 秒后发送第一个就绪探测。 探测器会尝试连接 goproxy 容器的 8080 端口。 如果探测成功,这个 Pod 会被标记为就绪状态,kubelet 将继续每隔 10 秒运行一次检测。

events:

Events:
  Type    Reason     Age   From               Message
  ----    ------     ----  ----               -------
  Normal  Scheduled  104s  default-scheduler  Successfully assigned default/goproxy to k8s-node22
  Normal  Pulled     104s  kubelet            Container image "geray/goproxy:0.1" already present on machine
  Normal  Created    104s  kubelet            Created container goproxy
  Normal  Started    104s  kubelet            Started container goproxy

查看这个events 似乎是成功了,好像并没有什么事情发生。

2.4 使用命名端口

对于 HTTP 或者 TCP 存活检测可以使用命名的 port。

ports:
- name: liveness-port
  containerPort: 8080
  hostPort: 8080

livenessProbe:
  httpGet:
    path: /healthz
    port: liveness-port

2.5 最佳实践-startupProbe-使用启动探测器保护慢启动容器

  有时候,会有一些现有的应用在启动时需要较长的初始化时间。 要这种情况下,若要不影响对死锁作出快速响应的探测,设置存活探测参数是要技巧的。 技巧就是使用相同的命令来设置启动探测,针对 HTTP 或 TCP 检测,可以通过将 failureThreshold * periodSeconds 参数设置为足够长的时间来应对糟糕情况下的启动时间。

ports:
- name: liveness-port
  containerPort: 8080
  hostPort: 8080

livenessProbe:
  httpGet:
    path: /healthz
    port: liveness-port
  failureThreshold: 1
  periodSeconds: 10

startupProbe:
  httpGet:
    path: /healthz
    port: liveness-port
  failureThreshold: 30
  periodSeconds: 10

  幸亏有启动探测,应用程序将会有最多 5 分钟(30 * 10 = 300s)的时间来完成其启动过程。 一旦启动探测成功一次,存活探测任务就会接管对容器的探测,对容器死锁作出快速响应。 如果启动探测一直没有成功,容器会在 300 秒后被杀死,并且根据 restartPolicy 来 执行进一步处置。(所以说,startupProbe启动探测过了之后才轮到livenessProbe起到作用,这两个probe是有先后顺序的)

2.6 定义readinessProbe

有时候,应用会暂时性地无法为请求提供服务。 例如,应用在启动时可能需要加载大量的数据或配置文件,或是启动后要依赖等待外部服务。 在这种情况下,既不想杀死应用,也不想给它发送请求。 Kubernetes 提供了就绪探测器来发现并缓解这些情况。 容器所在 Pod 上报还未就绪的信息,并且不接受通过 Kubernetes Service 的流量。

  1. 就绪探测器在容器的整个生命周期中保持运行状态。也就是说,并不是就绪了一次之后就不去探测了。
  2. livenessProbe并不会等待readinessProbe成功之后才会启动,这两个是没有先后顺序的,initialDelaySeconds 可以控制他们的先后顺序。

就绪探测器的配置和存活探测器的配置相似。 唯一区别就是要使用 readinessProbe 字段,而不是 livenessProbe 字段。一般两个探针是共同使用的。

2.7 参数配置

Probe 有很多配置字段,可以使用这些字段精确地控制活跃和就绪检测的行为:

  • initialDelaySeconds:容器启动后要等待多少秒后才启动存活和就绪探测器, 默认是 0 秒,最小值是 0。
  • periodSeconds:执行探测的时间间隔(单位是秒)。默认是 10 秒。最小值是 1。
  • timeoutSeconds:探测的超时后等待多少秒。默认值是 1 秒。最小值是 1。
  • successThreshold:探测器在失败后,被视为成功的最小连续成功数。默认值是 1。 存活和启动探测的这个值必须是 1。最小值是 1。
  • failureThreshold:当探测失败时,Kubernetes 的重试次数。 对存活探测而言,放弃就意味着重新启动容器。 对就绪探测而言,放弃意味着 Pod 会被打上未就绪的标签。默认值是 3。最小值是 1。

关于具体的配置可看官方链接,用到的时候再看,学习的时候常见的会就行了。

3 值得考虑的几个问题(难点)

3.1 Liveness 和 Readiness Probes

看上去这两个的配置都差不多啊,那他们的区别呢?
功能上:

  • Liveness探针来决定何时重新启动容器,当出现死锁这些情况时候就会重新构建容器。
    在这里插入图片描述

  • Readiness探针是为了防止容器还没有准备好,例如加载缓存什么的,那需要一定的时间,如果这个时候有请求过来了,必然无法响应,它的作用就是为了让所有的pod准备好,再去接受请求。
    在这里插入图片描述

3.2 关于探针配置的时间

  如果用的是Http的时候,由于接口有的时候负载高,响应的就慢,如果响应慢了,你配置的探针时间太短,就会默认这个容器出问题了,就会引发删除操作。因此支持该服务的所有Pod很可能会同时使“就绪性”探测失败。这将导致所有Pod从服务路由中删除。没有Pod支持该服务,Kubernetes将针对所有对该服务的请求返回HTTP 404(默认后端)。所以探针的超时和重试的次数需要好好斟酌的。所以探针虽然好,但是有的时候还是要根据实际情况好好配的。默认的failThreshold计数为3,即Readiness探针在不再将Pod视为就绪之前探测失败的次数。Readiness探针的频率(由periodSeconds参数确定),您可能需要增加failureThreshold计数。这样做的目的是避免在临时系统故障已经过去并且响应等待时间恢复正常之前,过早地使Readiness探针失败。

3.3 关于探针之间的搭配

  • liveness 一般和readiness 一起用,两者共同使用,可以确保流量不会发给还未就绪的容器,当这些探测失败时容器会被重新启动。
    比如这里的:
        livenessProbe:
          initialDelaySeconds: 30
          periodSeconds: 10
          timeoutSeconds: 5
          successThreshold: 1
          failureThreshold: 3
          exec:
            command: ["mysqladmin", "-uroot", "-p${MYSQL_ROOT_PASSWORD}", "ping"]
        readinessProbe:  
          initialDelaySeconds: 10
          periodSeconds: 10
          timeoutSeconds: 5
          successThreshold: 1
          failureThreshold: 3
          exec:
            command: ["mysqladmin", "-uroot", "-p${MYSQL_ROOT_PASSWORD}", "ping"]

它就可以保证mysql服务是可用的,不然就是重启。

  • liveness 和startup 一起用,可以防止因为慢启动导致的liveness失败原因。

参考文献

遗憾

遗憾的是,每次总结知识点的时候,大部分只有这种小实践,并没有真正的项目级使用的经验在里面,可能这些都是精髓,网上没有搜到相关知识。但是作为刚入门,知道这些应该是足够的了。

Logo

K8S/Kubernetes社区为您提供最前沿的新闻资讯和知识内容

更多推荐