ASP.NET Core on K8S深入學習(6)Health Check


本篇已加入《.NET Core on K8S學習實踐系列文章索引》,可以點擊查看更多容器化技術相關系列文章。

一、關於K8S中的Health Check

  所謂Health Check,就是健康檢查,即防微杜漸。K8S是一個編排引擎可以幫助我們快捷地部署容器集群,如果部署上錯誤的容器導致服務崩潰,通常情況下我們都會通過一些高可用機制進行故障轉移。但是,前提條件是有健康檢查。

  K8S自然幫我們考慮到了這個問題,健康檢查是K8S的重要特性之一,默認有健康檢查機制,此外還可以主動設置一些自定義的健康檢查。

  默認情況下,每個容器啟動時都會執行一個進程,由Dockerfile中的CMD或ENTRYPOINT指定。如果進程退出時的返回碼不為0,則認為容器發生了故障,K8S會根據重啟策略(restartPolicy)重啟容器。

  例如下面這個例子,它模擬了容器發生故障的場景,注意下面配置文件中的args選項的定義:

apiVersion: v1
kind: Pod
metadata:
  name: edc-healthcheck-demo
  labels:
    test: healthcheck
spec:
  restartPolicy: OnFailure
  containers:
    - name: healthcheck
      image: busybox
      imagePullPolicy: IfNotPresent
      args:
      - /bin/sh
      - -c
      - sleep 10; exit 1

  其中 sleep 10; exit 1代表啟動10秒之后就非正常退出(返回碼不為0),然后通過kubectl創建Pod:

kubectl apply -f health-check.yaml

  過一段時間后查看Pod的狀態,如下圖所示:

  

   可以看到,該容器已經重啟了2次。也可以看出,restartPolicy簡單直接暴力有效,不由感嘆重啟大法好!

  但是,也要正視一個問題:必須等到進程退出后的返回值是非零才會觸發重啟策略,不能直接監測容器是否是健康

  那么,K8S中有沒有更好的機制能夠實現智能一點的健康檢查呢?答案就是使用Liveness與Readinesss。

二、Liveness探測

2.1 Liveness初體驗

一句話Liveness:如果檢測有問題(如果健康檢查失敗),重啟pod!至於怎么檢測,你說了算(自定義判斷容器是否健康的條件)!  

  Liveness提供了一些重要的參數:

initialDelaySeconds:容器啟動后第一次執行探測是需要等待多少秒,看運行的服務而定。
periodSeconds:執行探測的頻率,默認是10秒,最小1秒。
timeoutSeconds:探測超時時間,默認1秒,最小1秒。
successThreshold:探測失敗后,最少連續探測成功多少次才被認定為成功,默認是1,對於liveness必須是1,最小值是1。
failureThreshold:探測成功后,最少連續探測失敗多少次才被認定為失敗。默認是3。最小值是1.

  下面實踐一個小例子創建一個Pod:

#command自己定義,例子為 /tmp/healthy 不存在則認為pod有問題,大家根據實際業務來自定義。
apiVersion: v1
kind: Pod
metadata:
  labels:
    test: liveness
  name: liveness-demo
spec:
  containers:
  - name: liveness
    image: busybox
    args:
    - /bin/sh
    - -c
    - touch /tmp/healthy; sleep 30; rm -rf/tmp/healthy; sleep 10
    livenessProbe:
      exec:
        command:
        - cat
        - /tmp/healthy
      initialDelaySeconds: 10
      periodSeconds: 5

  這里啟動pod后會創建文件夾 /tmp/healthy,30秒后刪除,在我們的設置中,如果 /tmp/healthy 存在,則認為容器處於正常狀態,否則認為發生故障。

  需要注意的就是livenessProbe部分的定義了:

  (1)探測方法:通過cat命令查看/tmp/healthy是否存在;如果返回值為0,則探測成功;否則,探測失敗;

  (2)initialDelaySeconds: 10 => 容器啟動10秒之后開始執行liveness探測;

  (3)periodSeconds: 5 => 每5秒執行一次liveness探測;如果連續執行3次探測都失敗,那么就會殺掉並重啟容器;

  下面快速地驗證一下:

  (1)kubectl創建demo

kubectl apply -f liveness-demo.yaml

  (2)查看pod日志

