
概述
健康檢查(Health Check)用于檢測(cè)您的應(yīng)用實(shí)例是否正常工作,是保障業(yè)務(wù)可用性的一種傳統(tǒng)機(jī)制,一般用于負(fù)載均衡下的業(yè)務(wù),如果實(shí)例的狀態(tài)不符合預(yù)期,將會(huì)把該實(shí)例“摘除”,不承擔(dān)業(yè)務(wù)流量。
Kubernetes中的健康檢查使用存活性探針(liveness probes)和就緒性探針(readiness probes)來(lái)實(shí)現(xiàn),service即為負(fù)載均衡,k8s保證 service 后面的 pod 都可用,是k8s中自愈能力的主要手段,基于這兩種探測(cè)機(jī)制,可以實(shí)現(xiàn)如下需求:
- 異常實(shí)例自動(dòng)剔除,并重啟新實(shí)例
- 多種類(lèi)型探針檢測(cè),保證異常pod不接入流量
- 不停機(jī)部署,更安全的滾動(dòng)升級(jí)
目前支持的探測(cè)方式包括:
- HTTP
- TCP
- Exec命令
k8s 中的 示例配置如下:
探針類(lèi)型
默認(rèn)機(jī)制:
如果把 k8s 對(duì) pod 的crash 狀態(tài)判斷也能稱(chēng)之為“健康檢查”的話,那算是默認(rèn)的健康檢查機(jī)制了,
每個(gè)容器啟動(dòng)時(shí)都會(huì)執(zhí)行一個(gè)主進(jìn)程,如果進(jìn)程退出返回碼不是0,則認(rèn)為容器異常,即pod異常,k8s 會(huì)根據(jù)restartPolicy策略選擇是否殺掉 pod,再重新啟動(dòng)一個(gè)。
restartPolicy分為三種:
- Always:當(dāng)容器終止退出后,總是重啟容器,默認(rèn)策略。
- Onfailure:當(dāng)容器異常退出(退出碼非0)時(shí),才重啟容器。
- Never:當(dāng)容器終止退出時(shí),才不重啟容器。
存活探針
上面的默認(rèn)機(jī)制中,容器進(jìn)程返回值非0則認(rèn)為容器發(fā)生故障,需要重啟。但很多情況下服務(wù)出現(xiàn)問(wèn)題,進(jìn)程卻沒(méi)有退出,如系統(tǒng)超載 5xx 錯(cuò)誤,資源死鎖等。這種情況下就需要健康檢查機(jī)制出場(chǎng)了
存活探針(Liveness probe):讓Kubernetes知道你的應(yīng)用程序是否健康,如果你的應(yīng)用程序不健康,Kubernetes將刪除Pod并啟動(dòng)一個(gè)新的替換它。這里的“健康”不再是進(jìn)程狀態(tài),而是用戶(hù)自定義探測(cè)方式:HTTP、TCP、Exec
舉例說(shuō)明:
apiVersion: v1
kind: Pod
metadata:
labels:
test: liveness
name: liveness
spec:
restartPolicy: OnFailure
containers:
- name: liveness
image: busybox
args:
- /bin/sh
- -c
- touch /tmp/healthy; sleep 30; rm -fr /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è):
探測(cè)的方法是:通過(guò) cat 命令檢查 /tmp/healthy 文件是否存在。如果命令執(zhí)行成功,返回值為0,Kubernetes 則認(rèn)為本次 Liveness 探測(cè)成功;如果命令返回值非0,本次 Liveness 探測(cè)失敗。
initialDelaySeconds: 10,指定容器啟動(dòng) 10s 之后開(kāi)始執(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ì)殺掉并重啟容器。3次是可以配置的,參數(shù)為failureThreshold,含義后面解釋
使用上面的 yaml 創(chuàng)建 pod:
剛開(kāi)始的 30s,健康檢查能通過(guò)。
此時(shí)的events 顯示正常
30s 后,日志會(huì)顯示 /tmp/healthy 已經(jīng)不存在,Liveness 探測(cè)失敗。再過(guò)幾十秒,幾次探測(cè)都失敗后,容器會(huì)被重啟。events 中可以看到重試 了 3次探測(cè),每次間隔 10s,單次探測(cè)的超時(shí)時(shí)間為 1s。
liveness 的配置來(lái)自 v1中的Probe資源,所有屬性含義如下:
- httpGet:對(duì)應(yīng)HTTPGetAction對(duì)象,屬性包括:host、httpHeaders、path、port、scheme
- initialDelaySeconds:容器啟動(dòng)后開(kāi)始探測(cè)之前需要等多少秒,如應(yīng)用啟動(dòng)一般30s的話,就設(shè)置為 30s
- periodSeconds:執(zhí)行探測(cè)的頻率(多少秒執(zhí)行一次)。默認(rèn)為10秒。最小值為1。
- successThreshold:探針失敗后,最少連續(xù)成功多少次才視為成功。默認(rèn)值為1。最小值為1。
- failureThreshold:最少連續(xù)多少次失敗才視為失敗。默認(rèn)值為3。最小值為1。
- timeoutSeconds:探測(cè)的超時(shí)時(shí)間,默認(rèn) 1s,最小 1s
- tcpSocket:對(duì)應(yīng)TCPSocketAction對(duì)象,TCPSocket指定端口。尚不支持TCP hook
- exec:對(duì)應(yīng)ExecAction對(duì)象,需要執(zhí)行的內(nèi)容
動(dòng)圖說(shuō)明:
探針執(zhí)行方式
HTTP
HTTP探針可能是最常見(jiàn)的自定義Liveness探針類(lèi)型。 即使您的應(yīng)用程序不是HTTP服務(wù),您也可以在應(yīng)用程序內(nèi)創(chuàng)建輕量級(jí)HTTP服務(wù)以響應(yīng)Liveness探針。 Kubernetes去訪問(wèn)一個(gè)路徑,如果它得到的是200或300范圍內(nèi)的HTTP響應(yīng),它會(huì)將應(yīng)用程序標(biāo)記為健康。 否則它被標(biāo)記為不健康。
httpGet配置項(xiàng):
- host:連接的主機(jī)名,默認(rèn)連接到pod的IP。你可能想在http header中設(shè)置"Host"而不是使用IP。
- scheme:連接使用的schema,默認(rèn)HTTP。
- path: 訪問(wèn)的HTTP server的path。
- httpHeaders:自定義請(qǐng)求的header。HTTP運(yùn)行重復(fù)的header。
- port:訪問(wèn)的容器的端口名字或者端口號(hào)。端口號(hào)必須介于1和65535之間。
apiVersion: v1
kind: Pod
metadata:
labels:
test: liveness
name: liveness-http
spec:
containers:
- name: liveness
image: k8s.gcr.io/liveness
args:
- /server
livenessProbe:
httpGet:
path: /healthz
port: 8080
httpHeaders:
- name: Custom-Header
value: Awesome
initialDelaySeconds: 3
periodSeconds: 3
Exec
對(duì)于Exec探針,Kubernetes則只是在容器內(nèi)運(yùn)行命令。 如果命令以退出代碼0返回,則容器標(biāo)記為健康。 否則,它被標(biāo)記為不健康。 當(dāng)您不能或不想運(yùn)行HTTP服務(wù)時(shí),此類(lèi)型的探針則很有用,但是必須是運(yùn)行可以檢查您的應(yīng)用程序是否健康的命令。
Exec 的配置項(xiàng)(exec):
- command:需要執(zhí)行的命令,需要符合命令的格式
apiVersion: v1
kind: Pod
metadata:
labels:
test: liveness
name: liveness-exec
spec:
containers:
- name: liveness
image: k8s.gcr.io/busybox
args:
- /bin/sh
- -c
- touch /tmp/healthy; sleep 30; rm -rf /tmp/healthy; sleep 600
livenessProbe:
exec:
command:
- cat
- /tmp/healthy
initialDelaySeconds: 5
periodSeconds: 5
TCP
最后一種類(lèi)型的探針是TCP探針,Kubernetes嘗試在指定端口上建立TCP連接。 如果它可以建立連接,則容器被認(rèn)為是健康的;否則被認(rèn)為是不健康的。
如果您有HTTP探針或Command探針不能正常工作的情況,TCP探測(cè)器會(huì)派上用場(chǎng)。 例如,gRPC或FTP服務(wù)是此類(lèi)探測(cè)的主要候選者。
TCP 的配置項(xiàng)(tcpSocket):
- host:探測(cè)的主機(jī),默認(rèn)為本pod ip
- port:端口,1到65535
apiVersion: v1
kind: Pod
metadata:
name: goproxy
labels:
app: goproxy
spec:
containers:
- name: goproxy
image: k8s.gcr.io/goproxy:0.1
ports:
- containerPort: 8080
readinessProbe:
tcpSocket:
port: 8080
initialDelaySeconds: 5
periodSeconds: 10
livenessProbe:
tcpSocket:
port: 8080
initialDelaySeconds: 15
periodSeconds: 20
就緒探針
就緒探針(Readiness probe):讓Kubernetes知道您的應(yīng)用是否準(zhǔn)備好其流量服務(wù)。 Kubernetes確保Readiness探針檢測(cè)通過(guò),然后允許服務(wù)將流量發(fā)送到Pod。 如果Readiness探針開(kāi)始失敗,Kubernetes將停止向該容器發(fā)送流量,直到它通過(guò)。 判斷容器是否處于可用Ready狀態(tài), 達(dá)到ready狀態(tài)表示pod可以接受請(qǐng)求, 如果不健康, 從service的后端endpoint列表中把pod隔離出去
用戶(hù)通過(guò) Liveness 探測(cè)可以告訴 Kubernetes 什么時(shí)候通過(guò)重啟容器實(shí)現(xiàn)自愈;而就緒探針Readiness則是告訴 Kubernetes 什么時(shí)候可以將容器加入到 Service 負(fù)載均衡中,對(duì)外提供服務(wù)。
Readiness 探測(cè)的配置語(yǔ)法與 Liveness 探測(cè)完全一樣,這里不再贅述。
如果連續(xù) n 次 Readiness 探測(cè)均失敗后,READY被設(shè)置為不可用。
status 為 running,但是 ready 數(shù)為 0/1
動(dòng)圖示例:
兩種探針對(duì)比
Liveness 探測(cè)和 Readiness 探測(cè)是兩種 Health Check 機(jī)制,如果不特意配置,Kubernetes 將對(duì)兩種探測(cè)采取相同的默認(rèn)行為,即通過(guò)判斷容器啟動(dòng)進(jìn)程的返回值是否為零來(lái)判斷探測(cè)是否成功。
兩種探測(cè)的配置方法完全一樣,支持的配置參數(shù)也一樣。不同之處在于探測(cè)失敗后的行為:Liveness 探測(cè)是重啟容器;Readiness 探測(cè)則是將容器設(shè)置為不可用,不接收 Service 轉(zhuǎn)發(fā)的請(qǐng)求。
Liveness 探測(cè)和 Readiness 探測(cè)是獨(dú)立執(zhí)行的,二者之間沒(méi)有依賴(lài),所以可以單獨(dú)使用,也可以同時(shí)使用。
用 Liveness 探測(cè)判斷容器是否需要重啟以實(shí)現(xiàn)自愈;用 Readiness 探測(cè)判斷容器是否已經(jīng)準(zhǔn)備好對(duì)外提供服務(wù)。Readiness可用于指定容器啟動(dòng)后,判斷容器各服務(wù)是否已正常啟動(dòng)(如啟動(dòng)腳本執(zhí)行后寫(xiě)指定內(nèi)容至特定文件)
使用場(chǎng)景
擴(kuò)縮容
對(duì)于生產(chǎn)環(huán)境中重要的應(yīng)用都建議配置 Health Check,保證處理客戶(hù)請(qǐng)求的容器都是準(zhǔn)備就緒的 Service backend。如果 Liveness不通過(guò),則應(yīng)該縮掉異常 pod,重新啟動(dòng)新 pod
示例:
對(duì)于 http://[container_ip]:8080/healthy,應(yīng)用則可以實(shí)現(xiàn)自己的判斷邏輯,比如檢查所依賴(lài)的數(shù)據(jù)庫(kù)是否就緒,示例代碼如下:
健康檢查的步驟為:
- 容器啟動(dòng) 10 秒之后開(kāi)始探測(cè)。
- 如果 http://[container_ip]:8080/healthy 返回代碼不是 200-400,表示容器沒(méi)有就緒,不接收 Service web-svc 的請(qǐng)求。
- 每隔 5 秒再探測(cè)一次。
- 直到返回代碼為 200-400,表明容器已經(jīng)就緒,然后將其加入到 web-svc 的負(fù)責(zé)均衡中,開(kāi)始處理客戶(hù)請(qǐng)求。
- 探測(cè)會(huì)繼續(xù)以 5 秒的間隔執(zhí)行,如果連續(xù)發(fā)生 3 次失敗,容器又會(huì)從負(fù)載均衡中移除,直到下次探測(cè)成功重新加入。
滾動(dòng)更新
現(xiàn)有一個(gè)正常運(yùn)行的多副本應(yīng)用,接下來(lái)對(duì)應(yīng)用進(jìn)行更新(比如使用更高版本的 image),Kubernetes 會(huì)啟動(dòng)新副本,然后發(fā)生了如下事件:
- 正常情況下新副本需要 10 秒鐘完成準(zhǔn)備工作,在此之前無(wú)法響應(yīng)業(yè)務(wù)請(qǐng)求。
- 但由于人為配置錯(cuò)誤,副本始終無(wú)法完成準(zhǔn)備工作(比如無(wú)法連接后端數(shù)據(jù)庫(kù))。
如果沒(méi)有配置健康檢查,則有問(wèn)題的新副本將替換老副本,導(dǎo)致集群服務(wù)異常。
如果正確配置了 Health Check,新副本只有通過(guò)了 Readiness 探測(cè),才會(huì)被添加到 Service;如果沒(méi)有通過(guò)探測(cè),現(xiàn)有副本不會(huì)被全部替換,業(yè)務(wù)仍然正常進(jìn)行。
實(shí)現(xiàn)原理
liveness 和 readiness 的探測(cè)都是由kubelet執(zhí)行。
exec方式
func (pb *prober) runProbe(p *v1.Probe, pod *v1.Pod, status v1.PodStatus, container v1.Container, containerID kubecontainer.ContainerID) (probe.Result, string, error) {
.....
command := kubecontainer.ExpandContainerCommandOnlyStatic(p.Exec.Command, container.Env)
return pb.exec.Probe(pb.newExecInContainer(container, containerID, command, timeout))
......
func (pb *prober) newExecInContainer(container v1.Container, containerID kubecontainer.ContainerID, cmd []string, timeout time.Duration) exec.Cmd {
return execInContainer{func() ([]byte, error) {
return pb.runner.RunInContainer(containerID, cmd, timeout)
}}
}
......
func (m *kubeGenericRuntimeManager) RunInContainer(id kubecontainer.ContainerID, cmd []string, timeout time.Duration) ([]byte, error) {
stdout, stderr, err := m.runtimeService.ExecSync(id.ID, cmd, 0)
return append(stdout, stderr...), err
}
由kubelet,通過(guò)CRI接口的ExecSync接口,在對(duì)應(yīng)容器內(nèi)執(zhí)行拼裝好的cmd命令。獲取返回值。
func (pr execProber) Probe(e exec.Cmd) (probe.Result, string, error) {
data, err := e.CombinedOutput()
glog.V(4).Infof("Exec probe response: %q", string(data))
if err != nil {
exit, ok := err.(exec.ExitError)
if ok {
if exit.ExitStatus() == 0 {
return probe.Success, string(data), nil
} else {
return probe.Failure, string(data), nil
}
}
return probe.Unknown, "", err
}
return probe.Success, string(data), nil
}
kubelet是根據(jù)執(zhí)行命令的退出碼來(lái)決定是否探測(cè)成功。當(dāng)執(zhí)行命令的退出碼為0時(shí),認(rèn)為執(zhí)行成功,否則為執(zhí)行失敗。如果執(zhí)行超時(shí),則狀態(tài)為Unknown。
http探測(cè)
func DoHTTPProbe(url *url.URL, headers http.Header, client HTTPGetInterface) (probe.Result, string, error) {
req, err := http.NewRequest("GET", url.String(), nil)
......
if res.StatusCode >= http.StatusOK && res.StatusCode < http.StatusBadRequest {
glog.V(4).Infof("Probe succeeded for %s, Response: %v", url.String(), *res)
return probe.Success, body, nil
}
......
http探測(cè)是通過(guò)kubelet請(qǐng)求容器的指定url,并根據(jù)response來(lái)進(jìn)行判斷。 當(dāng)返回的狀態(tài)碼在200到400(不含400)之間時(shí),也就是狀態(tài)碼為2xx和3xx,認(rèn)為探測(cè)成功。否則認(rèn)為失敗。
tcp探測(cè)
func DoTCPProbe(addr string, timeout time.Duration) (probe.Result, string, error) {
conn, err := net.DialTimeout("tcp", addr, timeout)
if err != nil {
// Convert errors to failures to handle timeouts.
return probe.Failure, err.Error(), nil
}
err = conn.Close()
if err != nil {
glog.Errorf("Unexpected error closing TCP probe socket: %v (%#v)", err, err)
}
return probe.Success, "", nil
}
tcp探測(cè)是通過(guò)探測(cè)指定的端口。如果可以連接,則認(rèn)為探測(cè)成功,否則認(rèn)為失敗。
其他
執(zhí)行命令探測(cè)失敗的原因主要可能是容器未成功啟動(dòng),或者執(zhí)行命令失敗。當(dāng)然也可能docker或者docker-shim存在故障。
由于http和tcp都是從kubelet自node節(jié)點(diǎn)上發(fā)起的,向容器的ip進(jìn)行探測(cè)。 所以探測(cè)失敗的原因除了應(yīng)用容器的問(wèn)題外,還可能是從node到容器ip的網(wǎng)絡(luò)不通。
readiness檢查結(jié)果會(huì)通過(guò)SetContainerReadiness函數(shù),設(shè)置到pod的status中,從而更新pod的ready condition。
liveness和readiness除了最終的作用不同,另外一個(gè)很大的區(qū)別是它們的初始值不同。
switch probeType {
case readiness:
w.spec = container.ReadinessProbe
w.resultsManager = m.readinessManager
w.initialValue = results.Failure
case liveness:
w.spec = container.LivenessProbe
w.resultsManager = m.livenessManager
w.initialValue = results.Success
}
liveness的初始值為成功。這樣防止在應(yīng)用還沒(méi)有成功啟動(dòng)前,就被誤殺。如果在規(guī)定時(shí)間內(nèi)還未成功啟動(dòng),才將其設(shè)置為失敗,從而觸發(fā)容器重建。
而readiness的初始值為失敗。這樣防止應(yīng)用還沒(méi)有成功啟動(dòng)前就向應(yīng)用進(jìn)行流量的導(dǎo)入。如果在規(guī)定時(shí)間內(nèi)啟動(dòng)成功,才將其設(shè)置為成功,從而將流量向應(yīng)用導(dǎo)入。
liveness與readiness二者作用不能相互替代。
例如只配置了liveness,那么在容器啟動(dòng),應(yīng)用還沒(méi)有成功就緒之前,這個(gè)時(shí)候pod是ready的(因?yàn)槿萜鞒晒?dòng)了)。那么流量就會(huì)被引入到容器的應(yīng)用中,可能會(huì)導(dǎo)致請(qǐng)求失敗。雖然在liveness檢查失敗后,重啟容器,此時(shí)pod的ready的condition會(huì)變?yōu)閒alse。但是前面會(huì)有一些流量因?yàn)殄e(cuò)誤狀態(tài)導(dǎo)入。
當(dāng)然只配置了readiness是無(wú)法觸發(fā)容器重啟的。
因?yàn)槎叩淖饔貌煌?,在?shí)際使用中,可以根據(jù)實(shí)際的需求將二者進(jìn)行配合使用。
新探針:?jiǎn)?dòng)探針
設(shè)計(jì)文檔:[https://github.com/kubernetes/enhancements/blob/master/keps/sig-node/20190221-livenessprobe-holdoff.md]
目的:
對(duì)于慢啟動(dòng)容器來(lái)說(shuō),現(xiàn)有的健康檢查機(jī)制不太好用
慢啟動(dòng)容器:指需要大量時(shí)間(一到幾分鐘)啟動(dòng)的容器。啟動(dòng)緩慢的原因可能有多種:
- 長(zhǎng)時(shí)間的數(shù)據(jù)初始化:只有第一次啟動(dòng)會(huì)花費(fèi)很多時(shí)間
- 負(fù)載很高:每次啟動(dòng)都花費(fèi)很多時(shí)間
- 節(jié)點(diǎn)資源不足/過(guò)載:即容器啟動(dòng)時(shí)間取決于外部因素
這種容器的主要問(wèn)題在于,在livenessProbe失敗之前,應(yīng)該給它們足夠的時(shí)間來(lái)啟動(dòng)它們。對(duì)于這種問(wèn)題,現(xiàn)有的機(jī)制的處理方式為:
- 方法一:livenessProbe中把
延遲初始時(shí)間initialDelaySeconds設(shè)置的很長(zhǎng),以允許容器啟動(dòng)(即initialDelaySeconds大于平均啟動(dòng)時(shí)間)。雖然這樣可以確保livenessProbe不會(huì)檢測(cè)失敗,但是不知道initialDelaySeconds應(yīng)該配置為多少,啟動(dòng)時(shí)間不是一個(gè)固定值。另外,因?yàn)閘ivenessProbe在啟動(dòng)過(guò)程還沒(méi)運(yùn)行,因此pod 得不到反饋,events 看不到內(nèi)容,如果你initialDelaySeconds是 10 分鐘,那這 10 分鐘內(nèi)你不知道在發(fā)生什么。 - 方法二:增加livenessProbe的失敗次數(shù)。即failureThreshold*periodSeconds的乘積足夠大,簡(jiǎn)單粗暴,同時(shí)容器在初次成功啟動(dòng)后,就算死鎖或以其他方式掛起,livenessProbe也會(huì)不斷探測(cè)
方法二可以解決這個(gè)問(wèn)題,但不夠優(yōu)雅。
因?yàn)閘ivenessProbe的設(shè)計(jì)是為了在 pod 啟動(dòng)成功后進(jìn)行健康探測(cè),最好前提是 pod 已經(jīng)啟動(dòng)成功,否則啟動(dòng)階段的多次失敗是沒(méi)有意義的,因此官方提出了一種新的探針:即startupProbe,startupProbe并不是一種新的數(shù)據(jù)結(jié)構(gòu),他完全復(fù)用了livenessProbe,只是名字改了下,多了一種概念,關(guān)于這個(gè) probe 的提議討論可以參考issue
使用方式:startup-probes
ports:
- name: liveness-port
containerPort: 8080
hostPort: 8080
livenessProbe:
httpGet:
path: /healthz
port: liveness-port
failureThreshold: 1
periodSeconds: 10
startupProbe:
httpGet:
path: /healthz
port: liveness-port
failureThreshold: 30
periodSeconds: 10
這個(gè)配置的含義是:
startupProbe首先檢測(cè),該應(yīng)用程序最多有5分鐘(30 * 10 = 300s)完成啟動(dòng)。一旦startupProbe成功一次,livenessProbe將接管,以對(duì)后續(xù)運(yùn)行過(guò)程中容器死鎖提供快速響應(yīng)。如果startupProbe從未成功,則容器將在300秒后被殺死。
k8s 1.16 才開(kāi)始支持startupProbe這個(gè)特性
最佳實(shí)踐
上述的擴(kuò)縮容和滾動(dòng)升級(jí)場(chǎng)景都需要用戶(hù)對(duì)應(yīng)用的健康檢查足夠了解,并且配置合適的策略。主要工作是:
- 給用戶(hù)程序開(kāi)發(fā)一個(gè)/healthy 接口,來(lái)獲取世界可用狀態(tài)(僅僅是 http服務(wù))
- 定義合理的健康檢查組合
注意事項(xiàng):
- periodSeconds探測(cè)周期不能太短,否則會(huì)發(fā)送很多請(qǐng)求,也不能太長(zhǎng),否則會(huì)導(dǎo)致發(fā)現(xiàn)不了異常 pod
- 合理配置failureThreshold和successThreshold,否則會(huì)導(dǎo)致在 ready 和 not ready 直接反復(fù)擺動(dòng)
改造服務(wù)
如果你的服務(wù)無(wú)法提供 http 的健康檢查接口,可能需要修改你的業(yè)務(wù)代碼:如 grpc 服務(wù),后面會(huì)提到
sidecar 形式做健康檢查
如果你不想更改你的業(yè)務(wù)邏輯,您的應(yīng)用程序容器優(yōu)沒(méi)有公開(kāi)HTTP接口以進(jìn)行健康檢查,那么您可以將另一個(gè)容器部署在pod內(nèi)并調(diào)用您的應(yīng)用程序的,即sidecar模式
之所以可行,是因?yàn)镻od的所有容器都在同一個(gè)環(huán)回接口(localhost)中。您無(wú)需將此端口暴露給外界。
Cli 工具
你的應(yīng)用雖然不提供端點(diǎn),但是可以通過(guò)exec 的方式執(zhí)行容器內(nèi)預(yù)裝的 cli 工具來(lái)實(shí)現(xiàn)健康檢查,其實(shí)就是腳本形式,變相的 exec
系統(tǒng)探針
你的應(yīng)用不提供端點(diǎn),但是可以側(cè)面顯示應(yīng)用的使用狀態(tài),如 cpu內(nèi)存使用率,通過(guò)系統(tǒng)指標(biāo)來(lái)側(cè)面反映服務(wù)的狀態(tài),如機(jī)器學(xué)習(xí)作業(yè)會(huì)使GPU升溫,從而導(dǎo)致計(jì)算速度變慢。檢測(cè)不通過(guò),將作業(yè)移到其他節(jié)點(diǎn)可以解決此問(wèn)題。
如何支持 gprc 的健康檢查
GRPC正在成為云原生微服務(wù)之間通信的通用語(yǔ)言。如果您今天要將gRPC應(yīng)用程序部署到Kubernetes,您可能想知道配置運(yùn)行狀況檢查的最佳方法。在本文中,我們將討論grpc-health-probe,一種Kubernetes本地健康檢查gRPC應(yīng)用程序的方法。
kubernetes本身不支持gRPC健康檢查。這使得gRPC開(kāi)發(fā)人員在部署到Kubernetes時(shí)有以下三種方法:
- httpGet probe: 不能與gRPC原生使用。您需要重構(gòu)您的應(yīng)用程序以同時(shí)提供gRPC和HTTP / 1.1協(xié)議(在不同的端口號(hào)上)。
- tcpSocket probe: 打開(kāi)套接字到gRPC服務(wù)器是沒(méi)有意義的,因?yàn)樗鼰o(wú)法讀取響應(yīng)正文。
- exec probe: 這會(huì)定期調(diào)用容器生態(tài)系統(tǒng)中的程序。對(duì)于gRPC,這意味著您自己實(shí)現(xiàn)健康RPC,然后使用編寫(xiě)客戶(hù)端工具,并將客戶(hù)端工具與容器打包到一起。
為了標(biāo)準(zhǔn)化上面提到的“exec探針”方法,我們需要:
- 標(biāo)準(zhǔn)的健康檢查“協(xié)議”,可以輕松地在任何gRPC服務(wù)器中實(shí)現(xiàn)。
- 標(biāo)準(zhǔn)的健康檢查“工具”,可以輕松查詢(xún)健康協(xié)議。
得慶幸的是,gRPC有一個(gè)標(biāo)準(zhǔn)的健康檢查協(xié)議。它可以從任何語(yǔ)言輕松使用。生成的代碼和用于設(shè)置運(yùn)行狀況的實(shí)用程序幾乎都在gRPC的所有語(yǔ)言實(shí)現(xiàn)中提供。
如果在gRPC應(yīng)用程序中實(shí)現(xiàn)此運(yùn)行狀況檢查協(xié)議,則可以使用標(biāo)準(zhǔn)/通用工具調(diào)用此Check()方法來(lái)確定服務(wù)器狀態(tài)。
下面你需要的是“標(biāo)準(zhǔn)工具”,它是grpc-health-probe。
使用此工具,您可以在所有g(shù)RPC應(yīng)用程序中使用相同的運(yùn)行狀況檢查配置。這種方法需要你:
- 選擇您喜歡的語(yǔ)言找到gRPC“health”模塊并開(kāi)始使用它(例如Go庫(kù))。
- 將grpc_health_probe二進(jìn)制文件打到容器中。
- 配置Kubernetes“exec”探針以調(diào)用容器中的“grpc_health_probe”工具。
或者參考:https://github.com/americanexpress/grpc-k8s-health-check
參考
- https://www.youtube.com/watch?v=mxEvAPQRwhw
- https://cloud.google.com/blog/products/gcp/kubernetes-best-practices-setting-up-health-checks-with-readiness-and-liveness-probes
- https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.10/#execaction-v1-core
- https://xuxinkun.github.io/2019/10/28/liveness-readiness/
- https://yq.aliyun.com/articles/68567
- https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-startup-probes/
- https://www.ianlewis.org/en/kubernetes-health-checks-django
- https://ahmet.im/blog/advanced-kubernetes-health-checks/