使用Kubernetes Pod Hook 優雅的停止Pod


 

微服務中,網關會把流量分配給每個Pod節點上

1. 如果我們直接將Pod殺死,那這部分流量就無法得到正確處理,會影響部分用戶,通常來說網關或者注冊中心會將我們的服務保持一個心跳,過了心跳超時之后會自動摘除我們的服務,但是有一個問題就是超時時間可能是30秒也可能是60秒,雖然不會影響我們的系統,但是會產生用戶輕微抖動。


2. 如果我們在停止前執行一條命令,通知網關或者注冊中心這台主機進行下線,那么注冊中心就會標記這台主機已經下線,不進行流量轉發,用戶就不會有任何影響,這就是優雅停止,將滾動更新影響最小化

 

Pod Hook
Pod Hook是由kubelet發起的,當容器中的進程啟動前或者容器中的進程終止之前運行,這是包含在容器的生命周期之中。我們可以同時為Pod中的所有容器都配置hook

在k8s中,理想的狀態是pod優雅釋放,並產生新的Pod。但是並不是每一個Pod都會這么順利

Pod卡死,處理不了優雅退出的命令或者操作
優雅退出的邏輯有BUG,陷入死循環
代碼問題,導致執行的命令沒有效果

對於以上問題,k8s的Pod終止流程中還有一個"最多可以容忍的時間",即grace period (在pod的.spec.terminationGracePeriodSeconds字段定義),這個值默認是30秒,當我們執行kubectl delete的時候也可以通過--grace-period參數顯示指定一個優雅退出時間來覆蓋Pod中的配置,如果我們配置的grace period超過時間之后,k8s就只能選擇強制kill Pod

 

Kubernetes終止生命周期

1 - K8S 啟動新POD。
2 - K8S等待新POD進入Ready(Running) 狀態。
3 - K8S創建Endpoint。此時,k8s創建endpoint,將新服務納入負載均衡。
4 - 用戶刪除pod,Pod設置為”Terminating”狀態,並從所有服務的Endpoints列表中刪除。此時,Pod停止獲得新的流量。但在Pod中運行的容器不會受到影響。
5 - preStop Hook被執行。 preStop Hook是一個發送到Pod中的容器特殊命令或Http請求。
6 - SIGTERM信號被發送到Pod。 此時,Kubernetes將向pod中的容器發送SIGTERM信號。這個信號讓容器知道它們很快就會關閉。
7 - Kubernetes等待優雅的終止 此時,Kubernetes等待指定的時間稱為優雅終止寬限期。默認情況下,這是30秒。值得注意的是,這與preStop Hook和SIGTERM信號並行發生。Kubernetes不會等待preStop Hook完成。

 

 

基於PreStop環境演示

在生產環境中使用spring框架,由於服務更新過程中,服務容器被直接充值,部分請求仍被分發到終止的容器(沒有配置鈎子,熟悉默認環境),導致服務出現500錯誤,這部分錯誤請求數據占用比較少,因為Pod滾動更新都是一對一。因為部分用戶會產生服務器錯誤的情況,考慮使用優雅的終止方式,將錯誤請求降到最低,直至滾動更新不影響用戶

Eureka是一個基於REST的服務,作為Spring Cloud服務注冊中心,用於定位服務來進行中間層服務器的負載均衡和故障轉移。各服務啟動時,會向Eureka Server注冊自己的信息(IP、端口、服務信息等),Eureka Server會存儲這些信息,微服務啟動后,會周期性(默認30秒)的向Eureka Server發送心跳以續約自己的租期,並且可以從eureka中獲取其他微服務的地址信息,執行相關邏輯。。

由於Eureka默認的心跳檢測為30秒,當K8S下線Pod時Eureka會有30秒的異常問題,所以我們需要在Pod 停止前發送一條請求,通知Eureka進行下線操作,這樣進行優雅的停止對用戶的影響做到最小

apiVersion: v1
kind: Pod
metadata:
  name: abcdocker
  labels:
    name: abcdocker
