1. kubernetes 健康检查
打造一个完全不出事的容器是不可能的,因此需要对容器进行健康检查,以 check 容器是否处于可用状态。
健康检查可分为进程级健康检查和业务级健康检查两种:
- 进程级健康检查即检查容器进程是否存活,kubelet 定期通过 docker daemon 获取所有 docker 进程的运行情况,如果发现某个容器未正常运行,则重启该容器。
- 业务级健康检查是更细粒度的健康检查,试想容器进程处于死锁状态,此时容器是不可用的,但是容器进程是运行的,kubelet 不会重启该容器。针对这种情况 kubernetes 设计了 probe 探针来检查容器是否可用。
probe 又分两种:活性探针(liveness probe)和业务探针(readiness probe)。
1.1 liveness probe
以 livenss probe 为例,在启动容器时执行一段死锁代码如下:
#coding:utf8
import threading
import time
num = 0
lock = threading.Lock()
def func(n):
lock.acquire()
print n
if(n == 5):
print "deadLock"
raise Exception('deadLock')
lock.release()
if __name__ == "__main__":
t4 = threading.Thread(target=func, args=(5,))
t1 = threading.Thread(target=func, args=(8,))
t2 = threading.Thread(target=func, args=(4,))
t3 = threading.Thread(target=func, args=(2,))
t4.start()
t1.start()
t2.start()
t3.start()
启动包含该死锁代码的容器:
$ docker run -it centos_python2:v1 python2 /home/deadLock.py
5
deadLock here
Exception in thread Thread-1:
Traceback (most recent call last):
File "/usr/lib64/python2.7/threading.py", line 805, in __bootstrap_inner
self.run()
File "/usr/lib64/python2.7/threading.py", line 758, in run
self.__target(*self.__args, **self.__kwargs)
File "/home/deadLock.py", line 13, in func
raise Exception('Error: deadLock ')
Exception: Error: deadLock
此时容器停留在死锁状态,这时候容器是无法正常工作的!
kubernetes 的 liveness probe 通过三种方式对应用进行健康检查:
- HTTP GET
- Container Exec
- TCP Socket
这里仅介绍第二种方式 Container Exec(详细信息看 这里),它通过在容器中检查执行命令的退出码来检查容器应用是否正常,如退出码为 0 则认为容器应用工作正常。如下所示:
[root@chunqiu ~ ]# kubectl exec -it httpd /bin/bash -n ci
bash-5.0$ /usr/bin/genapik8s --is-alive
bash-5.0$ echo $?
0
bash-5.0$ /usr/bin/genapik8s --is-alive2
unknown option: --is-alive2
bash-5.0$ echo $?
3
这里展示了一个容器应用正常工作的容器,使用 genapik8s 检查容器是否可用。对应的 helm chat 如下:
livenessProbe:
exec:
command:
- /usr/bin/genapik8s
- --is-alive
initialDelaySeconds: 15
periodSeconds: 5
successThreshold: 1
failureThreshold: 3
其中,initialDelaySeconds 表示容器启动到执行健康检查的延迟时间,延迟时间的设计是为了容器进程有时间完成必要的初始化工作,而不是在还未启动好之前就被 kubelet 重启了。
同理,将 livenessProbe 应用在前面死锁的容器,容器中执行命令的退出码不为 0,使得 kubelet 发现到该容器无法正常工作,进而重启该容器。
1.2 readiness probe
如果某些容器应用只是暂时性出了问题,而不想 kubelet 对其重启该怎么办呢?kubernetes 提供了 readiness probe 来解决这类问题。类似于 liveness probe,readiness probe 会在容器中执行检查操作,如果检查失败 kubelet 不会重启容器,而是将容器所属的 pod 从 endpoint 列表中删除,这样访问 pod 的请求就会被路由到其它 pod 上。
根据这一特性,可以构造“相同”副本的 pod,一个为 active,一个为 standby。它们都通过 service 建立连接,service 会将请求路由到 active 的 pod,而不会路由到 standby 的 pod。
这里仅以单一副本举例,不深入讨论 active 和 standby pod 的情况。构造带 readiness probe 的 pod:
apiVersion: apps/v1
kind: Deployment
metadata:
name: readinesstester
spec:
replicas: 1
selector:
matchLabels:
app: readinessprobe
template:
metadata:
labels:
app: readinessprobe
spec:
containers:
- name: httpd
image: docker-httpd:0.2.4
readinessProbe:
exec:
command:
- cat
- /tmp/healthy
initialDelaySeconds: 5
periodSeconds: 5
container 内包含一个 readinessProbe 探针,它会执行命令 cat /tmp/healthy 检查容器是否健康。容器中 healthy 是不存在的,执行会失败。创建 service 如下:
apiVersion: v1
kind: Service
metadata:
name: readinessservice
spec:
selector:
app: readinessprobe
ports:
- protocol: TCP
port: 8080
targetPort: 80
检查 pod 状态:
[root@chunqiu readinessProbe ]# kubectl get pods -o wide
NAME READY STATUS RESTARTS AGE IP NODE
readinesstester-85bbbd7947-dzsm6 0/1 Running 0 3h34m 10.10.44.173 chunqiu-node-2
[root@chunqiu readinessProbe ]# kubectl describe pods readinesstester-85bbbd7947-dzsm6
...
Status: Running
Controlled By: ReplicaSet/readinesstester-85bbbd7947
Containers:
httpd:
...
State: Running
Started: Sun, 30 May 2021 11:57:21 +0800
Ready: False
Restart Count: 0
Readiness: exec [cat /tmp/healthy] delay=5s timeout=1s period=5s #success=1 #failure=3
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Warning Unhealthy 3m13s (x2460 over 3h28m) kubelet Readiness probe failed: cat: /tmp/healthy: No such file or directory
这里仅列出需要重点关注的信息。从 pod 状态来看, pod 并未 ready,不过 pod 并未被重启,且 pod 是 running 的。进一步查看 service 对应的 pod endpoint 是否存在:
[root@chunqiu readinessProbe ]# kubectl describe service readinessservice
Name: readinessservice
Namespace: default
Labels: <none>
Annotations: <none>
Selector: app=readinessprobe
Type: ClusterIP
IP: 10.254.194.117
Port: <unset> 8080/TCP
TargetPort: 80/TCP
Endpoints:
Session Affinity: None
Events: <none>
可以看到未 ready 的 pod endpoints 并未放到 service 中,虽然 pod 有自己的 ip。
2. Deployment 和 replicaSet
这一节我们这么部署 pod 和 service 看看会发生什么。
首先创建不带 readinessProbe 的 pod:
[root@chunqiu readinessProbe ]# kubectl get pods -o wide
NAME READY STATUS RESTARTS AGE IP NODE
readinesstester-cd586b86d-b4n7t 1/1 Running 0 26s 10.10.44.179 chunqiu-node-2
[root@chunqiu readinessProbe ]# kubectl describe service readinessservice
Name: readinessservice
Namespace: default
Labels: <none>
Annotations: <none>
Selector: app=readinessprobe
Type: ClusterIP
IP: 10.254.194.117
Port: <unset> 8080/TCP
TargetPort: 80/TCP
Endpoints: 10.10.44.179:80
Session Affinity: None
Events: <none>
很正常,接着将 readinessProbe 加到 pod 内,重新配置 pod,查看 pod 状态:
[root@chunqiu readinessProbe ]# kubectl get pods -o wide
NAME READY STATUS RESTARTS AGE IP NODE
readinesstester-85bbbd7947-xj9gv 0/1 Running 0 29s 10.10.44.161 chunqiu-node-2
readinesstester-cd586b86d-b4n7t 1/1 Running 0 3m51s 10.10.44.179 chunqiu-node-2
发现有两个名为 readinesstester... 的 pod 存在,且状态还不一致,这是为什么呢?
自上而下通过 Deployment 和 replicatSet 查看 pod 干了什么:
[root@chunqiu readinessProbe ]# kubectl get deployments.apps
NAME READY UP-TO-DATE AVAILABLE AGE
readinesstester 1/1 1 1 5m39s
[root@chunqiu readinessProbe ]# kubectl describe deployments.apps readinesstester
...
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal ScalingReplicaSet 5m48s deployment-controller Scaled up replica set readinesstester-cd586b86d to 1
Normal ScalingReplicaSet 2m26s deployment-controller Scaled up replica set readinesstester-85bbbd7947 to 1
根据 Events 可以看到 deployment-controller scale up 一个 replicaSet readinesstester-85bbbd7947,由这个 replicaSet 接管新的带 readinessProbe pod 的创建:
[root@chunqiu readinessProbe ]# kubectl get replicasets.apps
NAME DESIRED CURRENT READY AGE
readinesstester-85bbbd7947 1 1 0 9m13s
readinesstester-cd586b86d 1 1 1 12m
[root@chunqiu readinessProbe ]# kubectl describe replicasets.apps readinesstester-85bbbd7947
Name: readinesstester-85bbbd7947
Replicas: 1 current / 1 desired
Pods Status: 1 Running / 0 Waiting / 0 Succeeded / 0 Failed
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal SuccessfulCreate 9m24s replicaset-controller Created pod: readinesstester-85bbbd7947-xj9gv
...
scale up 的 replicaSet 创建出 Pod,该 pod 的状态并未 ready,导致 deployment 的滚动更新策略一直卡在等待新 replicaSet 创建完成这里,因此会出现两个并行的 pod。从这个示例也可以看出 Deployment 是为了应用的更新而设计的。
芝兰生于空谷,不以无人而不芳。