一:需求来源:
首先来看一下,整个需求的来源:当把应用迁移到 Kubernetes 之后,要如何去保障应用的健康与稳定呢?其实很简单,可以从两个方面来进行增强:
1,首先是提高应用的可观测性;
2,第二是提高应用的可恢复能力。
从可观测性上来讲,可以在三个方面来去做增强:
1,首先是应用的健康状态上面,可以实时地进行观测;
2,第二个是可以获取应用的资源使用情况;
3,第三个是可以拿到应用的实时日志,进行问题的诊断与分析。
当出现了问题之后,首先要做的事情是要降低影响的范围,进行问题的调试与诊断。最后当出现问题的时候,理想的状况是:可以通过和 K8s 集成的自愈机制进行完整的恢复。
二:介绍两种探测方式:livenessProbe和ReadnessProbe
- livenessProbe:[活跃度探测],就是根据用户自定义的规则判断容器是否健康。也叫存活指针,如果 Liveness 指针判断容器不健康,此时会通过 kubelet 杀掉相应的 pod,并根据重启策略来判断是否重启这个容器。如果默认不配置 Liveness 指针,则默认情况下认为它这个探测默认返回是成功的。
- ReadnessProbe:[敏捷探测],用来判断这个容器是否启动完成,即 pod 的 状态(期望值)是否 为ready。如果探测的一个结果是不成功,那么此时它会从 pod 上 Endpoint 上移除,也就是说从接入层上面会把前一个 pod 进行摘除(设置pod为不可用的状态),直到下一次判断成功,这个 pod 才会再次挂到相应的 endpoint 之上。
什么是Endpoint?
Endpoint是k8s集群中的一个资源对象,存储在etcd中,用来记录一个service对应的所有pod的访问地址。
2,Liveness和Readness两种探测机制的使用场景:
Liveness 指针适用场景是支持那些可以重新拉起的应用,而 Readiness 指针主要应对的是启动之后无法立即对外提供服务的这些应用。
3,,Liveness和Readness两种探测机制的相同点和不同点:
相同点是根据探测pod内某个应用或文件,来检查pod的健康状况,不同点是liveness如果探测失败会重启pod,而readliness则在连续3次探测失败之后,会将pod设置为不可用的状态,并不会重启pod。
4,Liveness 指针和 Readiness 指针支持三种不同的探测方式:
- 1,第一种是 httpGet。它是通过发送 http Get 请求来进行判断的,当返回码是 200-399 之间的状态码时,标识这个应用是健康的;
- 2,第二种探测方式是 Exec。它是通过执行容器中的一个命令来判断当前的服务是否是正常的,当命令行的返回结果是 0,则标识容器是健康的;
- 3,第三种探测方式是 tcpSocket 。它是通过探测容器的 IP 和 Port 进行 TCP 健康检查,如果这个 TCP 的链接能够正常被建立,那么标识当前这个容器是健康的。
第一种探测方式和第三种非常相似,一般常用的是第一和第二种的探测方式。
三,探测机制应用实例:
1,LivenessProbe:
方法1:使用exec探测方式,查看pod内某个指定的文件是否存在,如果存在则认为状态为健康的,否则会根据设置的重启重启策略重启pod。
###pod的配置文件:
[[email protected] yaml]# vim livenss.yaml
kind: Pod
apiVersion: v1
metadata:
name: liveness
labels:
name: liveness
spec:
restartPolicy: OnFailure ##定义重启策略,仅在pod对象出现错误时才重启
containers:
- name: liveness
image: busybox
args:
- /bin/sh
- -c
- touch /tmp/test; sleep 30; rm -rf /tmp/test; sleep 300 #创建文件,并且在30秒后将文件进行删除
livenessProbe: #执行活跃度探测
exec:
command:
- cat #探测/tmp目录下是有test文件,如果有则代表健康,如果没有则执行重启pod策略。
- /tmp/test
initialDelaySeconds: 10 #当容器运行多久之后开始探测(单位是s)
periodSeconds: 5 #探测频率(单位s),每隔5秒探测一次。
探测机制中其他的可选字段:
- initialDelaySeconds:容器启动后第一次执行探测是需要等待多少秒。
- periodSeconds:执行探测的频率。默认是10秒,最小1秒。
- timeoutSeconds:探测超时时间。默认1秒,最小1秒。
- successThreshold:探测失败后,最少连续探测成功多少次才被认定为成功。默认是1。对于liveness必须是1。最小值是1。
- failureThreshold:探测成功后,最少连续探测失败多少次才被认定为失败。默认是3。最小值是1。
//运行该pod进行测试:
[[email protected] yaml]# kubectl apply -f livenss.yaml
pod/liveness created
//监测pod的状态:
会在容器启动10秒后开始探测,且每5s探测一次。
我们可以看到pod一直在重启中,从上图看到RESTARTS
的次数已经为7次了,原因是在启动pod时执行了该命令:
/bin/sh -c "touch /tmp/test; sleep 30; rm -rf /tmp/test; sleep 300"
在容器生命的最初30秒内有一个 /tmp/test 文件,在这30秒内 cat /tmp/test命令会返回一个成功的返回码。但30秒后, cat /tmp/test 将返回失败的返回码,会触发pod的重启策略。
//我们来查看一下pod的Events信息:
[[email protected] ~]# kubectl describe pod liveness
从上面的事件中可以发现,探测失败,将重启容器,原因是在指定的目录下没有发现该文件。
方法2:使用httpGet探测方式,运行一个web服务,探测web网页的根目录下是否有指定的文件,也就等同于 “curl -I 容器ip地址:/healthy”。(这里的目录/,指定的是容器内提供web服务的主目录。)
//pod的yaml文件:
[[email protected] yaml]# vim http-livenss.yaml
apiVersion: v1
kind: Pod
metadata:
name: web
labels:
name: mynginx
spec:
restartPolicy: OnFailure #定义pod重启策略
containers:
- name: nginx
image: nginx
ports:
- containerPort: 80
livenessProbe: #定义探测机制
httpGet: #探测方式为httpGet
scheme: HTTP #指定协议
path: /healthy #指定路径下的文件,如果不存在,探测失败
port: 80
initialDelaySeconds: 10 #当容器运行多久之后开始探测(单位是s)
periodSeconds: 5 #探测频率(单位s),每隔5秒探测一次
---
apiVersion: v1 #关联一个service对象
kind: Service
metadata:
name: web-svc
spec:
selector:
name: mynginx
ports:
- protocol: TCP
port: 80
targetPort: 80
httpGet探测方式有如下可选的控制字段:
- host:连接的主机名,默认连接到pod的IP。你可能想在http header中设置”Host”而不是使用IP。
- scheme:连接使用的schema,默认HTTP。
- path: 访问的HTTP server的path。
- httpHeaders:自定义请求的header。HTTP运行重复的header。
- port:访问的容器的端口名字或者端口号。端口号必须介于1和65525之间。
//运行该pod:
[[email protected] yaml]# kubectl apply -f http-livenss.yaml
pod/web created
service/web-svc created
##查看pod运行10秒前的情况:
##最开始的10秒该容器是活着的,且返回的状态码为200.
###10秒后当探测机制开始探测时再次查看pod的情况:
//查看的pod的events:
[[email protected] yaml]# kubectl describe pod web
可以看到返回的状态码为404,表示在网页的根目录下并没有找到指定的文件,表示探测失败,且重启4次,状态是completed(完成的状态),说明pod是存在问题的。
2)接下来我们继续进行检测,使其最终探测成功:
修改pod的配置文件:
[[email protected] yaml]# vim http-livenss.yaml
//重新运行pod:
[[email protected] yaml]# kubectl delete -f http-livenss.yaml
pod "web" deleted
service "web-svc" deleted
[[email protected] yaml]# kubectl apply -f http-livenss.yaml
pod/web created
service/web-svc created
//最终我们查看pod的状态及Events信息:
[[email protected] yaml]# kubectl get pod -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
web 1/1 Running 0 5s 10.244.1.11 node01 <none> <none>
[[email protected] yaml]# kubectl describe pod web
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal Scheduled 71s default-scheduler Successfully assigned default/web to node01
Normal Pulling 71s kubelet, node01 Pulling image "nginx"
Normal Pulled 70s kubelet, node01 Successfully pulled image "nginx"
Normal Created 70s kubelet, node01 Created container nginx
Normal Started 70s kubelet, node01 Started container nginx
可以看到pod的状态时正常运行的。
##测试访问网页头部信息:
[[email protected] yaml]# curl -I 10.244.1.11
返回的状态码为200,表示这个pod的状况时健康的。
ReadnessProbe探测:
方法1:使用exec探测方式,与iveness相同,探测某个文件是否存在。
//pod的配置文件如下:
[[email protected] yaml]# vim readiness.yaml
kind: Pod
apiVersion: v1
metadata:
name: readiness
labels:
name: readiness
spec:
restartPolicy: OnFailure
containers:
- name: readiness
image: busybox
args:
- /bin/sh
- -c
- touch /tmp/test; sleep 30; rm -rf /tmp/test; sleep 300;
readinessProbe: #定义readiness探测方式
exec:
command:
- cat
- /tmp/test
initialDelaySeconds: 10
periodSeconds: 5
//运行该pod:
[[email protected] yaml]# kubectl apply -f readiness.yaml
pod/readiness created
//检测pod的状态:
//查看pod的Events:
[[email protected] yaml]# kubectl describe pod readiness
可以看到找不到该文件,表示探测失败,但是readiness机制与liveness机制不同,它并不会重启pod,而是连续探测3次失败后,则将容器设置为不可用的状态。
方法二:httpGet方式。
[[email protected] yaml]# vim http-readiness.yaml
apiVersion: v1
kind: Pod
metadata:
name: web2
labels:
name: web2
spec:
containers:
- name: web2
image: nginx
ports:
- containerPort: 81
readinessProbe:
httpGet:
scheme: HTTP #指定协议
path: /healthy #指定路径,如果不存在,则需要进行创建,否则探测失败
port: 81
initialDelaySeconds: 10
periodSeconds: 5
---
apiVersion: v1
kind: Service
metadata:
name: web-svc
spec:
selector:
name: web2
ports:
- protocol: TCP
port: 81
targetPort: 81
//运行pod:
[[email protected] yaml]# kubectl apply -f http-readiness.yaml
pod/web2 created
service/web-svc created
//查看pod的状态:
[[email protected] yaml]# kubectl get pod -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
readiness 0/1 Completed 0 37m 10.244.2.12 node02 <none> <none>
web 1/1 Running 0 50m 10.244.1.11 node01 <none> <none>
web2 0/1 Running 0 2m31s 10.244.1.14 node01 <none> <none>
查看pod的Events信息,通过探测,可以知道pod是不健康的,且http访问失败。
它并不会重启,而是直接将pod设置为不可用的状态。
健康检测在滚动更新过程中的应用:
首先我们通过explain工具来查看更新使用的字段:
[[email protected] ~]# kubectl explain deploy.spec.strategy.rollingUpdate
可以看到在滚动更新的过程中有两个参数可用:
- maxSurge:此参数控制滚动更新过程中,副本总数超过预期pod数量的上限。可以是百分比,也可以是具体的值,默认为1。如果该值设置为3,则在更新的过程中,一来则会直接增加三个pod进行更新(当然还要进行探测机制的验证是否更新成功)。该值设置得越大、升级速度越快,但会消耗更多的系统资源。
- maxUnavailable:此参数控制滚动更新过程中,不可用的pod的数量,注意是在原pod数量中进行减少,不会计算maxSurge值的范围。若当该值为3时,如果探测失败则在升级的过程中会有3个pod不可用。该值设置得越大、升级速度越快,但会消耗更多的系统资源。
maxSurge和maxUnavailable的适用场景:
1,如果您希望在保证系统可用性和稳定性的前提下尽可能快地进行升级,可以将 maxUnavailable 设置为 0,同时为 maxSurge 赋予一个较大值。
2,如果系统资源比较紧张,pod 负载又比较低,为了加快升级速度,可以将 maxSurge 设置为 0,同时为 maxUnavailable 赋予一个较大值。需要注意的是,如果 maxSurge 为 0maxUnavailable 为 DESIRED,可能造成整个服务的不可用,此时 RollingUpdate 将退化成停机发布
1)首先我们创建一个deployment资源对象:
[[email protected] ~]# vim app.v1.yaml
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
name: my-web
spec:
replicas: 10 #定义副本数量为10个
template:
metadata:
labels:
name: my-web
spec:
containers:
- name: my-web
image: nginx
args:
- /bin/sh
- -c
- touch /usr/share/nginx/html/test.html; sleep 300000; #创建文件,使其在探测时保持pod为健康状态
ports:
- containerPort: 80
readinessProbe: #使用readiness机制
exec:
command:
- cat
- /usr/share/nginx/html/test.html
initialDelaySeconds: 10
periodSeconds: 10
//运行该pod后,查看pod数量(10个):
[[email protected] yaml]# kubectl get pod -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
my-web-7bbd55db99-2g6tp 1/1 Running 0 2m11s 10.244.2.44 node02 <none> <none>
my-web-7bbd55db99-2jdbz 1/1 Running 0 118s 10.244.2.45 node02 <none> <none>
my-web-7bbd55db99-5mhcv 1/1 Running 0 2m53s 10.244.1.40 node01 <none> <none>
my-web-7bbd55db99-77b4v 1/1 Running 0 2m 10.244.1.44 node01 <none> <none>
my-web-7bbd55db99-h888n 1/1 Running 0 2m53s 10.244.2.41 node02 <none> <none>
my-web-7bbd55db99-j5tgz 1/1 Running 0 2m38s 10.244.2.42 node02 <none> <none>
my-web-7bbd55db99-kjgm2 1/1 Running 0 2m25s 10.244.1.42 node01 <none> <none>
my-web-7bbd55db99-kkmh2 1/1 Running 0 2m38s 10.244.1.41 node01 <none> <none>
my-web-7bbd55db99-lr896 1/1 Running 0 2m13s 10.244.1.43 node01 <none> <none>
my-web-7bbd55db99-rpd8v 1/1 Running 0 2m23s 10.244.2.43 node02 <none>
探测成功,10个副本全部运行。
2)第一次更新:
更新nginx镜像版本,且设置滚动更新策略:
[[email protected] yaml]# vim app.v1.yaml
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
name: my-web
spec:
strategy: #设置滚动更新策略,通过该字段下的rollingUpdate的子属性来设置
rollingUpdate:
maxSurge: 3 #指定在滚动更新过程中最多可创建3个额外的 pod
maxUnavailable: 3 #- 指定在滚动更新过程中最多允许3 pod 不可用
replicas: 10
template:
metadata:
labels:
name: my-web
spec:
containers:
- name: my-web
image: 172.16.1.30:5000/nginx:v2.0 #更新的镜像为私有仓库中的镜像nginx:v2.0
args:
- /bin/sh
- -c
- touch /usr/share/nginx/html/test.html; sleep 300000;
ports:
- containerPort: 80
readinessProbe:
exec:
command:
- cat
- /usr/share/nginx/html/test.html
initialDelaySeconds: 10
periodSeconds: 10
//执行yaml文件后,查看pod数量:
[[email protected] yaml]# kubectl get pod -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
my-web-7db8b88b94-468zv 1/1 Running 0 3m38s 10.244.2.57 node02 <none> <none>
my-web-7db8b88b94-bvszs 1/1 Running 0 3m24s 10.244.1.60 node01 <none> <none>
my-web-7db8b88b94-c4xvv 1/1 Running 0 3m38s 10.244.2.55 node02 <none> <none>
my-web-7db8b88b94-d5fvc 1/1 Running 0 3m38s 10.244.1.58 node01 <none> <none>
my-web-7db8b88b94-lw6nh 1/1 Running 0 3m21s 10.244.2.59 node02 <none> <none>
my-web-7db8b88b94-m9gbh 1/1 Running 0 3m38s 10.244.1.57 node01 <none> <none>
my-web-7db8b88b94-q5dqc 1/1 Running 0 3m38s 10.244.1.59 node01 <none> <none>
my-web-7db8b88b94-tsbmm 1/1 Running 0 3m38s 10.244.2.56 node02 <none> <none>
my-web-7db8b88b94-v5q2s 1/1 Running 0 3m21s 10.244.1.61 node01 <none> <none>
my-web-7db8b88b94-wlgwb 1/1 Running 0 3m25s 10.244.2.58 node02 <none> <none>
//查看pod的版本信息:
[[email protected] yaml]# kubectl get deployments. -o wide
NAME READY UP-TO-DATE AVAILABLE AGE CONTAINERS IMAGES SELECTOR
my-web 10/10 10 10 49m my-web 172.16.1.30:5000/nginx:v2.0 name=my-web
探测成功,10个pod版本全部更新成功。
3)第二次更新:
将镜像版本更新为3.0版本,且设置滚动更新策略。(探测失败)
pod的配置文件如下:
[[email protected] yaml]# vim app.v1.yaml
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
name: my-web
spec:
strategy:
rollingUpdate:
maxSurge: 3 #定义更新策略,数量依然都是保持3个
maxUnavailable: 3
replicas: 10 #pod数量依然是10个
template:
metadata:
labels:
name: my-web
spec:
containers:
- name: my-web
image: 172.16.1.30:5000/nginx:v3.0 #测试镜像版本更新为3.0
args:
- /bin/sh
- -c
- sleep 300000; #不在创建指定文件,使其探测失败
ports:
- containerPort: 80
readinessProbe:
exec:
command:
- cat
- /usr/share/nginx/html/test.html
initialDelaySeconds: 10
periodSeconds: 5
//重新运行pod配置文件:
[[email protected] yaml]# kubectl apply -f app.v1.yaml
deployment.extensions/my-web configured
//查看pod更新后的数量:
[[email protected] yaml]# kubectl get pod -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
my-web-7db8b88b94-468zv 1/1 Running 0 12m 10.244.2.57 node02 <none> <none>
my-web-7db8b88b94-c4xvv 1/1 Running 0 12m 10.244.2.55 node02 <none> <none>
my-web-7db8b88b94-d5fvc 1/1 Running 0 12m 10.244.1.58 node01 <none> <none>
my-web-7db8b88b94-m9gbh 1/1 Running 0 12m 10.244.1.57 node01 <none> <none>
my-web-7db8b88b94-q5dqc 1/1 Running 0 12m 10.244.1.59 node01 <none> <none>
my-web-7db8b88b94-tsbmm 1/1 Running 0 12m 10.244.2.56 node02 <none> <none>
my-web-7db8b88b94-wlgwb 1/1 Running 0 12m 10.244.2.58 node02 <none> <none>
my-web-849cc47979-2g59w 0/1 Running 0 3m9s 10.244.1.63 node01 <none> <none>
my-web-849cc47979-2lkb6 0/1 Running 0 3m9s 10.244.1.64 node01 <none> <none>
my-web-849cc47979-762vb 0/1 Running 0 3m9s 10.244.1.62 node01 <none> <none>
my-web-849cc47979-dv7x8 0/1 Running 0 3m9s 10.244.2.61 node02 <none> <none>
my-web-849cc47979-j6nwz 0/1 Running 0 3m9s 10.244.2.60 node02 <none> <none>
my-web-849cc47979-v5h7h 0/1 Running 0 3m9s 10.244.2.62 node02 <none> <none>
我们可以看到当前pod的总数量为13个,(包括maxSurge增加额外的数量)因为探测失败,则设置为将3个pod(包括额外的pod)为不可用的状态,但还剩下7个pod可用( 因为maxUnavailable设置为3个),但注意:这7个pod的版本并没有更新成功,还是上一个的版本。
//查看pod的更新后的版本信息:
[[email protected] yaml]# kubectl get deployments. -o wide
NAME READY UP-TO-DATE AVAILABLE AGE CONTAINERS IMAGES SELECTOR
my-web 7/10 6 7 58m my-web 172.16.1.30:5000/nginx:v3.0 name=my-web
参数解释:
READY :表示用户的期望值
UP-TO-DATE:表示已更新的
AVAILABLE:表示可用的
我们可以发现已更新的镜像版本数量为6个(包括额外的3个pod),但是为不可用的状态,但是确保可用的的pod数量为7个,但是版本并没有更新。
总结:
描述在滚动更新过程中,探测机制有什么作用?
如果在公司中需要对某个应用中的pod进行更新操作,如果没有探测机制,无论该pod是不是当你已经做好更新工作准备要进行更新的,它都会将该应用中所有的pod进行更新,这样会造成严重的后果,虽然更新后你发现pod的状态是正常的,为了达到controller manager的期望值,READY的值依然是1/1, 但是pod已经是重新生成的pod了,表示pod内的数据将会全部丢失。
如果加上探测机制的话,会探测容器内你指定的文件或其他应用是否存在,如果达到你指定的条件,则探测成功,则会对你的pod进行更新,如果探测失败则会设置pod(容器)为不可用,虽然探测失败的容器不可用了,但至少该模块中还有其他之前版本的pod可用,确保该公司该服务的正常运行。可见探测机制是多么的重要啊。
———————— 本文至此结束,感谢阅读 ————————
原文地址:https://blog.51cto.com/13972012/2456952