kubectl describe pod liveness-demo

  結果如下圖所示:

  

   30秒之后,/tmp/healthy 被刪除了,liveness探測失敗,又過了幾十秒,重復探測均失敗后,開啟了重啟容器。

  

2.2 Liveness探針

  上面的例子使用的是Liveness的exec探針,此外K8S還有幾種其他類型的探針:

  • exec:在容器中執行一個命令,如果命令退出碼返回0則表示探測成功,否則表示失敗
  • tcpSocket:對指定的容IP及端口執行一個TCP檢查,如果端口是開放的則表示探測成功,否則表示失敗
  • httpGet:對指定的容器IP、端口及路徑執行一個HTTP Get請求,如果返回的狀態碼在 [200,400)之間則表示探測成功,否則表示失敗

  針對tcpSocket的例子:這里會檢測80端口是否可以正常訪問;

#檢測80端口是否聯通
apiVersion: v1
kind: Pod
metadata:
  labels:
    test: readiness
  name: readiness-tcp
spec:
  containers: 
  - name: readiness
    image: nginx
    readinessProbe:
      failureThreshold: 3
      tcpSocket:
        port: 80
      initialDelaySeconds: 10
      periodSeconds: 10
      successThreshold: 1
      timeoutSeconds: 10

  針對httpGet的例子:這里會檢測index.html文件是否可以正常訪問;

#訪問80端口的index.html文件是否存在
apiVersion: v1
kind: Pod
metadata:
  labels:
    test: readiness
  name: readiness-httpget
spec:
  containers:
  - name: readiness
    image: nginx
    readinessProbe:
      failureThreshold: 3
      httpGet:
        path: /index.html
        port: 80
        scheme: HTTP
      initialDelaySeconds: 10
      periodSeconds: 10
      successThreshold: 1
      timeoutSeconds: 10

三、Readiness探測

3.1 Readiness初體驗

一句話Readiness:如果檢查失敗,K8S會將該Pod從服務代理的分發后端去除,不再讓其接客(分發請求給該Pod)。如果檢測成功,那么K8S就會將容器加入到分發后端,重新對外接客(對外提供服務)。  

  下面繼續以上面Liveness的例子來實踐一下:

apiVersion: v1
kind: Pod
metadata:
  labels:
    test: readiness
  name: readiness-demo
spec:
  containers:
  - name: readiness
    image: busybox
    args:
    - /bin/sh
    - -c
    - touch /tmp/healthy; sleep 30; rm -rf/tmp/healthy; sleep 10
    readinessProbe:
      exec:
        command:
        - cat
        - /tmp/healthy
      initialDelaySeconds: 10
      periodSeconds: 5

  readinessProbe的配置語法與livenessProbe完全一致,但執行后的效果卻不一樣,見下圖所示:

  

  可以看出:

  (1)剛被創建時,其READY狀態為不可用;

  (2)15秒(initialDelaySeconds + periodSeconds = 10 + 5 = 15)之后,第一次進行Readiness探測成功,其READY狀態變為可用。

  (3)30秒之后,/tmp/healthy被刪除,連續3次Readiness探測均失敗后,其READY狀態又變為了不可用。

  此外,我們也可以通過 kubectl describe pod readiness-demo 查看到更想起的日志信息。

3.2 與Liveness的對比

  Liveness與Readiness都是K8S的Health Check機制,Liveness探測是重啟容器,而Readiness探測則是將容器設置為不可用,不讓其再接受Service轉發的請求。

  Liveness與Readiness是獨立執行的,二者無依賴,可以單獨使用也可以同時使用。

四、Health Check在K8S中的應用

4.1 在Scale Up中的應用

  對於多副本應用,當執行Scale Up操作時,新的副本會作為后端服務加入到Service的負載均衡列表中。但是,很多時候應用的啟動都需要一定的時間做准備(比如加載緩存、連接數據庫等等),這時我們可以通過Readiness探測判斷容器是否真正就緒,從而避免將請求發送到還未真正就緒的后端服務。

  下面一個示例YAML配置文件定義了Readiness探測,重點關注readinessProbe部分:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: edc-webapi-deployment
  namespace: aspnetcore
spec:
  replicas: 2
  selector:
    matchLabels:
      name: edc-webapi
  template:
    metadata:
      labels:
        name: edc-webapi
    spec:
      containers:
      - name: edc-webapi-container
        image: edisonsaonian/k8s-demo:1.2
        ports:
        - containerPort: 80
        imagePullPolicy: IfNotPresent
        readinessProbe: httpGet: scheme: HTTP path: /api/health port: 80 initialDelaySeconds: 10 periodSeconds: 5

