1、pom.xml文件中引入依赖

<!--k8s相关jar包-->
		<dependency>
			<groupId>io.kubernetes</groupId>
			<artifactId>client-java</artifactId>
			<version>8.0.2</version>
			<scope>compile</scope>
		</dependency>

2、逻辑流程

大致流程
 * 1)创建一个命名空间
 * 2)根据资源对象文件创建deployment,创建多个副本,并指定自定义调度器
 * 3)通过API获取节点和pod
 * 4)选择phase=pending和schedulerName=my-scheduler的pod
 * 5)然后通过自定义的调度算法(这里只采用随机数的方法)计算出pod的合适目标节点。并使用Binding对象去绑定。

3、配置连接k8s
在这里插入图片描述
在这里插入图片描述
4、项目工程目录如下
在这里插入图片描述
只需要关注config文件、myScheduler包、uitl包即可。
5、util包中工具类是配置连接客户端

package com.example.demo.util;


import io.kubernetes.client.openapi.ApiClient;
import io.kubernetes.client.openapi.Configuration;
import io.kubernetes.client.openapi.auth.ApiKeyAuth;
import io.kubernetes.client.util.ClientBuilder;
import io.kubernetes.client.util.KubeConfig;

import java.io.FileReader;
import java.io.IOException;

/**
 * 配置信息
 */
public class Config {
    // 返回client
    // 默认使用这个方法
    public static ApiClient  defaultClient() throws IOException {

        String kubeConfigPath = "config";
        //
        ApiClient client =
                ClientBuilder.kubeconfig(KubeConfig.loadKubeConfig(new FileReader(kubeConfigPath))).build();
        return client;

    }

}

6、在主类中实现,具体代码如下

package com.example.demo.myScheduler;

import com.example.demo.util.Config;
import io.kubernetes.client.ProtoClient;
import io.kubernetes.client.openapi.ApiClient;
import io.kubernetes.client.openapi.ApiException;
import io.kubernetes.client.openapi.Configuration;
import io.kubernetes.client.openapi.apis.AppsV1Api;
import io.kubernetes.client.openapi.apis.AppsV1beta1Api;
import io.kubernetes.client.openapi.apis.CoreV1Api;
import io.kubernetes.client.openapi.apis.ExtensionsV1beta1Api;
import io.kubernetes.client.openapi.models.*;
import io.kubernetes.client.proto.Meta;
import io.kubernetes.client.proto.V1;
import io.kubernetes.client.util.Yaml;

import java.io.File;
import java.io.IOException;
import java.util.List;
import java.util.Random;

/**
 * 大致流程
 * 1)创建一个命名空间
 * 2)根据资源对象文件创建deployment,创建多个副本,并指定自定义调度器
 * 3)通过API获取节点和pod
 * 4)选择phase=pending和schedulerName=my-scheduler的pod
 * 5)然后通过自定义的调度算法(这里只采用随机数的方法)计算出pod的合适目标节点。并使用Binding对象去绑定。
 */
public class myScheduler {

    public static File deploymentFile = new File("test-scheduler.yaml");

    public static void main(String[] args) throws IOException, ApiException {

        String namespaceStr = "shiyan";
        // 配置client
        ApiClient client = Config.defaultClient();
        Configuration.setDefaultApiClient(client);
        AppsV1Api apiInstance = new AppsV1Api(client);
        CoreV1Api api = new CoreV1Api(client);
        ProtoClient protoClient = new ProtoClient(client);

        // 创建命名空间和deployment
        //createNameSpaceAndDepoyment(namespaceStr,apiInstance,protoClient);

        // 通过api获得节点和pod,并完成预选,优选的过程
        //getNodeAndPod(namespaceStr,api,client);

        // 删除deployment
        //deleteDeploymentByYaml(apiInstance,namespaceStr);

    }

    /**
     * 删除对应的deployment
     * @param api
     * @param namespaceStr
     */
    private static void deleteDeploymentByYaml(AppsV1Api api, String namespaceStr) throws ApiException {
        // 传入deployment的名字,命名空间,就可以删除deployment以及所有的pod了
        V1Status v1Status = api.deleteNamespacedDeployment("pause",namespaceStr,null,null,null,null,null,null);
        System.out.println(v1Status.getCode()+"删除完毕");
    }

    /**
     * 获得可用节点和判断pod的状态完成绑定
     * @param namespaceStr
     * @param api
     * @param client
     * @throws ApiException
     */
    private static void getNodeAndPod(String namespaceStr, CoreV1Api api, ApiClient client) throws ApiException {
        Random random = new Random();
        // 调用方法,计算出所有可用的节点
        // 预选
        V1NodeList nodeList = ready_node(api);
        // 通过api获取指定命名空间(shiyan)中的pod
        V1PodList list = api.listNamespacedPod(namespaceStr,null,null,null,null,null,null,null, null, null);
        // 遍历list,循环判断pod的状态,并完成pod的调度
        for(V1Pod item:list.getItems()){
            // 优选
            int n = random.nextInt(nodeList.getItems().size());
            // 获取pod的状态
            String podStatus = item.getStatus().getPhase();
            String nodeName = item.getSpec().getNodeName();
            // 根据pod的状态和所属节点的名称进行绑定
            if (podStatus.equals("Pending") && nodeName == null){
                // 执行调度方法 pod的名字;可用node;命名空间;客户端;api
                schedluer(item.getMetadata().getName(),nodeList.getItems().get(n),namespaceStr,client,api);
            }
        }
    }

