Kubernetes(k3s)進階學(xué)習(xí)(七) -- Pod狀態(tài)檢測

Kubernetes 一個重要特性是Pod具備自愈能力。當Pod發(fā)生故障后會能通過自愈機制對Pod自動重啟,另外也可以通過Liveness和Readiness的探測機制進行更精細的故障監(jiān)控排查。

先了解Kubernetes 默認的自檢機制

每個容器啟動時都會執(zhí)行一個進程,此進程由Dockerfile的CMD或ENTRYPOINT指定。如果進程退出返回碼為非0,則認為容器發(fā)生故障,Kubernetes會根據(jù)restartPolicy重啟容器。

#創(chuàng)建應(yīng)用測試

#本地終端,進入server節(jié)點容器的命令
multipass shell server

#進入容器后,先創(chuàng)建文件:pod-monitorer.yml
sudo vi pod-monitorer.yml

#pod-monitorer.yml 的內(nèi)容
apiVersion: v1
kind: Pod
metadata:
  name: monitorer
  labels:
    app: monitorer
spec:
  restartPolicy: OnFailure   #Pod的restartPolicy設(shè)置為OnFailure,默認策略為Always
  containers:
  - name: monitorer
    image: busybox
    args:
    - /bin/sh
    - -c
    - sleep 20;exit 1

執(zhí)行Pod

#導(dǎo)入yml
sudo kubectl apply -f pod-monitorer.yml

#輸出結(jié)果
pod/monitorer created

然后我們來觀察效果

#我們馬上執(zhí)行pod 的查詢,發(fā)現(xiàn)pod 正常,RESTARTS 次數(shù)為0
ubuntu@server:~$ sudo kubectl get pods -o wide
NAME                                   READY   STATUS                   RESTARTS        AGE   IP           NODE     NOMINATED NODE   READINESS GATES
monitorer                              1/1     Running                  0               16s   10.42.2.52   node2    <none>           <none>

#20秒后,我們再觀察發(fā)現(xiàn)RESTARTS次數(shù)加1了
ubuntu@server:~$ sudo kubectl get pods -o wide
NAME                                   READY   STATUS                   RESTARTS        AGE   IP           NODE     NOMINATED NODE   READINESS GATES
monitorer                              1/1     Running                  1 (19s ago)     42s   10.42.2.52   node2    <none>           <none>

每過20秒,容器就會因為進程返回非0,自動重啟1次。
容器進程返回值非0,Kubernetes認為容器發(fā)生故障,需要重啟,有不少場景下發(fā)生故障,但進程不會退出,比如訪問web服務(wù)時發(fā)生500內(nèi)部錯誤,可能是負載過高,也可能是資源死鎖,此時httpd進程并沒有異常退出,在這種情況下重啟容器可能是最直接、最有效的解決方案,處理此類場景那就用到Liveness探測。

Liveness探測

Liveness探測讓用戶可以添加自定義檢測條件,判斷容器是否健壯的情況,如果檢測失敗,Kubernetes 就會重啟Pod容器。

先建立一個測試使用的NodeJS Demo

第六章節(jié)時,我們已經(jīng)建立了一個能訪問MongoDB的NodeJS Demo,現(xiàn)在需要在這基礎(chǔ)上增加Liveness監(jiān)控的代碼。
這里我對示例做一個簡單的說明, 我希望pod 能夠每10秒時間監(jiān)控NodeJS的健康狀態(tài),我通過API:/checkLiveness 模擬獲取這個健康情況,假設(shè)改變數(shù)據(jù)表的數(shù)據(jù)來模擬崩潰了即代表NodeJS有異常,Pod 需要捕捉這個情況并且重啟。
API:/changeLivenessStatus 則是改變數(shù)據(jù)表的健康值。

#1、修改app/controller/home.js    
    //改變?nèi)萜鹘】禒顟B(tài)(測試)
    async changeLivenessStatus(){
        const status=this.ctx.paramValue('status','open');
        
        await this.ctx.app.mongo.insertOne('liveness', {
            doc: {
                status,
                create_time:(new Date()).toLocaleString()
            }
        });
    }


    //容器是否健康
    async checkLiveness(){
        const liveness=await this.app.mongo.find('liveness', {            
            sort:{
                _id:-1
            },
        });

        if(liveness.length>0)
            console.log(`new status:${liveness[0].status}`);

        if(liveness.length>0 && liveness[0].status=='close'){
            throw new Error('Catch me');
        }
        else{
            this.ctx.body="it is ok.";
        }
    }

