封装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更方便些

原文地址:https://www.cnblogs.com/zihunqingxin/p/14460032.html