簡介
此文講述如何配置容器的Liveness、Readiness、Startup探針。
kubelet使用Liveness探測器來知道什么時候要重啟容器。例如,Liveness探測器可以捕捉到死鎖(應用程序在運行,但是無法繼續執行后面的步驟)。這樣的情況下重啟容器有助于讓應用程序在有問題的情況下更可用。
kubelet使用Readiness探測器可以知道容器什么時候準備好了并可以開始接受請求流量, 當一個Pod內的所有容器都準備好了,才能把這個Pod看作就緒了。這種信號的一個用途就是控制哪個Pod作為Service的后端。在Pod還沒有準備好的時候,會從Service的負載均衡器中被剔除的。
kubelet使用Startup探測器可以知道應用程序容器什么時候啟動了。如果配置了這類探測器,就可以控制容器在啟動成功后再進行Liveness和Readiness檢查,確保這些存活、就緒探測器不會影響應用程序的啟動。這可以用于對慢啟動容器進行存活性檢測,避免它們在啟動運行之前就被殺掉。
定義一個Liveness探針
許多長時間運行的應用程序最終會過渡到斷開的狀態,除非重新啟動,否則無法恢復。Kubernetes提供了Liveness探測器來發現并補救這種情況。
創建一個Pod,其中運行一個基于k8s.gcr.io/busybox鏡像的容器。配置文件如下。文件名:exec-liveness.yaml
- 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
在配置文件中,可以看到Pod中只有一個容器。periodSeconds字段指定了kubelet應該每5秒執行一次存活檢測。initialDelaySeconds字段告訴kubelet在執行第一次探針前應該等待5秒。kubelet在容器中執行命令cat /tmp/healthy來進行檢測。如果命令執行成功并且返回值為0,kubelet會認為這個容器是健康存活的。如果這個命令返回非0值,kubelet會殺死這個容器并重新啟動它。執行命令如下:
- /bin/sh -c "touch /tmp/healthy; sleep 30; rm -rf /tmp/healthy; sleep 600"
這個容器生命的前30秒,/tmp/healthy文件是存在的。執行命令cat /tmp/healthy會返回成功碼。30秒后,執行命令cat /tmp/healthy就回返回失敗碼。
創建Pod:
- # kubectl apply -f /root/k8s-example/probe/exec-liveness.yaml
在 30 秒內,查看Pod的事件:
- kubectl describe pod liveness-exec
輸出結果顯示還沒有存活探測器失敗:
- Events:
- Type Reason Age From Message
- ---- ------ ---- ---- -------
- Normal Scheduled default-scheduler Successfully assigned default/liveness-exec to k8s-node04
- Normal Pulled 22s kubelet, k8s-node04 Container image "k8s.gcr.io/busybox" already present on machine
- Normal Created 22s kubelet, k8s-node04 Created container liveness
- Normal Started 22s kubelet, k8s-node04 Started container liveness
30秒之后,再來看Pod的事件:
- kubectl describe pod liveness-exec
在輸出結果的最下面,有信息顯示存活探測器失敗了,這個容器被殺死并且被重建了。
- Events:
- Type Reason Age From Message
- ---- ------ ---- ---- -------
- Normal Scheduled default-scheduler Successfully assigned default/liveness-exec to k8s-node04
- Normal Pulled 47s kubelet, k8s-node04 Container image "k8s.gcr.io/busybox" already present on machine
- Normal Created 47s kubelet, k8s-node04 Created container liveness
- Normal Started 47s kubelet, k8s-node04 Started container liveness
- Warning Unhealthy 5s (x3 over 15s) kubelet, k8s-node04 Liveness probe failed: cat: can't open '/tmp/healthy': No such file or directory
- Normal Killing 5s kubelet, k8s-node04 Container liveness failed liveness probe, will be restarted
再等另外30秒,檢查看這個容器被重啟了:
- kubectl get pod liveness-exec
- NAME READY STATUS RESTARTS AGE
- liveness-exec 1/1 Running 2 3m10s
再查看Pod資源詳情:
- kubectl describe pod liveness-exec
輸出結果如下,容器重啟成功。
- Events:
- Type Reason Age From Message
- ---- ------ ---- ---- -------
- Normal Scheduled default-scheduler Successfully assigned default/liveness-exec to k8s-node04
- Warning Unhealthy 35s (x6 over 2m) kubelet, k8s-node04 Liveness probe failed: cat: can't open '/tmp/healthy': No such file or directory
- Normal Killing 35s (x2 over 110s) kubelet, k8s-node04 Container liveness failed liveness probe, will be restarted
- Normal Pulled 5s (x3 over 2m32s) kubelet, k8s-node04 Container image "k8s.gcr.io/busybox" already present on machine
- Normal Created 5s (x3 over 2m32s) kubelet, k8s-node04 Created container liveness
- Normal Started 5s (x3 over 2m32s) kubelet, k8s-node04 Started container liveness
定義一個存活態HTTP請求接口
另外一種類型的Liveness探測方式是使用HTTP GET請求。下面是一個Pod的配置文件,其中運行一個基于k8s.gcr.io/liveness鏡像的容器。
創建Pod:
- 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: X-Custom-Header
- value: Awesome
- initialDelaySeconds: 3
- periodSeconds: 3
配置文件中,Pod中只有一個容器。periodSeconds字段指定了kubelet每隔3秒執行一次檢測。initialDelaySeconds字段告訴kubelet在執行第一次探測前應該等待3秒。kubelet 會向容器內運行的服務(服務會監聽 8080 端口)發送一個 HTTP GET 請求來執行探測。如果服務上/healthz路徑下的處理程序返回成功碼。則kubelet認為容器是健康存活的。如果處理程序返回失敗碼,則kubelet會殺死這個容器并且重新啟動它。
任何大于或等于200并且小于400的返回碼標示成功,其它返回碼都標示失敗。
可以在這里看到服務的源碼:https://github.com/kubernetes/ ... er.go
容器存活的最開始10秒中,/healthz處理程序返回一個200的狀態碼。之后處理程序返回500的狀態碼。
- 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"))
- }
- })
kubelet在容器啟動之后3秒開始執行健康檢測。所以前幾次健康檢查都是成功的。但是10秒之后,健康檢查會失敗,并且kubelet會殺死容器再重新啟動容器。
- # kubectl apply -f /root/k8s-example/probe/http-liveness.yaml
10秒之后,通過看Pod事件來檢測存活探測器已經失敗了并且容器被重新啟動了。
- Events:
- Type Reason Age From Message
- ---- ------ ---- ---- -------
- Normal Scheduled default-scheduler Successfully assigned default/liveness-http to k8s-node01
- Normal Pulled 17s kubelet, k8s-node01 Container image "k8s.gcr.io/liveness" already present on machine
- Normal Created 17s kubelet, k8s-node01 Created container liveness
- Normal Started 16s kubelet, k8s-node01 Started container liveness
- Warning Unhealthy 1s (x2 over 4s) kubelet, k8s-node01 Liveness probe failed: HTTP probe failed with statuscode: 500
定義TCP的存活探測
第三種類型的liveness探測是使用TCP套接字。通過配置,kubelet會嘗試在指定端口和容器建立套接字鏈接。如果能建立鏈接,這個容器就被看作是健康的,如果不能則這個容器就被看作是有問題的。
創建一個Pod。文件名:tcp-liveness-readiness.yaml
- 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
TCP檢測的配置和HTTP檢測非常相似。下面這個例子同時使用就緒和存活探測器。kubelet會在容器啟動5秒后發送第一個就緒探測。這會嘗試連接goproxy容器的8080端口。如果探測成功,這個Pod會被標記為就緒狀態,kubelet將繼續每隔10秒運行一次檢測。
除了readiness探測,這個配置包括了一個Liveness探測。kubelet會在容器啟動15秒后進行第一次Liveness探測。就像Readiness探測一樣,會嘗試連接goproxy容器的8080端口。如果存活探測失敗,這個容器會被重新啟動。
- # kubectl apply -f /root/k8s-example/probe/tcp-liveness-readiness.yaml
15秒之后,通過看Pod事件來檢測存活探測器:
- # kubectl describe pod goproxy
使用命名端口:
對于HTTP或者TCP存活檢測可以使用命名的容器端口。
- ports:
- - name: liveness-port
- containerPort: 8080
- hostPort: 8080
- ?
- livenessProbe:
- httpGet:
- path: /healthz
- port: liveness-port
使用Startup探測器保護慢啟動容器
有時候,會有一些現有的應用程序在啟動時需要較多的初始化時間。要不影響對引起探測死鎖的快速響應,這種情況下,設置Liveness探測參數是要技巧的。技巧就是使用一個命令來設置Startup探測,針對HTTP或者TCP檢測,可以通過設置failureThreshold * periodSeconds參數來保證有足夠長的時間應對糟糕情況下的啟動時間。
所以,前面的例子就變成了:
- 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
幸虧有Startup探測,應用程序將會有最多5分鐘(30*10=300s)的時間來完成它的啟動。 一旦Startup探測成功一次,存活探測任務就會接管對容器的探測,對容器死鎖可以快速響應。 如果Startup探測一直沒有成功,容器會在300秒后被殺死,并且根據restartPolicy來設置Pod狀態。
定義Readliness探測器
有時候,應用程序會暫時性的不能提供通信服務。例如,應用程序在啟動時可能需要加載很大的數據或配置文件,或是啟動后要依賴等待外部服務。在這種情況下,既不想殺死應用程序,也不想給它發送請求。Kubernetes提供了就緒探測器來發現并緩解這些情況。容器所在Pod上報還未就緒的信息,并且不接受通過Kubernetes Service的流量。
注意:就緒探測器在容器的整個生命周期中保持運行狀態。
就緒探測器的配置和存活探測器的配置相似。唯一區別就是要使用readinessProbe字段,而不是LivenessProbe字段。
- readinessProbe:
- exec:
- command:
- - cat
- - /tmp/healthy
- initialDelaySeconds: 5
- periodSeconds: 5
HTTP和TCP的Readliness探測器配置也和Liveness探測器的配置一樣的。
Readliness和Liveness探測可以在同一個容器上并行使用。兩者都用可以確保流量不會發給還沒有準備好的容器,并且容器會在它們失敗的時候被重新啟動。
配置探測器
探測器有很多配置字段,可以使用這些字段精確的控制存活和就緒檢測的行為:
- initialDelaySeconds:容器啟動后要等待多少秒后存活和就緒探測器才被初始化,默認是0秒,最小值是0。
- periodSeconds:執行探測的時間間隔(單位是秒)。默認是10秒。最小值是1。
- timeoutSeconds:探測的超時后等待多少秒。默認值是1秒。最小值是1。
- successThreshold:探測器在失敗后,被視為成功的最小連續成功數。默認值是1。存活探測的這個值必須是1。最小值是1。
- failureThreshold:當Pod啟動了并且探測到失敗,Kubernetes的重試次數。存活探測情況下的放棄就意味著重新啟動容器。就緒探測情況下的放棄Pod會被打上未就緒的標簽。默認值是3。最小值是1。
HTTP探測器可以在httpGet上配置額外的字段:
- host:連接使用的主機名,默認是Pod的IP。也可以在HTTP頭中設置“Host”來代替。
- scheme:用于設置連接主機的方式(HTTP還是HTTPS)。默認是HTTP。
- path:訪問HTTP服務的路徑。
- httpHeaders:請求中自定義的HTTP頭。HTTP頭字段允許重復。
- port:訪問容器的端口號或者端口名。如果數字必須在1 ~ 65535之間。
對于HTTP探測,kubelet發送一個HTTP請求到指定的路徑和端口來執行檢測。除非httpGet中的host字段設置了,否則kubelet默認是給Pod的IP地址發送探測。如果scheme字段設置為了HTTPS,kubelet會跳過證書驗證發送HTTPS請求。大多數情況下,不需要設置host字段。這里有個需要設置host字段的場景,假設容器監聽127.0.0.1,并且Pod的hostNetwork字段設置為了true。那么httpGet中的host字段應該設置為127.0.0.1。可能更常見的情況是如果Pod依賴虛擬主機,你不應該設置host字段,而是應該在httpHeaders中設置Host。
對于一次探測,kubelet在節點上(不是在Pod里面)建立探測連接,這意味著你不能在host參數上配置service name,因為kubelet不能解析service name。
原文地址:https://juejin.cn/post/6993457394827132964