• 封装k8s sdk判断statefulset 子pod完全运行的方式


    封装k8s sdk判断statefulset 子pod完全运行的方式

    立项开发云平台,需在K8s api上封装一个sdk

    https://github.com/kubernetes-client

    调研时犹豫过使用哪种语言开发,产品业务的开发语言是java,因此为方便集成选型为java

    语言 优点
    go 原生,纯技术的话,个人倾向这点,已经一年多没写go了,想重新捡起来
    python 非原生,但考虑后期集成到airflow工作流(python)里,对airflow较为友好,看airflow的源码,其本身就继承了k8s的sdk调用
    java 非原生,但和现有的spring-boot/spring-cloud结合较好,java开发团队人员的人手比较足

    个人也负责多个k8s集群及相关组件的维护,对k8s的各特性相对较为熟悉,因此主要负责k8s层的调研和对接,向更上层的产品暴露功能

    涉及到一个技术点是如何判断'服务'成功启动,服务并不是k8s内的概念,而是产品业务上的概念

    对服务的上线形态,目前分别抽像为pod和statefulset/deploy

    对pod可以直接以status.phase: Running 判断,检测到是Running即可,pod执行异常,会根据返回的exitCode 更新pod本身的状态

    而对 statefulset/deploy 也用status.phase: Running则不能确定,因为statefulset的Running状态,只表示k8s在执行这个statefulset,但是statefulset的子pod可能并未完全都执行,或者有部分pod可能已经异常退出,现在的需求以statefulset下所有pod都执行成功才判为上线成功

    要在k8s statefulset 运行状态之上抽像出一层running的定义

    statefulset 预设的所有pod都在执行中,statefulset 预设3个pod,则必须3个pod都处在running状态statefulset才是真正的running,并且考虑到如若statefulset下pod执行异常,会在布署初期反馈异常状态,假设在pod运行1min内未有报错,则认为该pod已经稳定运行

    而考虑statefulset下多个pod的调度会有一定的执行间隔,只计数成功项,则有些之前成功的pod,因为异常已经失败了,statefulset也不能判断为成功

    判断 statefulset 完成启动成功的状态,是用status.containerStatuses.state.terminated 存在exitCode!=0 则有contain失败,整个statefulset并未完全成功

        public boolean pod_is_running_not_error(V1Pod pod) {
            var currentInfo=read_pod(pod);
            var state = _task_status(currentInfo);
            return state != State.SUCCESS && state != State.FAILED && state != State.QUEUED
                    &&_task_contain_running(currentInfo);
        }
        
    public boolean _task_contain_running(V1Pod event) {
            if (event.getStatus().getContainerStatuses().size()==0){
                return false;
            }
            var lastState=event.getStatus().getContainerStatuses().get(0);
            log.info("pod lastState {}",lastState);
            if(lastState.getLastState()!=null&&lastState.getLastState().getTerminated()!=null&&lastState.getLastState().getTerminated().getExitCode()!=0){
                log.info("pod 运行出错 {}",lastState.getLastState());
                return false;
            }
            return true;
        }
        
            public Boolean checkRunningSuccess(String namespace, String name, Integer replicas) throws Exception {
                log.info("获取 子pod {}", DEFAULT_LABEL_KEY + "=" + name);
                V1PodList v1PodList = coreV1Api.listNamespacedPod(namespace, null, null, null, null, DEFAULT_LABEL_KEY + "=" + name, null, null, 120, false);
                log.info("sub pod size {}", v1PodList.getItems().size());
                if (replicas > v1PodList.getItems().size()) {
                    log.info("副本数不足 need {},current {}", replicas, v1PodList.getItems().size());
                    return false;
                }
                boolean noHasNoReady = false;
    
                for (V1Pod item : v1PodList.getItems()) {
                    DateTime dt = DateTime.now();
                    val startTime = item.getStatus().getStartTime();
                    Period p = new Period(item.getStatus().getStartTime(), dt, PeriodType.seconds());
                    log.info("服务启动时间 {} 当前时间 {} 提交时间", startTime, dt, p);
                    log.info("time after min: {} sec: {}", p.getMinutes(), p.getSeconds());
                    if (!podOpera.pod_is_running_not_error(item)) {
                        log.info("子pod 未完全运行" + item.getMetadata().getName());
                        TimeUnit.SECONDS.sleep(30);
                        noHasNoReady = true;
                        continue;
                    }
                    if(p.getSeconds()<=60){
                        log.info("子pod 运行未超过1分钟" + item.getMetadata().getName());
                        TimeUnit.SECONDS.sleep(30);
                        noHasNoReady=true;
                        continue;
                    }
                }
                if (!noHasNoReady) {
                    log.info("子pod都已运行");
                    return true;
                }
                return false;
        }
    
    
    

    以上尝试出来的一种判断办法,暂时满足需求,或许有更高效精准的方式。

    原理是这样,即使用语言开发,判断方法也一致


    题外话,个人早期使用deploy较多,对前台后台任务都使用,但因为deploy生成的子pod名称是随机的,对维护并不友好,而statefulset生成的子pod名称是数值有序的,因此即使没有数据存储的要求,对后台任务个人目前更倾向于使用statefulset

    例如 如果是deploy服务

    查看pod日志,及进入pod内会需要至少两步(如果单pod内有多个容器,步骤又会变多)

    先查看deploy 列出pod信息,pod名称是随机的乱码,然后拷备乱码名称,排查pod

    如果是statefulset服务

    则因为statefulset服务下的pod 名称就是0,1,2

    可以直接排查pod

    不考虑其他需求的话,整体上statefulset更方便些

  • 相关阅读:
    浅谈均值、方差、标准差、协方差的概念及意义
    数据库设计的基本步骤
    VS添加lib库
    Android activity_main.xml删除边缘距离,充满屏幕
    Android 设置旋转朝向
    WebStorm注册码
    VS禁止特定警告
    VS 高亮显示不带后缀的C++头文件
    VS2010设置C++包含目录和库目录
    读取STL模型
  • 原文地址:https://www.cnblogs.com/zihunqingxin/p/14460032.html
Copyright © 2020-2023  润新知