#2、修改/app/router.js
#添加API路由  
router.get('/changeLivenessStatus',controller.home.changeLivenessStatus);
router.get('/checkLiveness', controller.home.checkLiveness);


#3、檢查代碼錯誤,這里略
#4、記得檢查是否編譯了。
npm install --production

使用Dockerfile生成鏡像(不明白的請回顧第六章節(jié))。

docker build -t k3s-test:latest .

#容器測試
docker run -p 8080:7001 --name k3s-test -e host_param="192.168.64.2" -e port_param="30017" -e db_param="db_test" -e user_param="test" -e pwd_param="test1234"  k3s-test:latest

#測試
curl http://localhost:8080

#輸出內(nèi)容
Hello K8s!%

#再連接進行檢查。
curl https://localhost:7001/changeLivenessStatus?status=open
curl https://localhost:7001/checkLiveness

后面就是回顧第二章節(jié)的內(nèi)容把鏡像 打包到鏡像倉庫,獲得一個鏡像地址。這里就不再復(fù)述了,有興趣的朋友請看回第二章節(jié) ,同時為了讀者測試方便,我把做好的鏡像放到\color{red}{registry.cn-hangzhou.aliyuncs.com/dawson-project/k3s-test-v2:latest},方便各位使用。

使用yaml 編寫Deployment

\color{red}{注:}所有的示例均在Multipass內(nèi)執(zhí)行。

#本地終端,進入server節(jié)點容器的命令
multipass shell server

#進入容器后,先創(chuàng)建文件:pod-liveness.yml
sudo vi pod-liveness.yml

#pod-liveness.yml 的內(nèi)容
apiVersion: apps/v1
kind: Pod
metadata:
  name: liveness
  labels:
    app: liveness
spec:
  restartPolicy: OnFailure
  containers:
  - name: liveness
    image: registry.cn-hangzhou.aliyuncs.com/dawson-project/k3s-test-v2:latest
    env:
      - name: host_param
        value: 192.168.64.2
      - name: port_param
        value: "30017"
      - name: db_param
        value: db_test
      - name: user_param
        value: test
      - name: pwd_param
        value: test1234

    livenessProbe:
      httpGet:
        scheme: HTTP     #指定協(xié)議,默認為HTTP
        path: /checkLiveness   #訪問路徑
        port: 7001
      periodSeconds: 5   #指每5秒執(zhí)行一次Liveness探測


#192.168.64.2 等參數(shù)為本人測試數(shù)據(jù)庫地址。

引入yaml 文件

ubuntu@server:~$ sudo kubectl apply -f pod-liveness.yml 
pod/liveness created
觀察Liveness 監(jiān)控效果吧

我們通過kubectl 監(jiān)控pod時,pod 運行一切正常。

ubuntu@server:~$ sudo kubectl get pods -o wide
NAME                                   READY   STATUS    RESTARTS        AGE    IP           NODE    NOMINATED NODE   READINESS GATES
liveness-deployment-684f6f6f66-wjpr2   1/1     Running   0               11m    10.42.1.81   node1   <none>           <none>

接下來,我們試著往數(shù)據(jù)表liveness 插入一條關(guān)閉命令。

#調(diào)用pod 的關(guān)閉命令
ubuntu@server:~$ curl http://10.42.1.81:7001/changeLivenessStatus?status=close

大約過了20秒后,我們再重新監(jiān)控pod 時,RESTARTS的次數(shù)已經(jīng)改變了。證明Liveness 監(jiān)控到應(yīng)用發(fā)生了異常,激活了重啟機制。

NAME                                   READY   STATUS            RESTARTS      AGE    IP           NODE    NOMINATED NODE   READINESS GATES
liveness-deployment-684f6f6f66-wjpr2   1/1     CrashLoopBackOff   1 (24s ago)   14m    10.42.1.81   node1   <none>           <none>

Readiness探測