    /**
     * binding 绑定的过程
     * @param name
     * @param v1Node
     * @param namespaceStr
     * @param client
     * @param api
     * @throws ApiException
     */
    private static void schedluer(String name, V1Node v1Node, String namespaceStr, ApiClient client, CoreV1Api api) throws ApiException {
        V1Binding body = new V1Binding();
        V1ObjectReference target = new V1ObjectReference();
        V1ObjectMeta meta = new V1ObjectMeta();
        target.setKind("Node");
        target.setApiVersion("v1");
        target.setName(v1Node.getMetadata().getName());  // 节点的名称
        meta.setName(name);
        body.setTarget(target);
        body.setMetadata(meta);
        api.createNamespacedBinding(namespaceStr,body,null,null,null);
    }


    /**
     * 获得可用的node,这个方法相当于预选阶段
     * @param api
     * @return
     * @throws ApiException
     */
    private static V1NodeList ready_node(CoreV1Api api) throws ApiException {
        // 定义list装所有符合条件的节点
        V1NodeList nodeSelectList = new V1NodeList();
        // 通过api获取所有的节点
        V1NodeList nodeList = api.listNode(null,null,null,null,null,null,null,null,null);;
        // 遍历,找出能用的node,存进list中去
        for (V1Node node:nodeList.getItems()){
            List<V1NodeCondition> conditionsList = node.getStatus().getConditions();
            // 取出最后一个
            String status = conditionsList.get(conditionsList.size()-1).getStatus();
            String type = conditionsList.get(conditionsList.size()-1).getType();
            // 这里的预选策略相当于就是判断node节点的两个状态值
            if (status.equals("True")&&type.equals("Ready")){
                nodeSelectList.addItemsItem(node);
            }
        }
        // 返回所有符合条件的节点
        return nodeSelectList;
    }

    /**
     * 创建命名空间和通过deployment资源对象文件来创建deployment
     * @param namespaceStr
     * @param apiInstance
     * @param protoClient
     * @throws IOException
     * @throws ApiException
     */
    private static void createNameSpaceAndDepoyment(String namespaceStr, AppsV1Api apiInstance, ProtoClient protoClient) throws IOException, ApiException {
        // 1、创建一个命名空间 shiyan
        V1.Namespace namespace =
                V1.Namespace.newBuilder().setMetadata(Meta.ObjectMeta.newBuilder().setName(namespaceStr).build()).build();
        protoClient.create(namespace,"/api/v1/namespaces","v1","Namespace");
        //   删除指定的命名空间
        //protoClient.delete(V1.Namespace.newBuilder(),"/api/v1/namespaces/"+namespaceStr);
        // 2、根据资源对象文件创建deployment,创建多个副本,并指定自定义调度器
       // File deploymentFile = new File("test-scheduler.yaml");
        V1Deployment body = (V1Deployment) Yaml.load(deploymentFile);
        try {
            V1Deployment result = apiInstance.createNamespacedDeployment(namespaceStr,body,null,null,null);
            System.out.println("success,工作负载创建成功");
        }catch (ApiException e){
            if (e.getCode() == 409) {
                System.out.println("error 工作负载创建已重复!");
            } else if (e.getCode() == 200) {
                System.out.println("success 工作负载创建成功!");
            } else if (e.getCode() == 201) {
                System.out.println("error 工作负载创建已重复!");
            } else if (e.getCode() == 401) {
                System.out.println("error 无权限操作!");
            } else {
                System.out.println("error 工作负载创建失败!");
            }
            System.out.println("Exception when calling AppsV1Api#createNamespacedDeployment");
            System.out.println("Status code: {}"+ e.getCode());
            System.out.println("Reason: {}"+ e.getResponseBody());
            System.out.println("Response headers: {}"+ e.getResponseHeaders());
        }
    }
}

7、用到的deployment资源对象文件也在工程目录的根目录下,其具体的内容如下:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: pause  # 名称
  namespace: shiyan  #指定命名空间
spec:
  replicas: 5  # 副本数
  selector:
    matchLabels:
      app: pause
  template:
    metadata:
      labels:
        app: pause
    spec:
      schedulerName: my-scheduler  # 指定自定义的调度器
      containers:
      - name: pause
        image: docker.io/mirrorgooglecontainers/pause:3.1  # 镜像

8、通过java-client操作k8s集群内的资源对象,可以使用命令kubectl get pod -n shiyan(这个是命名空间的名称) 查看所有pod的状态。
补充几条命令:

kubectl get node
kubectl get pod -n shiyan
kubectl describe pod -n shiyan pod的名称
kubectl log pod的名称
Logo

开源、云原生的融合云平台

更多推荐