• Java操作k8s api示例:使用kubeconfig文件认证;获取所有pod;获取pod内应用容器的启动日志


    公司准备将应用容器化部署,先使用了华为云的 Kubernetes 服务,后面又使用阿里云的 Kubernetes 服务。并短期一个月内无法判断走哪个云商。而作为一个在公司内部用于应用发布,部署的应用。在对接完华为云的 Kubernetes 服务 Api 后。再对接阿里云发现阿里云并没用像华为云一样对 Kubernetes 的 Api 做简易的封装。其两者的区别是华为云可以通过 ak , sk 再加 Kubernetes Api 获取数据。可以理解为华为云多了一层代理。

    添加依赖

    本篇使用的是官方维护的 Kubernetes Java Client 包 。有兴趣的可以了解下面的社区维护版

    官方 SDK

    官方 JAVA SDK GitHub

    建议使用最新版本 maven 中央仓库 :

    <dependency>
        <groupId>io.kubernetesgroupId>
        <artifactId>client-javaartifactId>
        <version>16.0.0version>
    dependency>
    
    • 1
    • 2
    • 3
    • 4
    • 5

    准备 kubeconfig 文件

    用于配置集群访问的文件称为 kubeconfig 文件。默认情况下, kubectl 在 $HOME/.kube 目录下查找名为 config 的文件。什么是 kubeconfig

    阿里云

    容器服务 - Kubernetes 》 进入集群 》 集群信息 》 连接信息。复制内容

    华为云

    云容器引擎 》 资源管理 》 集群管理 》 进入集群 》 基本信息右边 kubectl 》 下载 kubectl 配置文件

    可以使用 lens 工具管理 Kubernetes 集群

    Java 连接 Kubernetes

    首先将这个内容文件重命名为 config 。

    使用:官方例子获取所有 NameSpaces 下的 Pod。

    package io.kubernetes.client.examples;
    
    import io.kubernetes.client.ApiClient;
    import io.kubernetes.client.ApiException;
    import io.kubernetes.client.Configuration;
    import io.kubernetes.client.apis.CoreV1Api;
    import io.kubernetes.client.models.V1Pod;
    import io.kubernetes.client.models.V1PodList;
    import io.kubernetes.client.util.ClientBuilder;
    import io.kubernetes.client.util.KubeConfig;
    import java.io.FileReader;
    import java.io.IOException;
    
    /**
     * A simple example of how to use the Java API from an application outside a kubernetes cluster
     *
     * 

    Easiest way to run this: mvn exec:java * -Dexec.mainClass="io.kubernetes.client.examples.KubeConfigFileClientExample" * */ public class KubeConfigFileClientExample { public static void main(String[] args) throws IOException, ApiException { //file path to your KubeConfig (看你自己 config 文件放哪) String kubeConfigPath = "~/.kube/config"; // loading the out-of-cluster config, a kubeconfig from file-system ApiClient client = ClientBuilder.kubeconfig(KubeConfig.loadKubeConfig(new FileReader(kubeConfigPath))).build(); // set the global default api-client to the in-cluster one from above Configuration.setDefaultApiClient(client); // the CoreV1Api loads default api-client from global configuration. CoreV1Api api = new CoreV1Api(); // invokes the CoreV1Api client V1PodList list = api.listPodForAllNamespaces(null, null, null, null, null, null, null, null, null); System.out.println("Listing all pods: "); for (V1Pod item : list.getItems()) { System.out.println(item.getMetadata().getName()); } } }

    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44

    但我不想使用第三方云存储如阿里云的 OSS 或者在 Jar 包的相对路径下放置一份 config 文件。所以我会把这个文件放在 Jar 包内。也就是我放在 resources 下的自建目录 kubernetes 下,所以改动如下。

    // 注意导包不要导错了
    // import sun.security.util.Resources;
    BufferedReader reader = new BufferedReader(new InputStreamReader(Resources.class.getResourceAsStream("/kubernetes/config")));
    ApiClient client = ClientBuilder.kubeconfig(KubeConfig.loadKubeConfig(reader)).build();
    
    • 1
    • 2
    • 3
    • 4

    解释:因为当打成 Jar 包后,文件就在包内部了。

    而 new FileReader(path) 实例化内部底层会 new File(path) 再获取 InputStream 。而 File 这个路径是相对于包的相对路径,并不是指向包内的文件。如果继续使用会出现 idea 跑项目时没有问题,打包部署时出错

    初始化 ApiClient

    因为有四个环境,不能像官方例子使用 Configuration.setDefaultApiClient() 设置默认 client

    public class KubernetesUtil {
      public final static Map alyClusterMap = new HashMap(){
        {
          try {
             BufferedReader reader = IoUtil.getReader(new ClassPathResource("kubernetes/aly-dev-config").getInputStream(), "utf-8");
             put("DEV",ClientBuilder.kubeconfig(KubeConfig.loadKubeConfig(reader)).build());
             put("TEST",null);
             put("PRE",null);
             put("PROD",null);
          } catch (IOException e) {
             e.printStackTrace();
          }
        }
      };
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    例:获取 pod 信息

    首先通过 Client 获取 CoreV1Api 对象用于调用相关接口

    这里要提一句:Java Kubernetes SKD 操作 Api 不仅只有 CoreV1Api 这个类。如操作 Deployment 时用的是 AppsV1Api . 使用什么我是先到 Kubernetes Api 下找。然后将请求路径在 SDK 源码中查。如果有更好的方法,还请多多提点下 (●’◡’●)

    public static List getPodsByAppName(String appName,String env) {
        // 按需获取所需要的 Client 
        CoreV1Api api = new CoreV1Api(KubernetesUtil.alyClusterMap.get(env));
        V1PodList list = null;
        try {
            list = api.listPodForAllNamespaces(null,null, null, "name="+appName+"-pod", null, null, null, null, null, null);
        } catch (ApiException e) {
            e.printStackTrace();
        }
        // JSONbject 是 Map 对象的实现类。来源是 Hutool 工具
        List jsonObjectList = list.getItems().parallelStream().map(m -> {
            JSONObject jsonObject = new JSONObject();
            jsonObject.putOnce("podName", m.getMetadata().getName());
            jsonObject.putOnce("podIp", m.getStatus().getPodIP());
            V1Container v1Container = m.getSpec().getContainers().get(0);
            String[] images = v1Container.getImage().split(":");
            jsonObject.putOnce("imageVersion", images[images.length - 1]);
            // 1048576 = 1024 * 1024 因为单位为 Byte 要转换为 MB。 因为获取到的是字节,需要转成 Mb
            jsonObject.putOnce("specification", String.format("CPU:%s | MEM:%s",
    			v1Container.getResources().getLimits().get("cpu").getNumber(),
            	v1Container.getResources().getLimits().get("memory").getNumber().divide(new BigDecimal(1048576))));
            jsonObject.putOnce("podStatus", m.getStatus().getPhase());
            Optional.ofNullable(m.getStatus().getStartTime()).ifPresent(p -> {
                // 时间是 UTC 的零时区。不是东八区
                jsonObject.putOnce("startTime", p.plusHours(8).format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
            });
            return jsonObject;
        }).collect(Collectors.toList());
        return jsonObjectList;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30

    例:获取 pod 内 Java 启动日志

    因为业务要求日志是从上往下看的。所以日志这块 Kubernetes 的参数有点不搭,所以就用 sinceSeconds 。意思是距离现在最近的多少秒日志

    接口详情

    public static StartLogVO getAppServerLogs(int startDate,String env,String podName,String endLog){
        CoreV1Api api = new CoreV1Api(KubernetesUtil.alyClusterMap.get(env));
        StartLogVO startLogVO = new StartLogVO("",0,null);
        try {
            int now = (int)LocalDateTime.now().toEpochSecond(ZoneOffset.of("+8"));
            startLogVO.setStartDate(now +"");
            // 加 5 秒防止日志丢失
            int i =  now - startDate + 5;
            String logs = api.readNamespacedPodLog(podName, "default", null, null, null, null, "true", null, i, null, null);
            startLogVO.setLineNum(logs.substring(logs.length()-20));
    		// 去除重复日志
            if(StrUtil.isNotBlank(endLog)){
                int end = logs.indexOf(endLog);
                if(end!=-1){
                    logs = StrUtil.removePrefix(logs.substring(end),endLog);
                }
            }
            startLogVO.setLogs(Optional.ofNullable(logs).orElse(""));
        } catch (ApiException e) {
            e.printStackTrace();
        }
        return startLogVO;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    总结

    因为华为云通过 ak , sk 再加 Kubernetes Api 请求地址。我这边仅需发起 HTTPS 请求就行。以至于对接阿里云还需要使用 Kubernetes 的 SDK 有些抗拒。还要载入 kubeconfig 文件。最后不想学阿里云例子通过调用系统命令 curl 。所以还是使用了,结果第一次使用就报 NoSuchMethodError kotlin.collections.ArraysKt.copyInto([B[BIII)[B 这个错误,一看报没方法错误,赶紧看下 Mavne 引入的依赖版本。原来是公司父级定义了旧版本,导致引入的最新 SDK 版本内的 Kotlin 版本被覆盖了。于是在最外层 pom.xml 下重新定义了版本约束。之后就是开发的小细节问题了。

    感觉 Kubernetes 的 SDK 包还是很厉害的。下次也不知道有没有机会搞个 WebShell 连下 Pod 。


    参考链接:https://guicai.work/after-post/java/Java%E6%93%8D%E4%BD%9CKubernetes

  • 相关阅读:
    java计算机毕业设计高校网上报销系统MyBatis+系统+LW文档+源码+调试部署
    如何用一颗芯片实现5V转正负12V
    java计算机毕业设计基于ssm的医院门诊互联电子病历管理信息系统(源代码+数据库+Lw文档)
    微服务架构的可观察性设计模式
    微信小程序开发07 数据监控:善用数据驱动产品迭代
    Kafka 基础与架构理解
    JSP 购物商城系统eclipse定制开发mysql数据库BS模式java编程servlet
    作为外贸业务员,为什么我经常随机轻松 就“捡“到精准潜在客户
    ES6
    CodeForces Round #829 (div.2) A~C2
  • 原文地址:https://blog.csdn.net/a772304419/article/details/126725026