---

apiVersion: v1
kind: Service
metadata:
  name: edc-webapi-service
  namespace: aspnetcore
spec:
  type: NodePort
  ports:
    - nodePort: 31000 
      port: 8080
      targetPort: 80
  selector:
    name: edc-webapi

  對於readinessProbe部分:

  (1)schema指定了協議,這里是HTTP協議,也可以是HTTPS協議;

  (2)path指定訪問路徑,這里是我們自定義的一個Controller中的接口:簡單地返回一個狀態碼為200的響應;

    [Produces("application/json")]
    [Route("api/Health")]
    public class HealthController : Controller
    {
        [HttpGet]
        public IActionResult Get() => Ok("ok");
    }

  (3)port指定端口,這里是容器的端口80;

  (4)initialDelaySeconds和periodSeconds指定了容器啟動10秒之后開始探測,然后每隔5秒執行探測,如果發生3次以上探測失敗,則該容器會從Service的負載均衡中移除,直到下次探測成功后才會重新加入。

4.2 在Rolling Update中的應用

  假設現在有一個正常運行的多副本應用,我們要對其進行滾動更新即Rolling Update,K8S會逐步用新Pod替換舊Pod,結果就有可能發生這樣的一個場景:當所有舊副本被替換之后,而新的Pod由於人為配置錯誤一直無法啟動,因此整個應用將無法處理請求,無法對外提供服務,后果很嚴重!

  因此,Readiness探測還提供了用於避免滾動更新中出現這種情況的一些解決辦法,比如maxSurge和maxUnavailable兩個參數,用來控制副本替換的數量。

  繼續以上面的YAML配置文件為例,重點關注strategy部分:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: edc-webapi-deployment
  namespace: aspnetcore
spec:
  strategy: rollingupdate: maxSurge: 25% maxUnavailable: 25%
  replicas: 10
  selector:
    matchLabels:
      name: edc-webapi
  template:
    metadata:
      labels:
        name: edc-webapi
    spec:
      containers:
      - name: edc-webapi-container
        image: edisonsaonian/k8s-demo:1.2
        ports:
        - containerPort: 80
        imagePullPolicy: IfNotPresent
        readinessProbe:
          httpGet:
            scheme: HTTP
            path: /api/health
            port: 80
          initialDelaySeconds: 10
          periodSeconds: 5

---

apiVersion: v1
kind: Service
metadata:
  name: edc-webapi-service
  namespace: aspnetcore
spec:
  type: NodePort
  ports:
    - nodePort: 31000 
      port: 8080
      targetPort: 80
  selector:
    name: edc-webapi

  (1)maxSurge : 25% => 控制滾動更新過程中副本總數超過預期(這里預期是10個副本 replicas: 10)的上限,可以是數值也可以是百分比,然后向上取整。這里寫的百分比,默認值是25%;

  如果預期副本數為10,那么副本總數的最大值為RoundUp(10 + 10 * 25%)=13個。

  (2)maxUnavailable : 25% => 控制滾動更新過程中不可用的副本(這里預期是10個副本 replicas: 10)占預期的最大比例,可以是數值也可以是百分比,然后向下取整,同樣地默認值也是25%;

  如果預期副本總數為10,那么可用的副本數至少要為10-roundDown(10 * 25%)=10-2=8個。

  綜上看來,maxSurge的值越大,初始創建的新副本數量就越多;maxUnavaliable值越大,初始銷毀的舊副本數量就越多;

五、小結

  本文探索了K8S中的默認健康檢查機制以及Liveness和Readiness兩種各有特點的探測機制,並通過一些小例子進行了說明。不過由於筆者也是初學,對於這一塊沒有過多實踐經驗,因此也是講的比較粗淺,也希望以后能夠有更多的實際經驗分享與各位。

參考資料

(1)CloudMan,《每天5分鍾玩轉Kubernetes

(2)李振良,《一天入門Kubernets教程

(3)馬哥(馬永亮),《Kubernetes快速入門

(4)華仔,《[譯]Kubernetes最佳實踐:使用Readiness和Liveness探測做Health Check

(5)benjanmin楊,《K8S中的Health Check

(6)條子在洗澡,《K8S健康性檢查-探測

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM