# 配置Pod的liveness和readiness探針
當你使用kubernetes的時候,有沒有遇到過Pod在啟動后一會就掛掉然后又重新啟動這樣的惡性循環?你有沒有想過kubernetes是如何檢測pod是否還存活?雖然容器已經啟動,但是kubernetes如何知道容器的進程是否準備好對外提供服務了呢?讓我們通過kubernetes官網的這篇文章[Configure Liveness and Readiness Probes](https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-probes/),來一探究竟。
本文將展示如何配置容器的存活和可讀性探針。
Kubelet使用liveness probe(存活探針)來確定何時重啟容器。例如,當應用程序處于運行狀態但無法做進一步操作,liveness探針將捕獲到deadlock,重啟處于該狀態下的容器,使應用程序在存在bug的情況下依然能夠繼續運行下去(誰的程序還沒幾個bug呢)。
Kubelet使用readiness probe(就緒探針)來確定容器是否已經就緒可以接受流量。只有當Pod中的容器都處于就緒狀態時kubelet才會認定該Pod處于就緒狀態。該信號的作用是控制哪些Pod應該作為service的后端。如果Pod處于非就緒狀態,那么它們將會被從service的load balancer中移除。
## 定義 liveness命令
許多長時間運行的應用程序最終會轉換到broken狀態,除非重新啟動,否則無法恢復。Kubernetes提供了liveness probe來檢測和補救這種情況。
在本次練習將基于 `gcr.io/google_containers/busybox`鏡像創建運行一個容器的Pod。以下是Pod的配置文件`exec-liveness.yaml`:
```yaml
apiVersion: v1
kind: Pod
metadata:
labels:
test: liveness
name: liveness-exec
spec:
containers:
- name: liveness
args:
- /bin/sh
- -c
- touch /tmp/healthy; sleep 30; rm -rf /tmp/healthy; sleep 600
image: gcr.io/google_containers/busybox
livenessProbe:
exec:
command:
- cat
- /tmp/healthy
initialDelaySeconds: 5
periodSeconds: 5
```
該配置文件給Pod配置了一個容器。`periodSeconds` 規定kubelet要每隔5秒執行一次liveness probe。 `initialDelaySeconds` 告訴kubelet在第一次執行probe之前要的等待5秒鐘。探針檢測命令是在容器中執行 `cat /tmp/healthy` 命令。如果命令執行成功,將返回0,kubelet就會認為該容器是活著的并且很健康。如果返回非0值,kubelet就會殺掉這個容器并重啟它。
容器啟動時,執行該命令:
```bash
/bin/sh -c "touch /tmp/healthy; sleep 30; rm -rf /tmp/healthy; sleep 600"
```
在容器生命的最初30秒內有一個 `/tmp/healthy` 文件,在這30秒內 `cat /tmp/healthy`命令會返回一個成功的返回碼。30秒后, `cat /tmp/healthy` 將返回失敗的返回碼。
創建Pod:
```bash
kubectl create -f https://k8s.io/docs/tasks/configure-pod-container/exec-liveness.yaml
```
在30秒內,查看Pod的event:
```
kubectl describe pod liveness-exec
```
結果顯示沒有失敗的liveness probe:
```bash
FirstSeen LastSeen Count From SubobjectPath Type Reason Message
--------- -------- ----- ---- ------------- -------- ------ -------
24s 24s 1 {default-scheduler } Normal Scheduled Successfully assigned liveness-exec to worker0
23s 23s 1 {kubelet worker0} spec.containers{liveness} Normal Pulling pulling image "gcr.io/google_containers/busybox"
23s 23s 1 {kubelet worker0} spec.containers{liveness} Normal Pulled Successfully pulled image "gcr.io/google_containers/busybox"
23s 23s 1 {kubelet worker0} spec.containers{liveness} Normal Created Created container with docker id 86849c15382e; Security:[seccomp=unconfined]
23s 23s 1 {kubelet worker0} spec.containers{liveness} Normal Started Started container with docker id 86849c15382e
```
啟動35秒后,再次查看pod的event:
```bash
kubectl describe pod liveness-exec
```
在最下面有一條信息顯示liveness probe失敗,容器被刪掉并重新創建。
```bash
FirstSeen LastSeen Count From SubobjectPath Type Reason Message
--------- -------- ----- ---- ------------- -------- ------ -------
37s 37s 1 {default-scheduler } Normal Scheduled Successfully assigned liveness-exec to worker0
36s 36s 1 {kubelet worker0} spec.containers{liveness} Normal Pulling pulling image "gcr.io/google_containers/busybox"
36s 36s 1 {kubelet worker0} spec.containers{liveness} Normal Pulled Successfully pulled image "gcr.io/google_containers/busybox"
36s 36s 1 {kubelet worker0} spec.containers{liveness} Normal Created Created container with docker id 86849c15382e; Security:[seccomp=unconfined]
36s 36s 1 {kubelet worker0} spec.containers{liveness} Normal Started Started container with docker id 86849c15382e
2s 2s 1 {kubelet worker0} spec.containers{liveness} Warning Unhealthy Liveness probe failed: cat: can't open '/tmp/healthy': No such file or directory
```
再等30秒,確認容器已經重啟:
```bash
kubectl get pod liveness-exec
```
從輸出結果來`RESTARTS`值加1了。
```bash
NAME READY STATUS RESTARTS AGE
liveness-exec 1/1 Running 1 1m
```
## 定義一個liveness HTTP請求
我們還可以使用HTTP GET請求作為liveness probe。下面是一個基于`gcr.io/google_containers/liveness`鏡像運行了一個容器的Pod的例子`http-liveness.yaml`:
```yaml
apiVersion: v1
kind: Pod
metadata:
labels:
test: liveness
name: liveness-http
spec:
containers:
- name: liveness
args:
- /server
image: gcr.io/google_containers/liveness
livenessProbe:
httpGet:
path: /healthz
port: 8080
httpHeaders:
- name: X-Custom-Header
value: Awesome
initialDelaySeconds: 3
periodSeconds: 3
```
該配置文件只定義了一個容器,`livenessProbe` 指定kubelet需要每隔3秒執行一次liveness probe。`initialDelaySeconds` 指定kubelet在該執行第一次探測之前需要等待3秒鐘。該探針將向容器中的server的8080端口發送一個HTTP GET請求。如果server的`/healthz`路徑的handler返回一個成功的返回碼,kubelet就會認定該容器是活著的并且很健康。如果返回失敗的返回碼,kubelet將殺掉該容器并重啟它。
任何大于200小于400的返回碼都會認定是成功的返回碼。其他返回碼都會被認為是失敗的返回碼。
查看該server的源碼:[server.go](https://github.com/kubernetes/kubernetes/blob/master/test/images/liveness/server.go).
最開始的10秒該容器是活著的, `/healthz` handler返回200的狀態碼。這之后將返回500的返回碼。
```go
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"))
}
})
```
容器啟動3秒后,kubelet開始執行健康檢查。第一次健康監測會成功,但是10秒后,健康檢查將失敗,kubelet將殺掉和重啟容器。
創建一個Pod來測試一下HTTP liveness檢測:
```bash
kubectl create -f https://k8s.io/docs/tasks/configure-pod-container/http-liveness.yaml
```
After 10 seconds, view Pod events to verify that liveness probes have failed and
the Container has been restarted:
10秒后,查看Pod的event,確認liveness probe失敗并重啟了容器。
```bash
kubectl describe pod liveness-http
```
## 定義TCP liveness探針
第三種liveness probe使用TCP Socket。 使用此配置,kubelet將嘗試在指定端口上打開容器的套接字。 如果可以建立連接,容器被認為是健康的,如果不能就認為是失敗的。
```yaml
apiVersion: v1
kind: Pod
metadata:
name: goproxy
labels:
app: goproxy
spec:
containers:
- name: goproxy
image: gcr.io/google_containers/goproxy:0.1
ports:
- containerPort: 8080
readinessProbe:
tcpSocket:
port: 8080
initialDelaySeconds: 5
periodSeconds: 10
livenessProbe:
tcpSocket:
port: 8080
initialDelaySeconds: 15
periodSeconds: 20
```
如您所見,TCP檢查的配置與HTTP檢查非常相似。 此示例同時使用了readiness和liveness probe。 容器啟動后5秒鐘,kubelet將發送第一個readiness probe。 這將嘗試連接到端口8080上的goproxy容器。如果探測成功,則該pod將被標記為就緒。Kubelet將每隔10秒鐘執行一次該檢查。
除了readiness probe之外,該配置還包括liveness probe。 容器啟動15秒后,kubelet將運行第一個liveness probe。 就像readiness probe一樣,這將嘗試連接到goproxy容器上的8080端口。如果liveness probe失敗,容器將重新啟動。
## 使用命名的端口
可以使用命名的ContainerPort作為HTTP或TCP liveness檢查:
```yaml
ports:
- name: liveness-port
containerPort: 8080
hostPort: 8080
livenessProbe:
httpGet:
path: /healthz
port: liveness-port
```
## 定義readiness探針
有時,應用程序暫時無法對外部流量提供服務。 例如,應用程序可能需要在啟動期間加載大量數據或配置文件。 在這種情況下,你不想殺死應用程序,但你也不想發送請求。 Kubernetes提供了readiness probe來檢測和減輕這些情況。 Pod中的容器可以報告自己還沒有準備,不能處理Kubernetes服務發送過來的流量。
Readiness probe的配置跟liveness probe很像。唯一的不同是使用 `readinessProbe `而不是`livenessProbe`。
```yaml
readinessProbe:
exec:
command:
- cat
- /tmp/healthy
initialDelaySeconds: 5
periodSeconds: 5
```
Readiness probe的HTTP和TCP的探測器配置跟liveness probe一樣。
Readiness和livenss probe可以并行用于同一容器。 使用兩者可以確保流量無法到達未準備好的容器,并且容器在失敗時重新啟動。
## 配置Probe
Probe 中有很多精確和詳細的配置,通過它們你能準確的控制liveness和readiness檢查:
- `initialDelaySeconds`:容器啟動后第一次執行探測是需要等待多少秒。
- `periodSeconds`:執行探測的頻率。默認是10秒,最小1秒。
- `timeoutSeconds`:探測超時時間。默認1秒,最小1秒。
- `successThreshold`:探測失敗后,最少連續探測成功多少次才被認定為成功。默認是1。對于liveness必須是1。最小值是1。
- `failureThreshold`:探測成功后,最少連續探測失敗多少次才被認定為失敗。默認是3。最小值是1。
HTTP probe 中可以給 `httpGet`設置其他配置項:
- `host`:連接的主機名,默認連接到pod的IP。你可能想在http header中設置"Host"而不是使用IP。
- `scheme`:連接使用的schema,默認HTTP。
- `path`: 訪問的HTTP server的path。
- `httpHeaders`:自定義請求的header。HTTP運行重復的header。
- `port`:訪問的容器的端口名字或者端口號。端口號必須介于1和65535之間。
對于HTTP探測器,kubelet向指定的路徑和端口發送HTTP請求以執行檢查。 Kubelet將probe發送到容器的IP地址,除非地址被`httpGet`中的可選`host`字段覆蓋。 在大多數情況下,你不想設置主機字段。 有一種情況下你可以設置它。 假設容器在127.0.0.1上偵聽,并且Pod的`hostNetwork`字段為true。 然后,在`httpGet`下的`host`應該設置為127.0.0.1。 如果你的pod依賴于虛擬主機,這可能是更常見的情況,你不應該是用`host`,而是應該在`httpHeaders`中設置`Host`頭。
## 參考
- 關于 [Container Probes](https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle/#container-probes) 的更多信息
- 序言
- 云原生
- 云原生(Cloud Native)的定義
- CNCF - 云原生計算基金會簡介
- CNCF章程
- 云原生的設計哲學
- Play with Kubernetes
- 快速部署一個云原生本地實驗環境
- Kubernetes與云原生應用概覽
- 云原生應用之路——從Kubernetes到Cloud Native
- 云原生編程語言
- 云原生編程語言Ballerina
- 云原生編程語言Pulumi
- 云原生的未來
- Kubernetes架構
- 設計理念
- Etcd解析
- 開放接口
- CRI - Container Runtime Interface(容器運行時接口)
- CNI - Container Network Interface(容器網絡接口)
- CSI - Container Storage Interface(容器存儲接口)
- Kubernetes中的網絡
- Kubernetes中的網絡解析——以flannel為例
- Kubernetes中的網絡解析——以calico為例
- 具備API感知的網絡和安全性管理開源軟件Cilium
- Cilium架構設計與概念解析
- 資源對象與基本概念解析
- Pod狀態與生命周期管理
- Pod概覽
- Pod解析
- Init容器
- Pause容器
- Pod安全策略
- Pod的生命周期
- Pod Hook
- Pod Preset
- Pod中斷與PDB(Pod中斷預算)
- 集群資源管理
- Node
- Namespace
- Label
- Annotation
- Taint和Toleration(污點和容忍)
- 垃圾收集
- 控制器
- Deployment
- StatefulSet
- DaemonSet
- ReplicationController和ReplicaSet
- Job
- CronJob
- Horizontal Pod Autoscaling
- 自定義指標HPA
- 準入控制器(Admission Controller)
- 服務發現
- Service
- Ingress
- Traefik Ingress Controller
- 身份與權限控制
- ServiceAccount
- RBAC——基于角色的訪問控制
- NetworkPolicy
- 存儲
- Secret
- ConfigMap
- ConfigMap的熱更新
- Volume
- Persistent Volume(持久化卷)
- Storage Class
- 本地持久化存儲
- 集群擴展
- 使用自定義資源擴展API
- 使用CRD擴展Kubernetes API
- Aggregated API Server
- APIService
- Service Catalog
- 資源調度
- QoS(服務質量等級)
- 用戶指南
- 資源對象配置
- 配置Pod的liveness和readiness探針
- 配置Pod的Service Account
- Secret配置
- 管理namespace中的資源配額
- 命令使用
- Docker用戶過度到kubectl命令行指南
- kubectl命令概覽
- kubectl命令技巧大全
- 使用etcdctl訪問kubernetes數據
- 集群安全性管理
- 管理集群中的TLS
- kubelet的認證授權
- TLS bootstrap
- 創建用戶認證授權的kubeconfig文件
- IP偽裝代理
- 使用kubeconfig或token進行用戶身份認證
- Kubernetes中的用戶與身份認證授權
- Kubernetes集群安全性配置最佳實踐
- 訪問Kubernetes集群
- 訪問集群
- 使用kubeconfig文件配置跨集群認證
- 通過端口轉發訪問集群中的應用程序
- 使用service訪問群集中的應用程序
- 從外部訪問Kubernetes中的Pod
- Cabin - Kubernetes手機客戶端
- Kubernetic - Kubernetes桌面客戶端
- Kubernator - 更底層的Kubernetes UI
- 在Kubernetes中開發部署應用
- 適用于kubernetes的應用開發部署流程
- 遷移傳統應用到Kubernetes中——以Hadoop YARN為例
- 最佳實踐概覽
- 在CentOS上部署Kubernetes集群
- 創建TLS證書和秘鑰
- 創建kubeconfig文件
- 創建高可用etcd集群
- 安裝kubectl命令行工具
- 部署master節點
- 安裝flannel網絡插件
- 部署node節點
- 安裝kubedns插件
- 安裝dashboard插件
- 安裝heapster插件
- 安裝EFK插件
- 生產級的Kubernetes簡化管理工具kubeadm
- 使用kubeadm在Ubuntu Server 16.04上快速構建測試集群
- 服務發現與負載均衡
- 安裝Traefik ingress
- 分布式負載測試
- 網絡和集群性能測試
- 邊緣節點配置
- 安裝Nginx ingress
- 安裝配置DNS
- 安裝配置Kube-dns
- 安裝配置CoreDNS
- 運維管理
- Master節點高可用
- 服務滾動升級
- 應用日志收集
- 配置最佳實踐
- 集群及應用監控
- 數據持久化問題
- 管理容器的計算資源
- 集群聯邦
- 存儲管理
- GlusterFS
- 使用GlusterFS做持久化存儲
- 使用Heketi作為Kubernetes的持久存儲GlusterFS的external provisioner
- 在OpenShift中使用GlusterFS做持久化存儲
- GlusterD-2.0
- Ceph
- 用Helm托管安裝Ceph集群并提供后端存儲
- 使用Ceph做持久化存儲
- 使用rbd-provisioner提供rbd持久化存儲
- OpenEBS
- 使用OpenEBS做持久化存儲
- Rook
- NFS
- 利用NFS動態提供Kubernetes后端存儲卷
- 集群與應用監控
- Heapster
- 使用Heapster獲取集群和對象的metric數據
- Prometheus
- 使用Prometheus監控kubernetes集群
- Prometheus查詢語言PromQL使用說明
- 使用Vistio監控Istio服務網格中的流量
- 分布式跟蹤
- OpenTracing
- 服務編排管理
- 使用Helm管理Kubernetes應用
- 構建私有Chart倉庫
- 持續集成與發布
- 使用Jenkins進行持續集成與發布
- 使用Drone進行持續集成與發布
- 更新與升級
- 手動升級Kubernetes集群
- 升級dashboard
- 領域應用概覽
- 微服務架構
- 微服務中的服務發現
- 使用Java構建微服務并發布到Kubernetes平臺
- Spring Boot快速開始指南
- Service Mesh 服務網格
- 企業級服務網格架構
- Service Mesh基礎
- Service Mesh技術對比
- 采納和演進
- 定制和集成
- 總結
- Istio
- 安裝并試用Istio service mesh
- 配置請求的路由規則
- 安裝和拓展Istio service mesh
- 集成虛擬機
- Istio中sidecar的注入規范及示例
- 如何參與Istio社區及注意事項
- Istio教程
- Istio免費學習資源匯總
- 深入理解Istio Service Mesh中的Envoy Sidecar注入與流量劫持
- 深入理解Istio Service Mesh中的Envoy Sidecar代理的路由轉發
- Linkerd
- Linkerd 使用指南
- Conduit
- Condiut概覽
- 安裝Conduit
- Envoy
- Envoy的架構與基本術語
- Envoy作為前端代理
- Envoy mesh教程
- SOFAMesh
- SOFAMesh中的Dubbo on x-protocol
- SOFAMosn
- 使用 SOFAMosn 構建 SOFAMesh
- 大數據
- Spark standalone on Kubernetes
- 運行支持Kubernetes原生調度的Spark程序
- Serverless架構
- 理解Serverless
- FaaS-函數即服務
- OpenFaaS快速入門指南
- 邊緣計算
- 人工智能