spec:
  containers:
  - name: abcdocker
    image: nginx
    ports:
      - containerPort: 80
    lifecycle:
      preStop:
        exec:
          command:
            - bash
            - -c
            - 'curl -X POST --data DOWN http://127.0.0.1:8080/service-registry/instance-status  -H
              "Content-Type: application/vnd.spring-boot.actuator.v2+json;charset=UTF-8";sleep 30'

####### 參數解釋
127.0.0.1:8080 #代表eureka地址
service-registry    #代表注冊中心
DOWN        #執行down請求
sleep       #等待30秒

 

當我們刪除Pod的時候就會執行上面的命令操作,並且等待30秒

[root@yzsjhl82-135 yaml]# kubectl get pod
NAME        READY   STATUS    RESTARTS   AGE
abcdocker   1/1     Running   0          2m16s
[root@yzsjhl82-135 yaml]# kubectl delete pod abcdocker
pod "abcdocker" deleted

#此刻Pod不會馬上刪除,而是執行Exec中的命令,並等待30秒

 

配置中添加了一個sleep時間,主要是作為服務停止的緩沖時間。

 

使用nacos作為springcloud的配置和注冊中心,在k8s上設置容器生命周期鈎子preStop的最佳實踐操作

 

nacos心跳檢測時間

Nacos 目前支持臨時實例使用心跳上報方式維持活性,發送心跳的周期默認是 5 秒,Nacos 服務端會在 15 秒沒收到心跳后將實例設置為不健康,

在 30 秒沒收到心跳時將這個臨時實例摘除。這里要注意30秒這個時間。

 

nacos下線應用地址舉例:

http://192.168.0.218:8848/nacos/v1/ns/instance?serviceName=jdd-parking-cloud-admin&clusterName=DEFAULT& \
groupName=DEFAULT_GROUP&ip=172.16.246.32&port=8093&ephemeral=true&weight=1&enabled=false&namespaceId=a9076f8c-a1c7-474c-9ea4-1112677d9af7

說明:

  • 192.168.0.218:8848 nacos注冊地址
  • jdd-parking-cloud-admin 注冊的應用名稱
  • 172.16.246.32 注冊的應用名稱所在主機地址
  • 8093 注冊的應用名稱使用的端口號
  • enabled=false 下線,enabled=true 上線
  • namespaceId 命令空間,默認使用public命名空間則不寫這個

 

通過分析nacos下線應用地址,需要如下參數:nacos注冊地址,應用名稱,應用所在主機ip,應用端口號,命令空間(public不需要)

考慮到應用所在主機ip是pod ip,這個需要從pod容器中獲取,因此,不能在PreStop中使用命令行的形式,也就是如下的形式

curl -x PUT http://192.168.0.218:8848/nacos/v1/ns/instance?serviceName=jdd-parking-cloud-admin& \
clusterName=DEFAULT&groupName=DEFAULT_GROUP&ip=172.16.246.32&port=8093&ephemeral=true& \
weight=1&enabled=false&namespaceId=a9076f8c-a1c7-474c-9ea4-1112677d9af7

 

 

 原因:nacos地址可以寫死,應用名稱可以寫死,應用端口號可以寫死,但是應用所在主機ip也就是pod ip沒法獲取。
1.PreStop是配置在Daeployment中的,pod的數量和ip都是不固定的。
2.就算把pod ip設置成環境變量的形式,也只能是在pod容器中使用,在PreStop中還是獲取不到pod ip

 

 

 

 

綜合以上分析,這里采取的辦法是在構造鏡像的時候入手,新增一個preStop.sh腳本,內容寫上nacos下線的那個命令, 然后載PreStop命令行中執行這個腳本文件。

在這個過程中,若是有些參數值無法從環境變量中獲取,則需要增加這些參數的環境變量。

 

preStop腳本內容

注意腳本中的sleep 45命令,這個是確保應用從nacos中下線使用的,默認是30秒,具體看開頭nacos心跳檢測時間,sleep設置時間大於30秒就可以,這里設置45秒
preStop.sh腳本中使用的變量有些是默認提供的,有些是需要提前設置環境變量的,取值是容器中的值

