強(qiáng)大的自愈能力是 Kubernetes 這類容器編排引擎的一個(gè)重要特性。自愈的默認(rèn)實(shí)現(xiàn)方式是自動(dòng)重啟發(fā)生故障的容器。除此之外,用戶還可以利用 Liveness
和 Readiness
探測(cè)機(jī)制設(shè)置更精細(xì)的健康檢查,進(jìn)而實(shí)現(xiàn)如下需求:
10年積累的網(wǎng)站建設(shè)、成都做網(wǎng)站經(jīng)驗(yàn),可以快速應(yīng)對(duì)客戶對(duì)網(wǎng)站的新想法和需求。提供各種問(wèn)題對(duì)應(yīng)的解決方案。讓選擇我們的客戶得到更好、更有力的網(wǎng)絡(luò)服務(wù)。我雖然不認(rèn)識(shí)你,你也不認(rèn)識(shí)我。但先網(wǎng)站設(shè)計(jì)后付款的網(wǎng)站建設(shè)流程,更有廣德免費(fèi)網(wǎng)站建設(shè)讓你可以放心的選擇與我們合作。
Liveness 探測(cè)讓用戶可以自定義判斷容器是否健康的條件。如果探測(cè)失敗,Kubernetes 就會(huì)重啟容器。
我們創(chuàng)建一個(gè) Pod 的配置文件liveness.yaml
,可以使用命令kubectl explain pod.spec.containers.livenessProbe
查看其使用方法。
apiVersion: v1
kind: Pod
metadata:
name: liveness
labels:
test: liveness
spec:
restartPolicy: OnFailure
containers:
- name: liveness
image: busybox
args:
- /bin/sh
- -c
- touch /tmp/healthy; sleep 30; rm -rf /tmp/healthy; sleep 600
livenessProbe:
exec:
command:
- cat
- /tmp/healthy
initialDelaySeconds: 10
periodSeconds: 5
啟動(dòng)進(jìn)程首先創(chuàng)建文件 /tmp/healthy,30 秒后刪除,在我們的設(shè)定中,如果 /tmp/healthy 文件存在,則認(rèn)為容器處于正常狀態(tài),反正則發(fā)生故障。
livenessProbe
部分定義如何執(zhí)行 Liveness 探測(cè):
cat
命令檢查 /tmp/healthy 文件是否存在。如果命令執(zhí)行成功,返回值為零,Kubernetes 則認(rèn)為本次 Liveness 探測(cè)成功;如果命令返回值非零,本次 Liveness 探測(cè)失敗。initialDelaySeconds: 10
指定容器啟動(dòng) 10 之后開始執(zhí)行 Liveness 探測(cè),我們一般會(huì)根據(jù)應(yīng)用啟動(dòng)的準(zhǔn)備時(shí)間來(lái)設(shè)置。比如某個(gè)應(yīng)用正常啟動(dòng)要花 30 秒,那么initialDelaySeconds
的值就應(yīng)該大于 30。periodSeconds: 5
指定每 5 秒執(zhí)行一次 Liveness 探測(cè)。Kubernetes 如果連續(xù)執(zhí)行 3 次 Liveness 探測(cè)均失敗,則會(huì)殺掉并重啟容器。下面創(chuàng)建 Pod liveness:
[root@master ~]# kubectl apply -f liveness.yaml
pod/liveness created
從配置文件可知,最開始的 30 秒,/tmp/healthy
存在,cat 命令返回 0,Liveness 探測(cè)成功,這段時(shí)間 kubectl describe pod liveness
的 Events部分會(huì)顯示正常的日志。
[root@master ~]# kubectl describe pod liveness
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal Pulling 25s kubelet, node02 pulling image "busybox"
Normal Pulled 24s kubelet, node02 Successfully pulled image "busybox"
Normal Created 24s kubelet, node02 Created container
Normal Started 23s kubelet, node02 Started container
Normal Scheduled 23s default-scheduler Successfully assigned default/liveness to node02
35 秒之后,日志會(huì)顯示 /tmp/healthy
已經(jīng)不存在,Liveness 探測(cè)失敗。再過(guò)幾十秒,幾次探測(cè)都失敗后,容器會(huì)被重啟。
[root@master ~]# kubectl describe pod liveness
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal Scheduled 6m9s default-scheduler Successfully assigned default/liveness to node02
Normal Pulled 3m41s (x3 over 6m10s) kubelet, node02 Successfully pulled image "busybox"
Normal Created 3m41s (x3 over 6m10s) kubelet, node02 Created container
Normal Started 3m40s (x3 over 6m9s) kubelet, node02 Started container
Warning Unhealthy 2m57s (x9 over 5m37s) kubelet, node02 Liveness probe failed: cat: can't open '/tmp/healthy': No such file or directory
Normal Pulling 2m27s (x4 over 6m11s) kubelet, node02 pulling image "busybox"
Normal Killing 60s (x4 over 4m57s) kubelet, node02 Killing container with id docker://liveness:Container failed liveness probe.. Container will be killed and recreated.
然后我們查看容器,已經(jīng)重啟了一次。
[root@master ~]# kubectl get pod
NAME READY STATUS RESTARTS AGE
liveness 1/1 Running 3 5m13s
用戶通過(guò) Liveness 探測(cè)可以告訴 Kubernetes 什么時(shí)候通過(guò)重啟容器實(shí)現(xiàn)自愈;Readiness 探測(cè)則是告訴 Kubernetes 什么時(shí)候可以將容器加入到 Service 負(fù)載均衡池中,對(duì)外提供服務(wù)。
Readiness 探測(cè)的配置語(yǔ)法與 Liveness 探測(cè)完全一樣,我們創(chuàng)建配置文件readiness.yaml
。
apiVersion: v1
kind: Pod
metadata:
name: readiness
labels:
test: readiness
spec:
restartPolicy: OnFailure
containers:
- name: readiness
image: busybox
args:
- /bin/sh
- -c
- touch /tmp/healthy; sleep 30; rm -rf /tmp/healthy; sleep 600
readinessProbe:
exec:
command:
- cat
- /tmp/healthy
initialDelaySeconds: 10
periodSeconds: 5
創(chuàng)建 Pod,然后查看其狀態(tài)。
[root@master ~]# kubectl apply -f readiness.yaml
pod/readiness created
剛剛創(chuàng)建時(shí),READY
狀態(tài)為不可用。
[root@master ~]# kubectl get pod
NAME READY STATUS RESTARTS AGE
readiness 0/1 Running 0 21s
15 秒后(initialDelaySeconds + periodSeconds),第一次進(jìn)行 Readiness 探測(cè)并成功返回,設(shè)置 READY
為可用。
[root@master ~]# kubectl get pod
NAME READY STATUS RESTARTS AGE
readiness 1/1 Running 0 38s
30 秒后,/tmp/healthy 被刪除,連續(xù) 3 次 Readiness 探測(cè)均失敗后,READY
被設(shè)置為不可用。
[root@master ~]# kubectl get pod
NAME READY STATUS RESTARTS AGE
readiness 0/1 Running 0 63s
通過(guò) kubectl describe pod readiness
也可以看到 Readiness 探測(cè)失敗的日志。
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal Pulling 5m29s kubelet, node01 pulling image "busybox"
Normal Scheduled 5m25s default-scheduler Successfully assigned default/readiness to node01
Normal Pulled 5m13s kubelet, node01 Successfully pulled image "busybox"
Normal Created 5m12s kubelet, node01 Created container
Normal Started 5m12s kubelet, node01 Started container
Warning Unhealthy 28s (x51 over 4m38s) kubelet, node01 Readiness probe failed: cat: can't open '/tmp/healthy': No such file or directory
下面對(duì) Liveness 探測(cè)和 Readiness 探測(cè)做個(gè)比較:
對(duì)于多副本應(yīng)用,當(dāng)執(zhí)行 Scale Up 操作時(shí),新副本會(huì)作為 backend 被添加到 Service 的負(fù)責(zé)均衡中,與已有副本一起處理客戶的請(qǐng)求。考慮到應(yīng)用啟動(dòng)通常都需要一個(gè)準(zhǔn)備階段,比如加載緩存數(shù)據(jù),連接數(shù)據(jù)庫(kù)等,從容器啟動(dòng)到正真能夠提供服務(wù)是需要一段時(shí)間的。我們可以通過(guò) Readiness 探測(cè)判斷容器是否就緒,避免將請(qǐng)求發(fā)送到還沒(méi)有 ready 的 backend。
下面我們創(chuàng)建一個(gè)配置文件來(lái)說(shuō)明這種情況。
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
name: web
spec:
replicas: 3
template:
metadata:
labels:
run: web
spec:
containers:
- name: web
images: myhttpd
ports:
- containerPort: 8080
readinessProbe:
httpGet:
scheme: HTTP
path: /health
port: 8080
initialDelaySeconds: 10
periodSeconds: 5
---
apiVersion: v1
kind: Service
metadata:
name: web-svc
spec:
selector:
run: web
ports:
- protocol: TCP
port: 8080
targetPort: 80
重點(diǎn)關(guān)注 readinessProbe
部分。這里我們使用了不同于 exec
的另一種探測(cè)方法 -- httpGet
。Kubernetes 對(duì)于該方法探測(cè)成功的判斷條件是 http 請(qǐng)求的返回代碼在 200-400 之間。
schema
指定協(xié)議,支持 HTTP(默認(rèn)值)和 HTTPS。path
指定訪問(wèn)路徑。port
指定端口。上面配置的作用是:
對(duì)于生產(chǎn)環(huán)境中重要的應(yīng)用都建議配置 Health Check,保證處理客戶請(qǐng)求的容器都是準(zhǔn)備就緒的 Service backend。
Health Check 另一個(gè)重要的應(yīng)用場(chǎng)景是 Rolling Update。試想一下下面的情況,現(xiàn)有一個(gè)正常運(yùn)行的多副本應(yīng)用,接下來(lái)對(duì)應(yīng)用進(jìn)行更新(比如使用更高版本的 image),Kubernetes 會(huì)啟動(dòng)新副本,然后發(fā)生了如下事件:
因?yàn)樾赂北颈旧頉](méi)有異常退出,默認(rèn)的 Health Check 機(jī)制會(huì)認(rèn)為容器已經(jīng)就緒,進(jìn)而會(huì)逐步用新副本替換現(xiàn)有副本,其結(jié)果就是:當(dāng)所有舊副本都被替換后,整個(gè)應(yīng)用將無(wú)法處理請(qǐng)求,無(wú)法對(duì)外提供服務(wù)。如果這是發(fā)生在重要的生產(chǎn)系統(tǒng)上,后果會(huì)非常嚴(yán)重。
如果正確配置了 Health Check,新副本只有通過(guò)了 Readiness 探測(cè),才會(huì)被添加到 Service;如果沒(méi)有通過(guò)探測(cè),現(xiàn)有副本不會(huì)被全部替換,業(yè)務(wù)仍然正常進(jìn)行。
下面通過(guò)例子來(lái)實(shí)踐 Health Check 在 Rolling Update 中的應(yīng)用。
用如下配置文件 app.v1.yml 模擬一個(gè) 10 副本的應(yīng)用:
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
name: app
spec:
replicas: 10
template:
metadata:
labels:
run: app
spec:
containers:
- name: app
images: busybox
args:
- /bin/sh
- -c
- sleep 10; touch /tmp/healthy; sleep 30000
readinessProbe:
exec:
command:
- cat
- /tmp/healthy
initialDelaySeconds: 10
periodSeconds: 5
10 秒后副本能夠通過(guò) Readiness 探測(cè)。
[root@master ~]# kubectl apply -f app.v1.yaml
deployment.extensions/app created
[root@master ~]# kubectl get pods
NAME READY STATUS RESTARTS AGE
app-56878b4676-4rftg 1/1 Running 0 34s
app-56878b4676-6jtn4 1/1 Running 0 34s
app-56878b4676-6smfj 1/1 Running 0 34s
app-56878b4676-8pnc2 1/1 Running 0 34s
app-56878b4676-hxzjk 1/1 Running 0 34s
app-56878b4676-mglht 1/1 Running 0 34s
app-56878b4676-t2qs6 1/1 Running 0 34s
app-56878b4676-vgw44 1/1 Running 0 34s
app-56878b4676-vnxfx 1/1 Running 0 34s
app-56878b4676-wb9rh 1/1 Running 0 34s
接下來(lái)滾動(dòng)更新應(yīng)用,配置文件 app.v2.yml
如下:
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
name: app
spec:
replicas: 10
template:
metadata:
labels:
run: app
spec:
containers:
- name: app
image: busybox
args:
- /bin/sh
- -c
- sleep 30000
readinessProbe:
exec:
command:
- cat
- /tmp/healthy
initialDelaySeconds: 10
periodSeconds: 5
很顯然,由于新副本中不存在 /tmp/healthy,是無(wú)法通過(guò) Readiness 探測(cè)的。驗(yàn)證如下:
[root@master ~]# kubectl apply -f app.v2.yaml
deployment.extensions/app configured
[root@master ~]# kubectl get pod
NAME READY STATUS RESTARTS AGE
app-56878b4676-4rftg 1/1 Running 0 4m42s
app-56878b4676-6jtn4 1/1 Running 0 4m42s
app-56878b4676-6smfj 1/1 Running 0 4m42s
app-56878b4676-hxzjk 1/1 Running 0 4m42s
app-56878b4676-mglht 1/1 Running 0 4m42s
app-56878b4676-t2qs6 1/1 Running 0 4m42s
app-56878b4676-vgw44 1/1 Running 0 4m42s
app-56878b4676-vnxfx 1/1 Running 0 4m42s
app-56878b4676-wb9rh 1/1 Running 0 4m42s
app-84fc656775-hf954 0/1 Running 0 66s
app-84fc656775-p287w 0/1 Running 0 66s
[root@master ~]# kubectl get deploy
NAME READY UP-TO-DATE AVAILABLE AGE
app 9/10 2 9 7m1s
先關(guān)注 kubectl get pod
輸出:
再來(lái)看 kubectl get deployment app
的輸出:
在我們的設(shè)定中,新副本始終都無(wú)法通過(guò) Readiness 探測(cè),所以這個(gè)狀態(tài)會(huì)一直保持下去。
上面我們模擬了一個(gè)滾動(dòng)更新失敗的場(chǎng)景。不過(guò)幸運(yùn)的是:Health Check 幫我們屏蔽了有缺陷的副本,同時(shí)保留了大部分舊副本,業(yè)務(wù)沒(méi)有因更新失敗受到影響。
滾動(dòng)更新可以通過(guò)參數(shù) maxSurge
和 maxUnavailable
來(lái)控制副本替換的數(shù)量。