K8S的pod探针
因为 k8s 中采用大量的异步机制、以及多种对象关系设计上的解耦,当应用实例数 增加/删除、或者应用版本发生变化触发滚动升级时,系统并不能保证应用相关的 service、ingress 配置总是及时能完成刷新。在一些情况下,往往只是新的 Pod 完成自身初始化,系统尚未完成 Endpoint、负载均衡器等外部可达的访问信息刷新,老得 Pod 就立即被删除,最终造成服务短暂的额不可用,这对于生产来说
事件背景
因为 k8s 中采用大量的异步机制、以及多种对象关系设计上的解耦,当应用实例数 增加/删除、或者应用版本发生变化触发滚动升级时,系统并不能保证应用相关的 service、ingress 配置总是及时能完成刷新。在一些情况下,往往只是新的 Pod 完成自身初始化,系统尚未完成 Endpoint、负载均衡器等外部可达的访问信息刷新,老得 Pod 就立即被删除,最终造成服务短暂的额不可用,这对于生产来说是不可接受的,所以 k8s 就加入了一些存活性探针:StartupProbe、LivenessProbe、ReadinessProbe。
技术探索
Pod 常见的状态
- Pending:挂起,我们在请求创建 pod 时,条件不满足,调度没有完成,没有任何一个节点能满足调度条件。已经创建了但是没有适合它运行的节点叫做挂起,这其中也包含集群为容器创建网络,或者下载镜像的过程。
- Running:Pod 内所有的容器都已经被创建,且至少一个容器正在处于运行状态、正在启动状态或者重启状态。
- Succeeded:Pod 中所以容器都执行成功后退出,并且没有处于重启的容器。
- Failed:Pod 中所以容器都已退出,但是至少还有一个容器退出时为失败状态。
- Unknown:未知状态,所谓 pod 是什么状态是 apiserver 和运行在 pod 节点的 kubelet 进行通信获取状态信息的,如果节点之上的 kubelet 本身出故障,那么 apiserver 就连不上 kubelet,得不到信息了,就会看 Unknown
Pod 重启策略
- Always: 只要容器失效退出就重新启动容器。
- OnFailure: 当容器以非正常(异常)退出后才自动重新启动容器。
- Never: 无论容器状态如何,都不重新启动容器。
探针简介
1、kubelet的探测方式
在K8S集群中,为了测试pod是否正常运行,可以通过kubelet定期对pod进行健康检查,从而保证服务正常运行。
kubelet的检查机制:
- exec: 在容器内执行指定命令,当返回码为0表示检测成功。在 Kubernetes 1.20 版本之前,exec 探针会忽略 timeoutSeconds: 探针会无限期地持续运行,甚至可能超过所配置的限期,直到返回结果为止。
- grpc:使用 gRPC 执行一个远程过程调用。 目标应该实现 gRPC健康检查。 如果响应的状态是 “SERVING”,则认为诊断成功。gRPC 探针是一个 alpha 特性,只有在你启用了"GRPCContainerProbe" 特性门控时才能使用。
- httpGet:对容器的IP和URL进行HTTP的GET请求,当响应码为大于等于200且小于400,表示容器正常运行。
- tcpSocket:对容器内的端口或socket进行TCP检查,如果端口成功打开,证明容器正常运行。
探针的返回结果:
- Success(成功):容器通过了诊断。
- Failure(失败):容器未通过诊断。
- Unknown(未知):诊断失败,因此不会采取任何行动。
探针的类型:
livenessProbe(存活探针):判断容器是否正常运行。如果存活探测失败,那么kubelet就会把老的容器删除,然后根据重启策略对容器做操作。如果容器没有存活探针,默认状态为Success。
readinessProbe(就绪探针):判断容器的是否就绪。比如:应用在启动时可能需要加载大量的数据或配置文件,或是启动后要依赖等待外部服务。在这种情况下,既不想杀死应用,也不想给它发送请求。这个时候就需要用到这个探针。
当就绪探测失败,端点控制器将从与 Pod匹配的所有服务的端点列表中删除该 Pod 的 IP 地址(如果是用nodeport映射端口到外网,这个时候就不能访问网站了,但是容器不会被删除),初始延迟之前的就绪态的状态值默认为 Failure。 如果容器不提供就绪态探针,则默认状态为 Success。
startupProbe(启动探针):指示容器中的应用是否已经启动。如果提供了启动探针,则所有其他探针都会被禁用,直到此探针成功为止。如果启动探测失败,kubelet 将删除容器,而容器依其重启策略进行重启。如果容器没有提供启动探测,则默认状态为 Success。
探针主要字段解析:
(initialDelaySeconds: 5):初始化延迟时间,主要是告诉kubelet,容器启动需要的时间,当过了这个时间再进行探测,要不然容器可能还没启动就会删除了。
(periodSeconds: 5):探测周期间隔时间,主要是指定kubelet对容器进行探针的时间周期,也就是第一次探测结束后,等待多少时间后对容器进行探测。默认值为10秒,最小值为1秒。
(timeoutSeconds: 5):单次探测超时时间,指定kubelet对容器探测的最大时间,超过这个时间证明容器探测失败。默认为1秒,最小为1秒。
(successThreshold: 5):探测失败到成功的重试次数,当kubelet对某个容器第一次探测失败后,重新进行探测的次数,比如指定为1,那么就会直接将容器删除。如果使用的探针是livenessProbe,那么只能配置为1,最小值为1次。
(failureThreshold: 5):探测成功到失败的重试次数,当kubelet对某个容器进行探测过程中,允许失败的次数,当用于readinessProbe探针,默认是3次,最小值为1次。也就是说当3次探测失败后,容器会被删除。当用于startupProbe探针,如果还设置了periodSeconds时间,那么等待容器启动的时间为failureThreshold的时间乘以periodSeconds时间的值,在这段时间内,容器没有启动,那么就会删除容器。
2、livenessProbe探针
2.1 livenessProbe和kubelet-exec
首先我们定义一个nginx的deployment:
kubectl apply -f nginx-exec.yaml
pod正常运行以后,我们查下探针里的目录文件是否存在,显然是正常返回的
kubectl exec -it nginx-deployment-5546c765b7-zt6cc -- ls /usr/share/nginx/html/index.html
我们修改index.html为index.html.bak,看看pod的探测无法获取到index.html后,可以看到RESTARTS变成了1,证明容器重启过了,可以看到文件又存在了,证明容器被重建了。
2.2、livenessProbe和kubelet-httpGet
master创建容器
删除文件,这样探针就会被触发,可以看到RESTARTS变成了1,证明容器重启过了
可以看到文件又存在了,证明容器被重建了
2.3、livenessProbe和kubelet-tcpSocket
3、readinessProbe探针
apiVersion: apps/v1
kind: Deployment
metadata:
labels:
app: nginx
name: nginx
namespace: default
spec:
replicas: 1
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx
ports:
- containerPort: 80
readinessProbe: #指定探针
exec: #指定探针探测方式
command:
- cat
- /usr/share/nginx/html/index.html
initialDelaySeconds: 5
periodSeconds: 5
---
apiVersion: v1
kind: Service
metadata:
labels:
svc: nginx-svc-nodeport
name: nginx-svc-nodeport
spec:
type: NodePort
ports:
- port: 80
targetPort: 80
nodePort: 30080
selector:
app: nginx
kubectl get pod -A -o wide 查看容器名字,可以看到原本READY是1/1
kubectl exec -it nginx-677665d5db-fct77 /bin/sh 连接容器
cd /usr/share/nginx/html/ 修改探针指定探测的文件名字,让探针触发
mv index.html index1.html
exit
kubectl get pod -A -o wide 可以看到READY变成了0/1,证明探针被触发了,容器没有被删除,也没有重建
curl -I http://192.168.2.10:30080 可以看到网站访问不了
kubectl describe svc nginx-svc-nodeport 可以看到Endpoints没有值,证明探针把service和pod解除绑定了
3.2、readinessProbe和kubelet-httpGet
[root@k8s-master readliness]# cat readliness-httpGet.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
labels:
app: nginx
name: nginx
namespace: default
spec:
replicas: 1
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx
ports:
- containerPort: 80
readinessProbe: #指定探针
httpGet: #指定探针方式
path: /index.html
port: 80
initialDelaySeconds: 5
periodSeconds: 5
---
apiVersion: v1
kind: Service
metadata:
labels:
svc: nginx-svc-nodeport
name: nginx-svc-nodeport
spec:
type: NodePort
ports:
- port: 80
targetPort: 80
nodePort: 30080
selector:
app: nginx
[root@k8s-master readliness]# kubectl apply -f readliness-httpGet.yaml
deployment.apps/nginx created
service/nginx-svc-nodeport created
[root@k8s-master readliness]# kubectl get pod -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
nginx-5865d7c55c-9p4cv 1/1 Running 0 60s 10.244.2.33 k8s-node2 <none> <none>
[root@k8s-master readliness]# kubectl describe svc nginx-svc-nodeport
Name: nginx-svc-nodeport
Namespace: default
Labels: svc=nginx-svc-nodeport
Annotations: <none>
Selector: app=nginx
Type: NodePort
IP: 10.99.244.94
Port: <unset> 80/TCP
TargetPort: 80/TCP
NodePort: <unset> 30080/TCP
Endpoints: 10.244.2.33:80
Session Affinity: None
External Traffic Policy: Cluster
Events: <none>
[root@k8s-master readliness]# kubectl get pod -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
nginx-5865d7c55c-9p4cv 1/1 Running 0 60s 10.244.2.33 k8s-node2 <none> <none>
[root@k8s-node1 ~]# curl -I http://192.168.2.10:30080 #成功访问svc
HTTP/1.1 200 OK
Server: nginx/1.23.2
Date: Mon, 07 Nov 2022 13:56:35 GMT
Content-Type: text/html
Content-Length: 615
Last-Modified: Wed, 19 Oct 2022 07:56:21 GMT
Connection: keep-alive
ETag: "634fada5-267"
Accept-Ranges: bytes
[root@k8s-master readliness]# kubectl exec -it nginx-5865d7c55c-9p4cv bash
kubectl exec [POD] [COMMAND] is DEPRECATED and will be removed in a future version. Use kubectl exec [POD] -- [COMMAND] instead.
root@nginx-5865d7c55c-9p4cv:/# cd /usr/share/nginx/html/
root@nginx-5865d7c55c-9p4cv:/usr/share/nginx/html# mv index.html index.html1
root@nginx-5865d7c55c-9p4cv:/usr/share/nginx/html# exit
exit
[root@k8s-master readliness]# #移动index.html出发探针失败
#宿主机已经无法访问,流量无法进入
[root@k8s-node1 ~]# curl -I http://192.168.2.10:30080
HTTP/1.1 403 Forbidden
Server: nginx/1.23.2
Date: Mon, 07 Nov 2022 13:57:45 GMT
Content-Type: text/html
Content-Length: 153
Connection: keep-alive
[root@k8s-master readliness]# kubectl describe svc nginx-svc-nodeport
Name: nginx-svc-nodeport
Namespace: default
Labels: svc=nginx-svc-nodeport
Annotations: <none>
Selector: app=nginx
Type: NodePort
IP: 10.99.244.94
Port: <unset> 80/TCP
TargetPort: 80/TCP
NodePort: <unset> 30080/TCP
Endpoints: #service没有与pod进行绑定
Session Affinity: None
External Traffic Policy: Cluster
Events: <none>
# pod虽然处于running状态,但不ready,不会作为service的endpoint提供服务
[root@k8s-master readliness]# kubectl get pod
NAME READY STATUS RESTARTS AGE
nginx-5865d7c55c-9p4cv 0/1 Running 0 6m8s
3.3、readinessProbe和kubelet-tcpSocket
[root@k8s-master readliness]# cat readliness-tcp.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
labels:
app: nginx
name: nginx
namespace: default
spec:
replicas: 1
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx
ports:
- containerPort: 80
readinessProbe: #指定探针
tcpSocket: #指定探测方式
port: 80
initialDelaySeconds: 5
periodSeconds: 5
---
apiVersion: v1
kind: Service
metadata:
labels:
svc: nginx-svc-nodeport
name: nginx-svc-nodeport
spec:
type: NodePort
ports:
- port: 80
targetPort: 80
nodePort: 30080
selector:
app: nginx
[root@k8s-master readliness]# kubectl apply -f readliness-tcp.yaml
deployment.apps/nginx created
service/nginx-svc-nodeport created
[root@k8s-master readliness]# kubectl get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 18d
nginx-svc-nodeport NodePort 10.99.24.165 <none> 80:30080/TCP 17s
[root@k8s-master readliness]# kubectl get pod
NAME READY STATUS RESTARTS AGE
nginx-7464f54778-tbwtf 1/1 Running 0 39s
[root@k8s-master readliness]# kubectl describe svc nginx-svc-nodeport
Name: nginx-svc-nodeport
Namespace: default
Labels: svc=nginx-svc-nodeport
Annotations: <none>
Selector: app=nginx
Type: NodePort
IP: 10.99.24.165
Port: <unset> 80/TCP
TargetPort: 80/TCP
NodePort: <unset> 30080/TCP
Endpoints: 10.244.2.34:80
Session Affinity: None
External Traffic Policy: Cluster
Events: <none>
#########svc可以正常访问
[root@k8s-node1 ~]# curl -I http://192.168.2.10:30080
HTTP/1.1 200 OK
Server: nginx/1.23.2
Date: Mon, 07 Nov 2022 14:05:53 GMT
Content-Type: text/html
Content-Length: 615
Last-Modified: Wed, 19 Oct 2022 07:56:21 GMT
Connection: keep-alive
ETag: "634fada5-267"
Accept-Ranges: bytes
[root@k8s-master readliness]# kubectl exec -it nginx-7464f54778-tbwtf bash
kubectl exec [POD] [COMMAND] is DEPRECATED and will be removed in a future version. Use kubectl exec [POD] -- [COMMAND] instead.
root@nginx-7464f54778-tbwtf:/# sed -i 's/80/8000/g' /etc/nginx/conf.d/default.conf
root@nginx-7464f54778-tbwtf:/# nginx -s reload
2022/11/07 14:07:29 [notice] 37#37: signal process started
root@nginx-7464f54778-tbwtf:/#
[root@k8s-master readliness]# kubectl describe svc nginx-svc-nodeport
Name: nginx-svc-nodeport
Namespace: default
Labels: svc=nginx-svc-nodeport
Annotations: <none>
Selector: app=nginx
Type: NodePort
IP: 10.99.24.165
Port: <unset> 80/TCP
TargetPort: 80/TCP
NodePort: <unset> 30080/TCP
Endpoints:
Session Affinity: None
External Traffic Policy: Cluster
Events: <none>
[root@k8s-node1 ~]# curl -I http://192.168.2.10:30080
curl: (7) Failed connect to 192.168.2.10:30080; Connection refused
更多推荐
所有评论(0)