在默认情况下,Kubernetes启动Pod成功后,便认为它是可用的,Service会将Pod加入服务,流量就会进入到启动的Pod中。

这其实会造成一些服务不可靠的情况,比如:

  1. 当我们用Deployment滚动升级的过程中,会遇到服务短暂性不可用,然后又恢复了。一个web服务,可能会出现502 Bad Gateway的一瞬间。
  2. 当一个Pod运行了一段时间后,应用程序由于各种原因已经处于不可用状态,需要重启,然而Pod还存活着,Kubernetes依旧认为它是可用的。
  3. 应用程序并不能成功启动,导致Pod在启动后又挂掉,陷入一个不断循环的过程。

可以看出,一个服务可不可用,并不能单纯地使用是否启动成功作为判断,对此,k8s有Pod专门的探针来检测。

探针的类型

探针有两种,LivenessReadiness,通过官网的介绍,我们明确得到:

  • Liveness: 让kubelet知道什么时候重启容器。比如,可以解决当一个应用程序虽然处于运行状态,实际却无法提供服务的情况。
  • Readniess:让kubelet知道容器是否已经准备好接受流量。Service的负载均衡会使用这个状态来决定是否将Pod作为服务的后端,或者什么时候将其剔除。

如何配置探针

首先我们要先理解好两种探针的区别,清楚我们的目的是什么,想要达到什么样的效果,然后配置对应的探针。
两种探针在配置上是一样的,只是配置的名字不同而已,我们这里只列出Liveness怎么配置。

因为Pod可以配置多个Container,所以探针的具体配置是在每个Container之中。
探针有三种试探方式:

  • exec:执行命令
  • httpGet:使用HTTP请求
  • tcpSocket:使用TCP检测

下面三个探针方式的配置例子来自k8s官网。

用命令来检测

exec是通过执行命令来检测,命令执行成功时返回0,则认为探测成功。
下面的配置是用exec作为探测的例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
apiVersion: v1
kind: Pod
metadata:
labels:
test: liveness
name: liveness-exec
spec:
containers:
- name: liveness
image: k8s.gcr.io/busybox
args:
- /bin/sh
- -c
- touch /tmp/healthy; sleep 30; rm -rf /tmp/healthy; sleep 600
livenessProbe:
exec:
command:
- cat
- /tmp/healthy
initialDelaySeconds: 5
periodSeconds: 5

容器启动时,会生成/tmp/healthy文件,然后在30秒后,将文件删除,并且先别退出,等待600秒。
liveness的配置中,initialDelaySeconds指定了启动容器后先等待5秒,然后再执行探针,periodSeconds代表执行探针的频率,这里是5秒一次。

具体的执行过程这里就不展示了,从结果上看:启动后的30秒内,探针的检测都是正常的,在第35秒后,会显示liveness探针失败,容器被删掉并重建。

用HTTP请求检测

还可以用http get请求作为检测:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
apiVersion: v1
kind: Pod
metadata:
labels:
test: liveness
name: liveness-http
spec:
containers:
- name: liveness
image: k8s.gcr.io/liveness
args:
- /server
livenessProbe:
httpGet:
path: /healthz
port: 8080
httpHeaders:
- name: Custom-Header
value: Awesome
initialDelaySeconds: 3
periodSeconds: 3

对于httpGet探针来说,返回状态码在200到400间,就会认为是成功的。状态码没有更改选项
该程序的源码如下:

1
2
3
4
5
6
7
8
9
10
http.HandleFunc("/healthz", func(w http.ResponseWriter, r *http.Request) {
duration := time.Now().Sub(started)
if duration.Seconds() > 10 {
w.WriteHeader(500)
w.Write([]byte(fmt.Sprintf("error: %v", duration.Seconds())))
} else {
w.WriteHeader(200)
w.Write([]byte("ok"))
}
})

代码可以看出,在程序启动的10秒内都是正常的,10秒后,会返回500的状态码,这个时候,探针会失败,容器将被杀掉并重启。

用TCP连接检测

使用TCP进行检测的方式是,只要通过指定的端口,能成功建立socket,则认为是成功的,否则认为失败。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
apiVersion: v1
kind: Pod
metadata:
name: goproxy
labels:
app: goproxy
spec:
containers:
- name: goproxy
image: k8s.gcr.io/goproxy:0.1
ports:
- containerPort: 8080
readinessProbe:
tcpSocket:
port: 8080
initialDelaySeconds: 5
periodSeconds: 10
livenessProbe:
tcpSocket:
port: 8080
initialDelaySeconds: 15
periodSeconds: 20

在这个例子里,同时存在了readinessProbe和livenessProbe,它们都是一样的,只要端口8080能成功建立连接,探针便是成功的。

使用命名端口

这个配置是针对httpGet和tcpSocket探针的,对于上面的port配置,都是配置一个具体端口号,我们也可以通过ContainerPort来配置它。比如:

1
2
3
4
5
6
7
8
9
ports:
- name: liveness-port
containerPort: 8080
hostPort: 8080

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

其他配置参数

从上面的例子我们看到了探针的一些其他配置参数,这里列出探针的所有具体配置项:

配置项 描述
initialDelaySeconds 探针在容器启动后的多少秒才开始执行
periodSeconds 探针执行频率。默认是10秒,最小1秒。
timeoutSeconds 探测超时时间。默认1秒,最小1秒。
successThreshold 探测失败后,最少连续探测成功多少次才被认定为成功。默认是1。对于liveness必须是1。最小值是1。
failureThreshold 探测成功后,最少连续探测失败多少次才被认定为失败。默认是3。最小值是1。

HTTP probe 中可以给 httpGet设置其他配置项:

配置项 描述
host 连接的主机名,默认连接到pod的IP。你可能想在http header中设置”Host”而不是使用IP。
scheme 连接使用的schema,默认HTTP。
path 访问的HTTP server的path。
httpHeaders 自定义请求的header。HTTP运行重复的header。
port 访问的容器的端口名字或者端口号。端口号必须介于1和65535之间。

总结

到这里,探针的相关内容都已经列出来了。我们要明确两种探针对应着什么样的效果:

  • Liveness: 让kubelet知道什么时候重启容器。比如,可以解决当一个应用程序虽然处于运行状态,实际却无法提供服务的情况。
  • Readniess:让kubelet知道容器是否已经准备好接受流量。Service的负载均衡会使用这个状态来决定是否将Pod作为服务的后端,或者什么时候将其剔除。

让它们相互之间配合,让我们的服务更加可靠。

参考

Configure Liveness and Readiness Probes
配置Pod的liveness和readiness探针