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的负载会自动排除异常容器,开始处理请求。
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 213,254评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,875评论 3 387
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 158,682评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,896评论 1 285
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,015评论 6 385
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,152评论 1 291
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,208评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,962评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,388评论 1 304
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,700评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,867评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,551评论 4 335
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,186评论 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,901评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,142评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,689评论 2 362
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,757评论 2 351

推荐阅读更多精彩内容