一、健康檢查
Pod中容器的聲明周期的兩個(gè)鉤子函數(shù), PostStart 與 PreStop,其中, PostStart 是在容器創(chuàng)建后立即執(zhí)行的,而 preStop 這個(gè)鉤子函數(shù)則是在容器終止之前執(zhí)行的,除了上面兩個(gè)鉤子函數(shù)之外,還有一項(xiàng)配置會(huì)影響到容器的生命周期,那就是健康檢查的探針。
在 <code>Kubernetes</code> 集群中,我們可以通過配置 <code>liveness probe(存活探針)</code>和 <code>readingess probe(可讀性探針)</code>來影響容器的生存周期
* kubelet 通過使用 liveness probe 來確定你的應(yīng)用程序
是否正在運(yùn)行,通俗點(diǎn)將就是是否還活著。一般來說,如果你的程序
一旦崩潰了, Kubernetes 就會(huì)立刻知道這個(gè)程序已經(jīng)終止了
,然后就會(huì)重啟這個(gè)程序。而我們的 liveness probe 的目的就
是來捕獲到當(dāng)前應(yīng)用程序還沒有終止,還沒有崩潰,如果出現(xiàn)了
這些情況,那么就重啟處于該狀態(tài)下的容器,使應(yīng)用程序在存在
bug 的情況下依然能夠繼續(xù)運(yùn)行下去。
* kubelet 使用 readiness probe 來確定容器是否已經(jīng)就緒可
以接收流量過來了。這個(gè)探針通俗點(diǎn)講就是說是否準(zhǔn)備好了,現(xiàn)
在可以開始工作了。只有當(dāng) Pod 中的容器都處于就緒狀態(tài)的時(shí)候
kubelet 才會(huì)認(rèn)定該 Pod 處于就緒狀態(tài),因?yàn)橐粋€(gè) Pod 下面
可能會(huì)有多個(gè)容器。當(dāng)然 Pod 如果處于非就緒狀態(tài),那么我們
就會(huì)將他從我們的工作隊(duì)列(實(shí)際上就是我們后面需要重點(diǎn)學(xué)習(xí)的
Service)中移除出來,這樣我們的流量就不會(huì)被路由到這個(gè) Pod 里面來了。
和前面的鉤子函數(shù)一樣的,我們這兩個(gè)探針的支持兩種配置方式:
* exec:執(zhí)行一段命令
* http:檢測(cè)某個(gè) http 請(qǐng)求
* tcpSocket:使用此配置, kubelet 將嘗試在指定端口上打開容器的套接字。如果可以建立連接,容器被認(rèn)為是健康的,如果不能就認(rèn)為是失敗的。實(shí)際上就是檢查端口
接下來為大家演示下存活探針的使用方法,首先我們用 <code>exec</code> 執(zhí)行命令的方式來檢測(cè)容器的存活,如下:
apiVersion: v1
kind: Pod
metadata:
name: liveness-exec
labels:
test: liveness
spec:
containers: //容器
- name: liveness //容器名字
image: busybox //鏡像
args: //args
- /bin/sh
- -c
- touch /tmp/healthy; sleep 30; rm -rf /tmp/healthy; sleep 600
livenessProbe: //存活檢查
exec:
command: //命令
- cat
- /tmp/healthy
initialDelaySeconds: 5 // 延遲探測(cè)時(shí)間
periodSeconds: 5 // 執(zhí)行探測(cè)頻率
這里我們需要用到一個(gè)新的屬性:<code>livenessProbe</code> ,下面通過<code>exec</code> 執(zhí)行一段命令,其中 <code>periodSeconds</code> 屬性表示讓 kubelet 每隔 5 秒執(zhí)行一次存活探針,也就是每 5秒執(zhí)行一次上面的 <code>cat/tmp/healthy</code> 命令,如果命令執(zhí)行成功了,則返回0,那么 kubelet 就會(huì)認(rèn)為當(dāng)前這個(gè)容器是存活的并且被監(jiān)控,如果返回的是非0,那么kubelet 就會(huì)把該容器殺掉然后重啟它,另外一個(gè)屬性 <code>initialDelaySeconds</code> 表示在第一次執(zhí)行探針的時(shí)候要等待 5 秒,這樣能夠確保我們的容器能夠有足夠的時(shí)間來啟動(dòng)起來。我們可以想象一下,如果你得第一次執(zhí)行探針等候的時(shí)間太短,是不是容器可能還沒正常啟動(dòng)起來,所以存活探針很可能始終都是失敗的,這樣就無限的重啟下去,所以一個(gè)合理的 <code>initiaDelaySeconds</code>非常重要。
另外我們?cè)谌萜鲉?dòng)的時(shí)候執(zhí)行了如下命令:
/bin/sh -c "touch /tmp/healthy; sleep 30; rm -rf /tmp/healthy; sleep 600"
意思就是說在容器最開始的30秒內(nèi)有一個(gè) <code>/tmp/healthy</code>文件,在這30秒內(nèi)執(zhí)行 <code>cat/tmp/heathy</code>命令都會(huì)返回一個(gè)成功的返回碼。30秒后,我們刪除這個(gè)文件,現(xiàn)在執(zhí)行 <code>cat/tmp/heathy</code> 是不是就會(huì)失敗了,這個(gè)時(shí)候就會(huì)重啟容器了。
我們創(chuàng)建下該 pod , 在30秒內(nèi),查看 pod 的 Event:
? ~ kubectl describe pod liveness-exec
我們可以觀察到容器是正常啟動(dòng)的,在隔一會(huì),比如40秒之后,再查看 pod 的 Event ,在最下面有一條信息顯示 <code>liveness probe</code> 失敗了,容器被刪掉并重新創(chuàng)建。
然后通過 kubelet get pod liveness-exec 可以看到 RESTARTS 值加 1 了。
同樣的,我們還可以使用 <code>HTTP GET </code> 請(qǐng)求來配置我們的存活探針,我們這里使用一個(gè) liveness 鏡像來驗(yàn)證演示如下 :
apiVersion: v1
kind: Pod
metadata:
labels:
test: liveness
name: liveness-http
spec:
containers:
- name: liveness
image: cnych/liveness
args:
- /server
livenessProbe:
httpGet:
path: /healthz //路徑
port: 8080 //端口
httpHeaders:
- name: X-Custom-Header //headers 名稱
value: Awesome //headers value
initialDelaySeconds: 3 // 延遲探測(cè)時(shí)間
periodSeconds: 3 // 執(zhí)行探測(cè)頻率
同樣的,根據(jù) periodSeconds 屬性我們可以知道 kubelet 需要每隔 3 秒執(zhí)行一次 liveness probe , 該探針將向容器中的 server 的8080端口發(fā)送一個(gè) HTTP GET 請(qǐng)求,如果 server 的 /heathyz 路徑的 handler 返回一個(gè)成功的返回碼,kubelet 就會(huì)認(rèn)定該容器是活著的并且很健康,如果返回失敗的返回碼, kubelet 將殺掉該容器并重啟它。 initiaDelaySeconds 指定 kubelet 在該執(zhí)行第一次探測(cè)之前需要等待3秒鐘。
通常來說,任何大于 200 小于 400 的返回碼都會(huì)認(rèn)定是成功的返回碼。其他返回碼都會(huì)被認(rèn)為是失敗的返回碼。
我們可以來查看下面的 healthy 的實(shí)現(xiàn):
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秒返回的狀態(tài)碼是 200 ,10s 過后就返回 500 的 status_code 了。所以當(dāng)容器啟動(dòng)3s 后,kubelet 開始執(zhí)行健康檢查。第一次檢測(cè)會(huì)成功,因?yàn)槭窃?10s 之內(nèi),但是 10 秒之后,健康檢查將失敗,因?yàn)楝F(xiàn)在返回的是一個(gè)錯(cuò)誤的狀態(tài)碼了,所以 kubelet 將會(huì)殺掉和重啟容器,
同意的我們來創(chuàng)建該P(yáng)od 測(cè)試下效果, 10 秒后,查看 Pod的 Event, 確認(rèn) liveness probe 失敗并重啟了容器。
? ~ kubectl describe pod liveness-http
然后我們來通過端口的方式來配置存活探針,使用此配置, kubelet 將嘗試在指定端口上打開容器的套接字。如果可以建立連接,容器被認(rèn)為是健康的,如果不能就是失敗的。
apiVersion: v1
kind: Pod
metadata:
name: goproxy
labels:
app: goproxy
spec:
containers:
- name: goproxy
image: cnych/goproxy
ports:
- containerPort: 8080
readinessProbe:
tcpSocket:
port: 8080
initialDelaySeconds: 5
periodSeconds: 10
livenessProbe:
tcpSocket:
port: 8080
initialDelaySeconds: 15
periodSeconds: 20
我們可以看到,TCP 檢查的配置與 HTTP 檢查非常相似,只是將httpGet替換成了tcpSocket。 而且我們同時(shí)使用了readiness probe和liveness probe兩種探針。 容器啟動(dòng)后5秒后,kubelet將發(fā)送第一個(gè)readiness probe(可讀性探針)。 該探針會(huì)去連接容器的8080端,如果連接成功,則該 Pod 將被標(biāo)記為就緒狀態(tài)。然后Kubelet將每隔10秒鐘執(zhí)行一次該檢查。
除了readiness probe之外,該配置還包括liveness probe。 容器啟動(dòng)15秒后,kubelet將運(yùn)行第一個(gè) liveness probe。 就像readiness probe一樣,這將嘗試去連接到容器的8080端口。如果liveness probe失敗,容器將重新啟動(dòng)。
有的時(shí)候,應(yīng)用程序可能暫時(shí)無法對(duì)外提供服務(wù),例如,應(yīng)用程序可能需要在啟動(dòng)期間加載大量數(shù)據(jù)或配置文件。 在這種情況下,您不想殺死應(yīng)用程序,也不想對(duì)外提供服務(wù)。 那么這個(gè)時(shí)候我們就可以使用readiness probe來檢測(cè)和減輕這些情況。 Pod中的容器可以報(bào)告自己還沒有準(zhǔn)備,不能處理Kubernetes服務(wù)發(fā)送過來的流量。
從上面的YAML文件我們可以看出readiness probe的配置跟liveness probe很像,基本上一致的。唯一的不同是使用readinessProbe而不是livenessProbe。兩者如果同時(shí)使用的話就可以確保流量不會(huì)到達(dá)還未準(zhǔn)備好的容器,準(zhǔn)備好過后,如果應(yīng)用程序出現(xiàn)了錯(cuò)誤,則會(huì)重新啟動(dòng)容器。
另外除了上面的initialDelaySeconds和periodSeconds屬性外,探針還可以配置如下幾個(gè)參數(shù):
* timeoutSeconds:探測(cè)超時(shí)時(shí)間,默認(rèn)1秒,最小1秒。
* successThreshold:探測(cè)失敗后,最少連續(xù)探測(cè)成功多少次才被認(rèn)定為成功。默認(rèn)是 1,但是如果是`liveness`則必須是 1。最小值是 1。
* failureThreshold:探測(cè)成功后,最少連續(xù)探測(cè)失敗多少次才被認(rèn)定為失敗。默認(rèn)是 3,最小值是 1。
這就是liveness probe(存活探針)和readiness probe(可讀性探針)的使用方法。在Pod的生命周期當(dāng)中,我們已經(jīng)學(xué)習(xí)了容器生命周期中的鉤子函數(shù)和探針檢測(cè),下節(jié)課給大家講解Pod層面生命周期的一個(gè)階段:初始化容器。