Kubernetes(k3s)进阶学习(七) -- Pod状态检测

Kubernetes 一个重要特性是Pod具备自愈能力。当Pod发生故障后会能通过自愈机制对Pod自动重启,另外也可以通过Liveness和Readiness的探测机制进行更精细的故障监控排查。

先了解Kubernetes 默认的自检机制

每个容器启动时都会执行一个进程,此进程由Dockerfile的CMD或ENTRYPOINT指定。如果进程退出返回码为非0,则认为容器发生故障,Kubernetes会根据restartPolicy重启容器。

#创建应用测试

#本地终端,进入server节点容器的命令
multipass shell server

#进入容器后,先创建文件:pod-monitorer.yml
sudo vi pod-monitorer.yml

#pod-monitorer.yml 的内容
apiVersion: v1
kind: Pod
metadata:
  name: monitorer
  labels:
    app: monitorer
spec:
  restartPolicy: OnFailure   #Pod的restartPolicy设置为OnFailure,默认策略为Always
  containers:
  - name: monitorer
    image: busybox
    args:
    - /bin/sh
    - -c
    - sleep 20;exit 1

执行Pod

#导入yml
sudo kubectl apply -f pod-monitorer.yml

#输出结果
pod/monitorer created

然后我们来观察效果

#我们马上执行pod 的查询,发现pod 正常,RESTARTS 次数为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秒后,我们再观察发现RESTARTS次数加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认为容器发生故障,需要重启,有不少场景下发生故障,但进程不会退出,比如访问web服务时发生500内部错误,可能是负载过高,也可能是资源死锁,此时httpd进程并没有异常退出,在这种情况下重启容器可能是最直接、最有效的解决方案,处理此类场景那就用到Liveness探测。

Liveness探测

Liveness探测让用户可以添加自定义检测条件,判断容器是否健壮的情况,如果检测失败,Kubernetes 就会重启Pod容器。

先建立一个测试使用的NodeJS Demo

第六章节时,我们已经建立了一个能访问MongoDB的NodeJS Demo,现在需要在这基础上增加Liveness监控的代码。
这里我对示例做一个简单的说明, 我希望pod 能够每10秒时间监控NodeJS的健康状态,我通过API:/checkLiveness 模拟获取这个健康情况,假设改变数据表的数据来模拟崩溃了即代表NodeJS有异常,Pod 需要捕捉这个情况并且重启。
API:/changeLivenessStatus 则是改变数据表的健康值。

#1、修改app/controller/home.js    
    //改变容器健康状态(测试)
    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生成镜像(不明白的请回顾第六章节)。

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

#输出内容
Hello K8s!%

#再连接进行检查。
curl https://localhost:7001/changeLivenessStatus?status=open
curl https://localhost:7001/checkLiveness

后面就是回顾第二章节的内容把镜像 打包到镜像仓库,获得一个镜像地址。这里就不再复述了,有兴趣的朋友请看回第二章节 ,同时为了读者测试方便,我把做好的镜像放到\color{red}{registry.cn-hangzhou.aliyuncs.com/dawson-project/k3s-test-v2:latest},方便各位使用。

使用yaml 编写Deployment

\color{red}{注:}所有的示例均在Multipass内执行。

#本地终端,进入server节点容器的命令
multipass shell server

#进入容器后,先创建文件:pod-liveness.yml
sudo vi pod-liveness.yml

#pod-liveness.yml 的内容
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     #指定协议,默认为HTTP
        path: /checkLiveness   #访问路径
        port: 7001
      periodSeconds: 5   #指每5秒执行一次Liveness探测


#192.168.64.2 等参数为本人测试数据库地址。

引入yaml 文件

ubuntu@server:~$ sudo kubectl apply -f pod-liveness.yml 
pod/liveness created
观察Liveness 监控效果吧

我们通过kubectl 监控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>

接下来,我们试着往数据表liveness 插入一条关闭命令。

#调用pod 的关闭命令
ubuntu@server:~$ curl http://10.42.1.81:7001/changeLivenessStatus?status=close

大约过了20秒后,我们再重新监控pod 时,RESTARTS的次数已经改变了。证明Liveness 监控到应用发生了异常,激活了重启机制。

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探测有所区别。Readiness探测告诉Kubernetes什么时候可以将Pod容器加入到Service负载均衡池中,对外提供服务。
这两种探测配置方法完全一样,支持的配置参数也一样,不同在于探测失败后,Liveness探测是重启容器,Readiness探测则将容器设置为不可用,不接收service转发的请求。

调整NodeJS Demo

与Liveness探测的Demo一致,我们先调整代码,加上可以专属Readiness的监管的代码。

#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';
    }

    //改变容器加入到service的状态(测试)
    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节点容器的命令
multipass shell server

#进入容器后,先创建文件:pod-readiness.yml
sudo vi pod-readiness.yml

#pod-readiness.yml的内容

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     #指定协议,默认为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 等参数为本人测试数据库地址。

导入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
  • 然后我们可以手动调整某些Pod的状态,只要调用http://[container_ip]:7001/changeReadinessStatus?status=close即可。
  • 容器会每隔10秒再探测一次
  • 直到某个容器的返回码为非200后,模拟出容器在崩溃状态,web-svc的负载会自动排除异常容器,开始处理请求。
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容