fabric8(kubernetes java client) 自定义开发实战

3 篇文章 0 订阅
1 篇文章 1 订阅

前言

 

 

本文章主要针对需要对fabric8 java client 进行修改的读者,对fabric8进行自定义开发,调用SDK对k8s CRD(CustomResourceDefinition) 资源进行操作。本文章根据作者自己的实际开发内容为主线理清开发思路,并不会特别全面的去讲解所有知识点,需要详细了解还需要自行查阅官网,同时我也会在相应的内容里面贴出一些参考地址。如果存在说错的地方,还请指正!!

github 下载相关代码以及工程简单解析

 

首先我们需要从fabric8 中去下载源代码(地址为:https://github.com/fabric8io/kubernetes-client)。如果需要了解 什么是fabric8,以及详细使用方法、详细内容都可以在这个地方查看。

工程目录结构如图:

 

 

简单介绍一下工程中各个模块的作用:

kubernetes-client:这是最主要的模块最终打包出来的sdk jar包就是这个模块生成的。在该工程下最主要的就是DefaultKubernetesClient.class 因为后续操作k8s的client 就是由这个DefaultKubernetesClient 提供,同时可以从这个类中看出这个类提供k8s 系统级别的所有组件资源,这个也是整个sdk的入口。作者后续的代码便是从AppsAPIGroupClient开始添加自定义资源。如图:

 

 

为了方便理解贴出一段生成client的代码。可以从代码中看出sdk client 就是从DefaultKubernetesClient类中new了一个对象。” KubernetesClient client = new DefaultKubernetesClient(config)“

private K8sClientEntity createK8sClient(String clusterName){
    K8sClientEntity k8sClientEntity = null;
    try {
        String token = "";
        String ip = "";
        ClusterEntity clusterEntity = clusterService.getClusterDetail(clusterName);
        List<ClusterNodeEntity> nodeEntities = clusterService.getClusterNodes(clusterName);
        if (Objects.nonNull(clusterEntity)) {
            token = clusterService.getClusterToken(clusterEntity.getId());
        }
        if (!CollectionUtils.isEmpty(nodeEntities)) {
            for (ClusterNodeEntity node : nodeEntities) {
                if (CommonUtils.isContainString(node.getName(), "master1." + clusterName)) {
                    ip = node.getIp();
                    break;
                }
            }
        }
        if (!StringUtils.isEmpty(token) && !StringUtils.isEmpty(ip)) {
            Config config = new ConfigBuilder().withMasterUrl("https://" + ip + ":6443")
                    .withOauthToken(token)
                    .withTrustCerts(true)
                    .build();
            KubernetesClient client = new DefaultKubernetesClient(config);//使用默认的就足够了
            k8sClientEntity = new K8sClientEntity();
            k8sClientEntity.setTime(System.currentTimeMillis());
            k8sClientEntity.setKubernetesClient(client);
            k8sClientEntity.setIp(ip);
            //获取prometheus port
            io.fabric8.kubernetes.api.model.Service promeSvc =  client.services().inNamespace("kube-operator").withName("f2c-prometheus-server").get();
            if(Objects.nonNull(promeSvc)){
                ServicePort servicePort = promeSvc.getSpec().getPorts().get(0);
                k8sClientEntity.setPromePort(servicePort.getNodePort().toString());
            }
            //获取loki port
            io.fabric8.kubernetes.api.model.Service lokiSvc =  client.services().inNamespace("kube-operator").withName("f2c-loki").get();
            if(Objects.nonNull(lokiSvc)){
                ServicePort servicePort = lokiSvc.getSpec().getPorts().get(0);
                k8sClientEntity.setLokiPort(servicePort.getNodePort().toString());
            }
        }
    } catch (Exception e) {
        logger.error("createK8sClient fail ====>",e);
    }
    return k8sClientEntity;
}

 

kubernetes-model-generator:是client 依赖的实际资源类的生成模块,该模块是通过maven jsonschema2pojo 插件自动生成client依赖类,由于k8s类型众多重复的字段也非常多,这样避免了简单而又繁琐的代码重复书写,后续的client会用到一个模式,我姑且叫“包装模式”很是该工程的巧妙之处。生成class的文件来至kube-schema.json(该文件又可以通过go写的工具来生成json,目前支持k8s原生资源,自定义的需要手动在里面添加)如图:

 

 

其它模块在该处就省略了,由兴趣的可以自己去看看!!!(^v^)

作者本次开发的内容TiDB on K8S 以及 K8S CRD(CustomResourceDefinition)

作者本次开发的内容是自定义fabric8里面的client 提供TiDB集群的部署功能,部署的流程pingcap 的官网有提供(地址: https://docs.pingcap.com/tidb-in-kubernetes/stable/get-started)。感兴趣的可以通过地址去了解。同时先把 K8S CRD(CustomResourceDefinition)的地址也贴上(https://kubernetes.io/zh/docs/tasks/extend-kubernetes/custom-resources/custom-resource-definitions/),为了防止一脸懵逼说明一下,TiDB是一种nosql数据库。

CustomResourceDefinition:这是k8s 自定义资源端点,用于扩展k8s api。实际定义的就是注册到k8s server的api endpoint的信息,operator开发的 自定义controller会根据endpoint来做具体的操作, 可以理解为自定义controller是实际的执行者。

operator: operator可以理解为扩展k8s api 的实际控制器,功能是利用自定义controller调用控制k8s 原生资源(deployment statefulset )的一套k8s 扩展自定义控制器 。operator会注册一个webhook 到api server ,如果发现资源是自定义的那么就会调用operator自定义的controller进行解析和操作。CRD官网也有,这里贴一个大佬写的地址(https://www.infoq.cn/article/MliPUPFhiESWyKyv8Aq8

这里简单说说TiDB是如何部署到k8s的,TiDB自己开发了一套operator,然后定义了crd.yaml,要部署TiDB 需要先部署一套tidb的operator,最后通过operator 部署TiDB集群,由于TiDB 这些资源非k8s 原生 所以接下来就有了作者的下一步工作。

为了方便理解这里贴一段CustomResourceDefinition 的官网demo。

apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
  # 名字必需与下面的 spec 字段匹配,并且格式为 '<名称的复数形式>.<组名>'
  name: crontabs.stable.example.com
spec:
  # 组名称,用于 REST API: /apis/<组>/<版本>
  group: stable.example.com
  # 列举此 CustomResourceDefinition 所支持的版本
  versions:
    - name: v1
      # 每个版本都可以通过 served 标志来独立启用或禁止
      served: true
      # 其中一个且只有一个版本必需被标记为存储版本
      storage: true
      schema:
        openAPIV3Schema:
          type: object
          properties:
            spec:
              type: object
              properties:
                cronSpec:
                  type: string
                image:
                  type: string
                replicas:
                  type: integer
  # 可以是 Namespaced 或 Cluster
  scope: Namespaced
  names:
    # 名称的复数形式,用于 URL:/apis/<组>/<版本>/<名称的复数形式>
    plural: crontabs
    # 名称的单数形式,作为命令行使用时和显示时的别名
    singular: crontab
    # kind 通常是单数形式的驼峰编码(CamelCased)形式。你的资源清单会使用这一形式。
    kind: CronTab
    # shortNames 允许你在命令行使用较短的字符串来匹配资源
    shortNames:
    - ct

 

 

 

 

fabric8 代码开发说明

需求:由于fabric8 支持原生k8s操作,不能够满足自定义资源的操作,所以需要自定义fabric中的代码。由于deployment 这些原生k8s 资源是通过AppsAPIGroupClient 进行操作,所以作者选择在AppsAPIGroupClient 中自定义。

书接第一小节,fabric8 是通过给操作对象封装一层接口和抽象类达到调用client的某个动作从而操作该对象资源的功能。为了方便理解贴一段创建TiDB的代码。

@GetMapping("/test")
public void test() {
    try {
        KubernetesClient client = kubernetesService.getKubernetesClient("k8s-1");
        TidbCluster tidbCluster = kubernetesTranslateService.translateToTidbCluster();
        client.apps().tidbClusters().inNamespace("tiger").create(tidbCluster);
    } catch (Exception e) {
        e.printStackTrace();
    }
}

 

上面代码中就是在k8s tiger 分区创建一个TiDB 集群,下面的代码就是构建TiDB 集群定义的java资源 对象代码,生成的对象就是tidbCluster, 该对象构建用到的TidbClusterBuilder就是通过kube-schema.json 自动生成的。

@Override
public TidbCluster translateToTidbCluster() {
    return new TidbClusterBuilder()
            .withMetadata(new ObjectMetaBuilder()
                    .withName("tiger")
                    .build())
            .withSpec(new TidbClusterSpecBuilder()
                    .withPd(new TidbTemplateBuilder()
                            .withBaseImage("110.0.100.59:5000/market/pd")
                            .withRequests(new TidbRequestsBuilder()
                                    .build())
                            .withConfig(new TidbTemConfigBuilder().build())
                            .build())
                    .withTikv(new TidbTemplateBuilder()
                            .withBaseImage("110.0.100.59:5000/market/tikv")
                            .withRequests(new TidbRequestsBuilder()
                                    .build())
                            .withConfig(new TidbTemConfigBuilder().build())
                            .build())
                    .withTidb(new TidbTemplateBuilder()
                            .withBaseImage("110.0.100.59:5000/market/tidb")
                            .withReplicas(3)
                            .withRequests(new TidbRequestsBuilder()
                                    .build())
                            .withConfig(new TidbTemConfigBuilder().build())
                            .withService(new TidbServiceBuilder()
                                    .withType("NodePort")
                                    .build())
                            .build())
                    .build())
            .build();
}

 

下面介绍整个自定义开发流程:

  1. kubernetes-model-generator中的kubernetes-model-apps模块的kube-schema.json添加操作资源对象的定义(jsonschema2pojo 规则参考 https://blog.csdn.net/github_35758702/article/details/52662676)如图:

 

  2. 在kubernetes-client 工程中如图显示的目录中创建资源操作对象,图中的对象用于操作关联的资源对象,可以重写其中的全部或者部分接口(按需求),但是必须重写“newInstance”方法请见下面代码片段。

 

public class TidbClusterOperationsImpl extends RollableScalableResourceOperation<TidbCluster, TidbClusterList, DoneableTidbCluster, RollableScalableResource<TidbCluster, DoneableTidbCluster>>
  implements TimeoutImageEditReplacePatchable<TidbCluster, TidbCluster, DoneableTidbCluster>
{

  public TidbClusterOperationsImpl(RollingOperationContext context) {
    super(context.withApiGroupName("pingcap.com")
      .withApiGroupVersion("pingcap.com/v1alpha1")
      .withPlural("tidbclusters"));
    this.type = TidbCluster.class;
    this.listType = TidbClusterList.class;
    this.doneableType = DoneableTidbCluster.class;
  }
  public TidbClusterOperationsImpl(OkHttpClient client, Config config) {
    this(client, config, null);
  }
  public TidbClusterOperationsImpl(OkHttpClient client, Config config, String namespace) {
    this(new RollingOperationContext().withOkhttpClient(client).withConfig(config).withNamespace(namespace).withPropagationPolicy(DEFAULT_PROPAGATION_POLICY));
  }

  @Override
  public BaseOperation<TidbCluster, TidbClusterList, DoneableTidbCluster, RollableScalableResource<TidbCluster, DoneableTidbCluster>> newInstance(OperationContext context) {
    return new TidbClusterOperationsImpl((RollingOperationContext) context);
  }

  @Override
  public TidbCluster updateImage(Map<String, String> containerToImageMap) {
    return null;
  }

  @Override
  public TidbCluster updateImage(String image) {
    return null;
  }

  @Override
  public ImageEditReplacePatchable<TidbCluster, TidbCluster, DoneableTidbCluster> withTimeout(long timeout, TimeUnit unit) {
    return null;
  }

  @Override
  public ImageEditReplacePatchable<TidbCluster, TidbCluster, DoneableTidbCluster> withTimeoutInMillis(long timeoutInMillis) {
    return null;
  }

  /**
   * just edit tidb replicas
   * @param count
   * @return
   */
  @Override
  protected TidbCluster withReplicas(int count) {
    return cascading(false).edit().editSpec().editTidb().withReplicas(count).endTidb().endSpec().done();
  }

  @Override
  protected RollingUpdater<TidbCluster, TidbClusterList, DoneableTidbCluster> getRollingUpdater(long rollingTimeout, TimeUnit rollingTimeUnit) {
    return null;
  }

  @Override
  protected int getCurrentReplicas(TidbCluster current) {
    return 0;
  }

  @Override
  protected int getDesiredReplicas(TidbCluster item) {
    return 0;
  }

  @Override
  protected long getObservedGeneration(TidbCluster current) {
    return 0;
  }

  @Override
  public String getLog() {
    return null;
  }

  @Override
  public String getLog(Boolean isPretty) {
    return null;
  }


  @Override
  public NonNamespaceOperation<TidbCluster, TidbClusterList, DoneableTidbCluster, RollableScalableResource<TidbCluster, DoneableTidbCluster>> inNamespace(String namespace) {
    return super.inNamespace(namespace);
  }
}

 

3. 在AppsAPIGroupDSL接口中添加自定义的方法:

public interface AppsAPIGroupDSL extends Client {

  MixedOperation<DaemonSet, DaemonSetList, DoneableDaemonSet, Resource<DaemonSet, DoneableDaemonSet>> daemonSets();

  MixedOperation<Deployment, DeploymentList, DoneableDeployment, RollableScalableResource<Deployment, DoneableDeployment>> deployments();

  MixedOperation<ReplicaSet, ReplicaSetList, DoneableReplicaSet, RollableScalableResource<ReplicaSet, DoneableReplicaSet>> replicaSets();

  MixedOperation<StatefulSet, StatefulSetList, DoneableStatefulSet, RollableScalableResource<StatefulSet, DoneableStatefulSet>> statefulSets();

  MixedOperation<TidbCluster, TidbClusterList, DoneableTidbCluster, RollableScalableResource<TidbCluster, DoneableTidbCluster>> tidbClusters();
}

 

4. 在AppsAPIGroupClient中实现第三步添加的接口

 

 

 

 

 

public class AppsAPIGroupClient extends BaseClient implements AppsAPIGroupDSL {

  public AppsAPIGroupClient() throws KubernetesClientException {
    super();
  }
  @Override
public MixedOperation<TidbCluster, TidbClusterList, DoneableTidbCluster, RollableScalableResource<TidbCluster, DoneableTidbCluster>> tidbClusters() {
  return new TidbClusterOperationsImpl(httpClient, getConfiguration());
}
}

 

5. 打包收工

 

开发过程中遇到的问题

发先调用的接口报错404,这个很明显就是 请求的地址和CRD 定义的端点对不上所以导致的。

在排查问题的时候发现api service 存在自定义的api 端点,但是发生了疑问 api server 和api service 的区别,api service 就是资源端点的统称,用于查看k8s api 端点(endpoint)的。

问题原因是在初始化TidbClusterOperationsImpl中配置的地址错误的,如图:

 

 

我们需要执行“curl https://10.0.100.51:6443/apis --cacert /etc/kubernetes/ssl/ca.pem --key /etc/kubernetes/ssl/ca-key.pem --cert /etc/kubernetes/ssl/ca.pem” 找到自定义的端点然后执行

“curl https://10.0.100.51:6443/apis/pingcap.com/v1alpha1 --cacert /etc/kubernetes/ssl/ca.pem --key /etc/kubernetes/ssl/ca-key.pem --cert /etc/kubernetes/ssl/ca.pem ” 找到具体的端点, 代码中的

withPlural 字段对应如下请求返回结果内容的name字段。

 

{
  "kind": "APIResourceList",
  "apiVersion": "v1",
  "groupVersion": "pingcap.com/v1alpha1",
  "resources": [
    {
      "name": "tidbinitializers",
      "singularName": "tidbinitializer",
      "namespaced": true,
      "kind": "TidbInitializer",
      "verbs": [
        "delete",
        "deletecollection",
        "get",
        "list",
        "patch",
        "create",
        "update",
        "watch"
      ],
      "shortNames": [
        "ti"
      ],
      "storageVersionHash": "CaLrWV3R+xo="
    },
    {
      "name": "tidbclusters",
      "singularName": "tidbcluster",
      "namespaced": true,
      "kind": "TidbCluster",
      "verbs": [
        "delete",
        "deletecollection",
        "get",
        "list",
        "patch",
        "create",
        "update",
        "watch"
      ],
      "shortNames": [
        "tc"
      ],
      "storageVersionHash": "2dlERqlmc8s="
    },
    {
      "name": "backupschedules",
      "singularName": "backupschedule",
      "namespaced": true,
      "kind": "BackupSchedule",
      "verbs": [
        "delete",
        "deletecollection",
        "get",
        "list",
        "patch",
        "create",
        "update",
        "watch"
      ],
      "shortNames": [
        "bks"
      ],
      "storageVersionHash": "AxduQBp2nn8="
    },
    {
      "name": "tidbmonitors",
      "singularName": "tidbmonitor",
      "namespaced": true,
      "kind": "TidbMonitor",
      "verbs": [
        "delete",
        "deletecollection",
        "get",
        "list",
        "patch",
        "create",
        "update",
        "watch"
      ],
      "shortNames": [
        "tm"
      ],
      "storageVersionHash": "2FNzNjpjaWs="
    },
    {
      "name": "tidbclusterautoscalers",
      "singularName": "tidbclusterautoscaler",
      "namespaced": true,
      "kind": "TidbClusterAutoScaler",
      "verbs": [
        "delete",
        "deletecollection",
        "get",
        "list",
        "patch",
        "create",
        "update",
        "watch"
      ],
      "shortNames": [
        "ta"
      ],
      "storageVersionHash": "zzMOZyvnKbk="
    },
    {
      "name": "backups",
      "singularName": "backup",
      "namespaced": true,
      "kind": "Backup",
      "verbs": [
        "delete",
        "deletecollection",
        "get",
        "list",
        "patch",
        "create",
        "update",
        "watch"
      ],
      "shortNames": [
        "bk"
      ],
      "storageVersionHash": "1U4MtjV1NUI="
    },
    {
      "name": "restores",
      "singularName": "restore",
      "namespaced": true,
      "kind": "Restore",
      "verbs": [
        "delete",
        "deletecollection",
        "get",
        "list",
        "patch",
        "create",
        "update",
        "watch"
      ],
      "shortNames": [
        "rt"
      ],
      "storageVersionHash": "CJXYT9H0VM4="
    }
  ]
}

奉上源码地址:https://github.com/XWTiger/kubernetes-client/blob/main/README.md

 

 

 

 

 

  • 1
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
好的,下面是集成 `io.fabric8 kubernetes-client` 的步骤: 1. 在 `pom.xml` 文件中加入 `io.fabric8` 的依赖: ```xml <dependency> <groupId>io.fabric8</groupId> <artifactId>kubernetes-client</artifactId> <version>4.13.0</version> </dependency> ``` 2. 创建 `KubernetesConfig` 类,用于配置 Kubernetes 的连接参数,例如: ```java import io.fabric8.kubernetes.client.Config; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @Configuration public class KubernetesConfig { @Value("${kubernetes.master}") private String kubernetesMaster; @Value("${kubernetes.api.token}") private String kubernetesApiToken; @Bean public Config kubernetesConfig() { Config config = new Config(); config.setMasterUrl(kubernetesMaster); config.setBearerToken(kubernetesApiToken); return config; } } ``` 其中,`kubernetes.master` 和 `kubernetes.api.token` 分别是 Kubernetes 的 API Server 地址和 API Token,可以通过配置文件或环境变量来设置。 3. 创建 `KubernetesClient` 类,用于创建 Kubernetes 的客户端对象,例如: ```java import io.fabric8.kubernetes.client.DefaultKubernetesClient; import io.fabric8.kubernetes.client.KubernetesClient; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @Configuration public class KubernetesClientConfig { @Autowired private Config kubernetesConfig; @Bean public KubernetesClient kubernetesClient() { return new DefaultKubernetesClient(kubernetesConfig); } } ``` 其中,`KubernetesClient` 是 `io.fabric8 kubernetes-client` 中的核心类,用于与 Kubernetes API Server 进行交互。 现在,你就可以在 Spring Boot 应用中使用 `KubernetesClient` 对象来操作 Kubernetes 了。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值