與Liveness探測有所區(qū)別。Readiness探測告訴Kubernetes什么時候可以將Pod容器加入到Service負載均衡池中,對外提供服務(wù)。
這兩種探測配置方法完全一樣,支持的配置參數(shù)也一樣,不同在于探測失敗后,Liveness探測是重啟容器,Readiness探測則將容器設(shè)置為不可用,不接收service轉(zhuǎn)發(fā)的請求。

調(diào)整NodeJS Demo

與Liveness探測的Demo一致,我們先調(diào)整代碼,加上可以專屬Readiness的監(jiān)管的代碼。

#1、修改app/controller/home.js    
    //獲取本機ip
    getLocalIPAddress(){
        let interfaces=os.networkInterfaces();
        for(let devName in interfaces){
            let iface=interfaces[devName];
            for(let alias of iface){
                if(alias.family=== 'IPv4' && alias.family!='127.0.0.1' && !alias.internal){
                    return alias.address;
                }
            }
        }
        return 'no address';
    }

    //改變?nèi)萜骷尤氲絪ervice的狀態(tài)(測試)
    async changeReadinessStatus(){
        const status=this.ctx.paramValue('status','open');
        
        await this.ctx.app.mongo.insertOne('rediness', {
            doc: {
                ip:this.getLocalIPAddress(),
                status,
                create_time:(new Date()).toLocaleString()
            }
        });
    }


    //容器是否允許加入到service(測試)
    async checkReadiness(){
        const rediness=await this.app.mongo.find('rediness', {            
            query:{
                ip:this.getLocalIPAddress()
            },
            sort:{
                _id:-1
            },
        });

        if(rediness.length>0)
            console.log(`new status:${rediness[0].status}`);

        if(rediness.length>0 && rediness[0].status=='close'){
            throw new Error('Catch me');
        }
        else{
            this.ctx.body="it is ok.";
        }
    }

#2、修改/app/router.js
#添加API路由  
router.get('/changeReadinessStatus',controller.home.changeReadinessStatus);
router.get('/checkReadiness', controller.home.checkReadiness);


#3、檢查代碼錯誤,這里略
#4、記得檢查是否編譯了。
npm install --production

然后打包到鏡像倉庫。

使用yaml編寫Deployment
#本地終端,進入server節(jié)點容器的命令
multipass shell server

#進入容器后,先創(chuàng)建文件:pod-readiness.yml
sudo vi pod-readiness.yml

#pod-readiness.yml的內(nèi)容

apiVersion: apps/v1beta2
kind: Deployment
metadata:
  name: readiness-deployment
spec:
  replicas: 2
  template:
    metadata:
      labels:
        app: readiness
    spec:
      containers:
      - name: readiness
        image: registry.cn-hangzhou.aliyuncs.com/dawson-project/k3s-test-v2:latest
        env:
          - name: host_param
            value: 192.168.64.2
          - name: port_param
            value: "30017"
          - name: db_param
            value: db_test
          - name: user_param
            value: test
          - name: pwd_param
            value: test1234
        ports:
        - containerPort: 7001
        readinessProbe:
          httpGet:
            scheme: HTTP     #指定協(xié)議,默認為HTTP
            path: /checkReadiness   #訪問路徑
            port: 7001
          periodSeconds: 10        #每10秒進行一次檢測
---
apiVersion: v1
kind: Service
metadata:
  name: readiness-service
spec:
  selector:
    app: readiness
  ports:
   - protocol: TCP
     port: 7001
     targetPort: 80

#192.168.64.2 等參數(shù)為本人測試數(shù)據(jù)庫地址。

導(dǎo)入yaml文件

ubuntu@server:~$ sudo kubectl apply -f pod-readiness.yml 
deployment.apps/readiness-deployment created
service/readiness-service created

測試場景的邏輯大致是這樣:

  • 容器啟動后開始探測。如果http://[container_ip]:7001/checkReadiness返回碼不是200~400,表示容器沒有就緒,不接收Service web-svc的請求。開始時都會正常返回200
  • 然后我們可以手動調(diào)整某些Pod的狀態(tài),只要調(diào)用http://[container_ip]:7001/changeReadinessStatus?status=close即可。
  • 容器會每隔10秒再探測一次
  • 直到某個容器的返回碼為非200后,模擬出容器在崩潰狀態(tài),web-svc的負載會自動排除異常容器,開始處理請求。
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容