#!/bin/sh

# shell腳本作用
# 在更新pod時先執行這個腳本,把pod應用從nacos中下線,然后再關閉pod

#echo "輸出必要的環境變量"
#echo "${NACOS_SERVER_ADDR}"
#echo "${PODNAME}"
#echo "${PODIP}"
#echo "${NACOS_NAMESPACE}"

result=$(curl -X PUT "http://${NACOS_SERVER_ADDR}/nacos/v1/ns/instance?serviceName=${PODNAME}&clusterName=DEFAULT&groupName=DEFAULT_GROUP&ip=${PODIP}&port=8093&enabled=false&namespaceId=${NACOS_NAMESPACE}")

echo "輸出curl執行結果result:${result}"

if [ ${result} == "ok" ]; then
  echo "執行成功"
  sleep 45
  exit 0
else
  echo "執行失敗"
  exit 1
fi

 

如上腳本中,
NACOS_SERVER_ADDR和NACOS_NAMESPACE從ConfigMap中設置中獲取,
PODIP和PODNAME是在環境變量中手動設置的

 

然后修改Dockerfile文件,增加這個preStop腳本,設置可執行權限,注意腳本放置的路徑,后面會用到

ADD preStop.sh /tmp/preStop.sh
RUN chmod 777 /tmp/preStop.sh

 

經過以上操作,項目中新增一個preStop腳本文件,把這個文件給添加到Dockerfile文件中,並放置到指定路徑下,然后提交到gitlab,自動構建docker鏡像,記住鏡像標簽。

然后在k8s中設置PreStop內容如下:

 

然后調整應用的deployment的yaml文件,把優雅終止寬限期(terminationGracePeriod限定時間)由默認的30秒調整為60秒,確保這個時間大於sleep 45的時間。

 

 

spec:
  containers:
  - name: nginx
    image: nginx
    ports:
    - name: http
      containerPort: 80
    lifecycle:
      preStop:
        exec:
          command: ['/bin/sh','-c','/tmp/preStop.sh']

 

 

 

同時更新應用使用的docker鏡像,待鏡像啟動后,增加副本數,由1增加到3,同時觀察nacos中注冊的應用數,確認顯示有3個。

然后縮減一個副本數,副本數由3變成2,注意觀察nacos中的應用是否有一個ip狀態的變成"上線"(顯示這個表示應用是下線狀態),等待30秒后就看不到這個ip應用了。
然后觀察k8s中pod的消失,等了60秒后才開始取消一個pod,最后查看事件events,發現並沒有FailedPreStopHook,這是正常的,因為只有報錯的情況下才會出現FailedPreStopHook,正常情況下不會出現這個。

 

Hook調用的日志沒有暴露給Pod的Event,所以只能到通過describe命令來獲取,如果是正常的操作是不會有event,如果有錯誤可以看到FailedPostStartHook和FailedPreStopHook這種event。並且如果Hook調用出現錯誤,則Pod狀態不會是Running

 

總結

1.pod滅亡有個優雅終止寬限期(terminationGracePeriod限定時間),默認是30秒,nacos中應用超過30秒則摘除,主要圍繞這倆時間來進行處理
2.項目中新增一個preStop.sh腳本,並添加到Dockerfile文件中,確保構造的鏡像中有這個sh文件
腳本內容是應用從nacos下面的命令,以及sleep時間,這個時間需要超過nacos默認的30秒 (pod鏡像中確保有curl命令)
3.k8s中增加sh腳本中使用到的環境變量,以便pod中sh腳本可以從pod環境中獲取這些變量的值
4.k8s中設置設置PreStop,使用命令行的方式執行如上的sh腳本
5.nacos中驗證,事件events中驗證

 

 

 

 

原文鏈接:https://blog.csdn.net/abcdocker/article/details/103054133

                  https://www.cnblogs.com/sanduzxcvbnm/p/15035174.html


免責聲明!

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



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