使用fabric8操作k8s

文章目录

  • 一、引入fabric包
  • 二、认证
    • 1、使用config文件认证
    • 2、使用oauthtoken认证
  • 三、pod的查询和遍历
  • 四、命名空间的创建和删除
  • 五、deployment的创建和删除
    • 部分参数说明
      • 1、resourceRequirements
      • 2、containerPorts
      • 3、envVarList
      • 4、volumeMounts和volumeList
      • 5、nodeAffinity
  • 六、单个pod的创建和删除
  • 七、DaemonSet的创建
  • 七、给node打标签

一、引入fabric包

<dependency>
    <groupId>io.fabric8</groupId>
    <artifactId>kubernetes-client</artifactId>
    <version>5.10.2</version>
</dependency>

二、认证

认证十分简单,只要拿到认证的config信息后使用以下方式即可。

KubernetesClient client = new DefaultKubernetesClient(config);

当然了,为了方便使用,client最好是存入数据库后再放在缓存中去维护。且需要对接多个k8s集群时,需要多个KubernetesClient,因此最好是在缓存中维护一个集群编码和client的对应关系。
那么认证的config信息要怎么拿到呢?通常有使用config文件和oauthtoken认证两种方式。

当然了,在初始时可以对这个client进行校验,测试连通性是否有问题,如果校验通过再在后面对它进行操作。

try {
    NonNamespaceOperation<Namespace, NamespaceList, Resource<Namespace>> namespaces = kubernetesClient.namespaces();
    if (namespaces != null) {
        namespaces.withName("default").get();
    }
} catch (Exception e) {
	    throw new XxxException(Xxxxxx);
} finally {
    kubernetesClient.close();
}

1、使用config文件认证

config文件在管理节点的/root/.kube/目录下面,在页面上传后,我们后端拿到的比如说是一个fileUploadReqBO下的byte[] file;

String kubeConfig = null;
Config config = null;
try {
    kubeConfig = new String(fileUploadReqBO.getFile(), Charsets.UTF_8.toString());
    config = Config.fromKubeconfig(kubeConfig);
} catch (Exception e) {
    throw new XxxException(xxxx, e.getMessage());
}

2、使用oauthtoken认证

获取oauthtoken需要有admin权限的serviceaccount,如果没有的话那么就自己手动创建一个。
创建serviceaccount账户,这里我们就叫test-admin:

kubectl create serviceaccount test-admin -n kube-system

给予admin权限:

kubectl create clusterrolebinding my-service-account-admin --clusterrole=cluster-admin --serviceaccount=kube-system:test-admin

执行以下命令

kubectl get secret -n kube-system|grep admin

找到返回结果中以test-admin-token-开头的内容,使用以下命令

kubectl describe secret test-admin-token-XXX -n kube-system

就可以获取到token了
获取到后可使用kubectl auth can-i create deployments --as=system:serviceaccount:kube-system:test-admin --token= 判断是否有管理员权限 yes有 no没有
那么这时假设我们能拿到一个masterUrl,例如 https://10.20.66.152:6443(kube-apiserver一般来说默认端口为6443)以及token。
就可以这样获取到config:

Config config = new ConfigBuilder().withTrustCerts(true).build();
config.setMasterUrl(masterUrl);
config.setOauthToken(oauthToken);

三、pod的查询和遍历

查询所有pod:

//已获取KubernetesClient:KubernetesClient client = new DefaultKubernetesClient(config);
PodList podList = client.pods().list();

根据命名空间查询:

PodList podList = client.pods().inNamespace(K8sGenericConstant.K8S_NAMESPACE_ENGINE_SERVER).list();

遍历pod:

if (podList != null && podList.getItems() != null) {
    for(Pod pod : podList.getItems()){
        //pod名称
        String podName = pod.getMetadata().getName();
        //pod所在节点名称
        String nodeName = pod.getSpec().getNodeName();
        //pod标签
        Map<String, String> labels = pod.getMetadata().getLabels();
        //命名空间
        String ns = pod.getMetadata().getNamespace();
        //状态
        pod.getStatus().getContainerStatuses();
        pod.getStatus().getReason();
        List<PodCondition> podConditions = pod.getStatus().getConditions();
        if (!CollectionUtils.isEmpty(podConditions)) {
            PodCondition podCondition = podConditions.get(0);
            reason = podCondition.getReason() + ":" + podCondition.getMessage();
        }
    }

四、命名空间的创建和删除

创建

NonNamespaceOperation<Namespace, NamespaceList, Resource<Namespace>> namespaces = client.namespaces();
if (namespaces == null) {
    return null;
}
String name = "test-ns";
Map<String, String> labels = Maps.newHashMap();
labels.put("testlabel", "testvalue");
Namespace ns = new NamespaceBuilder().withNewMetadata().withName(name).addToLabels(labels).endMetadata().build();
ns = namespaces.createOrReplace(ns);

删除

NonNamespaceOperation<Namespace, NamespaceList, Resource<Namespace>> namespaces = client.namespaces();
if (namespaces == null) {
    return null;
}
namespaces.withName(name).delete();

五、deployment的创建和删除

删除:

//注意这里deployment需要先查询出Deployment类型,而不只是名称
client.apps().deployments().inNamespace(namespace).delete(deployment);
client.apps().deployments().inNamespace(namespace).withName(deploymentname).delete();

创建:

Deployment deployment = new DeploymentBuilder()
	.withNewMetadata()
		.withName(podName)
	.endMetadata()
	.withNewSpec()
		.withNewSelector().addToMatchLabels(matchLabels).endSelector()
		.withReplicas(1)
		.withNewTemplate()
			.withNewMetadata().withLabels(matchLabels).withNamespace(namespace).withAnnotations(annotations).endMetadata()
			.withNewSpec()
				.addNewContainer()
				.withName(podName).withImage(imageUrl).withImagePullPolicy(K8sImagePullPolicyEnum.IF_NOT_PRESENT.getValue()).withResources(resourceRequirements).withPorts(containerPorts).withEnv(envVarList).withVolumeMounts(volumeMounts).withCommand(commandList).withArgs(argList).endContainer()
				.withVolumes(volumeList)
				.withNewAffinity().withNodeAffinity(nodeAffinity).endAffinity()
				.withNodeSelector(nodeSelector)
			.endSpec()
		.endTemplate()
	.endSpec()
	.build();
	
client.apps().deployments().inNamespace(namespace).create(deployment);

部分参数说明

其中的参数比如podName、namespace、podName、imageUrl是String类型,commandList、argList为List<String>类型,但也有不少需要提前构造的参数,比如matchLabels、annotations、nodeSelector是Map<String,String>的类型,又比如以下几个示例:

1、resourceRequirements

ResourceRequirements resourceRequirements = new ResourceRequirements();
Map<String, Quantity> limits = new HashMap<>();
limits.put("cpu", new Quantity("2000m"));
limits.put("memory", new Quantity("20480Mi"));
limits.put("nvidia.kubernetes.io/gpu", new Quantity("1"));
Map<String, Quantity> requests = new HashMap<>();
requests.put("cpu", new Quantity("1000m"));
requests.put("memory", new Quantity("10240Mi"));
requests.put("nvidia.kubernetes.io/gpu", new Quantity("1"));
resourceRequirements.setRequests(requests);
resourceRequirements.setLimits(limits);

注意这里的limits.put()后面的key要和describe node获取的一致。比如这里的gpu用的是nvidia.kubernetes.io/gpu,如果是其他厂商的或者映射出来的不一致,则要和环境中保持一致。实际使用中经常做成可配置/传参的,由于这里只是一个示例,因此写死了。

Capacity:
  cpu:                8
  ephemeral-storage:  308468608Ki
  hugepages-1Gi:      0
  hugepages-2Mi:      0
  memory:             32771060Ki
  nvidia.kubernetes.io/gpu:     1
  pods:               200

2、containerPorts

containerPorts需要的类型是List<ContainerPort>
也就是如下图所示:

public synchronized List<ContainerPort> buildContainerPorts() {
    LOGGER.info("ports={}", ports);
    List<ContainerPort> containerPortList = Lists.newArrayList();
    //实际使用时需作为入参传入List<ContainerPortBO>,这里作为示范直接写死
    ContainerPort port = new ContainerPort();
    port.setHostPort(32111);
    port.setName("web-port");
    port.setProtocol("TCP");
    port.setContainerPort(32111);
    containerPortList.add(port);
    //假设这里我们已经获得了一个containerPortList
    containerPortList = containerPortList.stream().filter(p -> p.getHostPort() != null && p.getContainerPort() != null).collect(Collectors.toList());
    if (CollectionUtils.isEmpty(containerPortList)) {
        return null;
    }
    // 如果由上层直接指定端口的话,这里直接return containerPortList即可
    //但当需要我们自己去分配端口时 需要尽量避免端口冲突,因此做了以下处理(并不完全能避免,但至少如果某个节点跑多个pod,不会只能跑一个其他的都在pending)
    // 1.查询每个POD占用的端口
    PodList podList = K8sClientTool.getKubernetesClient().pods().list();
    Set<Integer> excludeNodePortList = Sets.newHashSet();
    if (podList != null && podList.getItems() != null) {
        for (Pod pod : podList.getItems()) {
            List<Integer> portList = pod.getSpec().getContainers().stream().flatMap(m ->
                    m.getPorts().stream().filter(p -> p.getHostPort() != null).map(ContainerPort::getHostPort)
            ).collect(Collectors.toList());

            excludeNodePortList.addAll(portList);
        }
    }

    // 2.获取组件安装机器的端口,一般aid安装在K8S集群的主节点上,这样可以规避掉主要的端口
    try {
        String result = SshTool.doExecute("netstat -nlpt  | grep -Po '\\d+(?=.+)' | sort -rn | xargs -n1");
        if (StringUtils.isNotEmpty(result)) {
            excludeNodePortList.addAll(Arrays.stream(result.split("\n")).map(s -> Integer.parseInt(s.trim())).collect(Collectors.toList()));
        }
    } catch (Exception e) {
        throw new ComputeResourceException(AidServerErrorCode.ERR_DEVICE_SSH_CONNECT);
    }

    // 3.解决容器端口的占用和冲突问题,这里需要解决并发的问题,加一个锁来处理
    List<Pair<Integer, Long>> needRemovePortPairList = Lists.newArrayList();
    // 4.先加入配置文件中要排除的端口
    excludeNodePortList.addAll(Arrays.stream(excludeNodePorts.split(",")).map(s -> Integer.parseInt(s.trim())).collect(Collectors.toList()));
    // 5.再加入历史分配出去的端口,这些端口有可能没有真正的分配出去,但是需要缓存,避免同时出现2个要分配的端口
    excludeNodePortList.addAll(excludeNodePortPairList.stream().map(pair -> {
        if (pair.getRight() < (System.currentTimeMillis() - DEFAULT_TIME_TO_LIVE)) {
            return pair.getLeft();
        }
        needRemovePortPairList.add(pair);
        return null;
    }).filter(p -> p != null).collect(Collectors.toSet()));
    // 6.清理掉过期的缓存端口
    excludeNodePortPairList.removeAll(needRemovePortPairList);
    LOGGER.info("containerPortList={}, excludeNodePortList={}", containerPortList, excludeNodePortList);
    containerPortList.stream().forEach(c -> {
        // 优先使用分配的hostPort,不满足再随机分配
        Integer hostPort = c.getHostPort();
        while (excludeNodePortList.contains(hostPort)) {
            hostPort = RandomUtils.nextInt(minNodePort, maxNodePort);
        }
        excludeNodePortList.add(hostPort);
        excludeNodePortPairList.add(Pair.of(hostPort, System.currentTimeMillis()));
        if (StringUtils.isNotEmpty(c.getName())) {
            c.setName(c.getName().toLowerCase().replaceAll("_", "-"));
            if (c.getName().length() > 15) {
                c.setName(c.getName().substring(0, 15));
            }
        }
        c.setHostPort(hostPort);
    });
    LOGGER.info("containerPortList={}", containerPortList);
    return containerPortList;
}

3、envVarList

List<EnvVar> envVarList = Lists.newArrayList();
EnvVar envVar = new EnvVar();
envVar.setName("TEST_ENV_KEY");
envVar.setValue("TEST_ENV_VALUE");
envVarList.add(envVar);

4、volumeMounts和volumeList

假设参数以List<Map<String, String>>形式传入,例如:
“volumeMounts”:[{“name”:“test-name”,“mountPath”:“/home/test”,“hostPath”:“/home/test”}]

volumeMounts:

public List<VolumeMount> buildVolumeMounts(List<Map<String, String>> volumeMountMapList) {
    List<VolumeMount> volumeMounts = Lists.newArrayList();
    if (!CollectionUtils.isEmpty(volumeMountMapList)) {
        for (Map<String, String> map : volumeMountMapList) {
            volumeMounts.add(TypeTool.castToBean(map, VolumeMount.class));
        }
    }
//    VolumeMount testVolumeMount = new VolumeMount();
//    testVolumeMount.setName("test-name");
//    testVolumeMount.setMountPath("/home/test");
//    volumeMounts.add(testVolumeMount); volumeMounts.add(testVolumeMount);
    return volumeMounts;
}

volumeList:

public List<Volume> buildVolumes(List<VolumeMount> volumeMounts, List<Map<String, String>> volumeMountMapList) {
    return volumeMounts.stream().map(m -> {
        Volume volume = new Volume();
        volume.setName(m.getName());
        String path = m.getMountPath();
        if (!CollectionUtils.isEmpty(volumeMountMapList)) {
            Optional<Map<String, String>> optional = volumeMountMapList.stream().filter(p -> m.getName().equals(p.get("name"))).findFirst();
            if (optional.isPresent()) {
                Map<String, String> volumeMap = optional.get();
                if (volumeMap.containsKey("hostPath")) {
                    path = optional.get().get("hostPath");
                }
            }
        }
        HostPathVolumeSource hostPath = new HostPathVolumeSource();
        hostPath.setPath(path);
        volume.setHostPath(hostPath);
        return volume;
    }).collect(Collectors.toList());
}

5、nodeAffinity

List<NodeSelectorRequirement> matchExpressions = Lists.newArrayList();
	  matchExpressions.add(new NodeSelectorRequirementBuilder().withKey("nvidia.kubernetes.io/gpu")
	    //GpuTypeEnum.toContainerValues():List<String>
	    .withOperator("In").withValues(GpuTypeEnum.toContainerValues()).build());
	    NodeAffinity nodeAffinity = new NodeAffinityBuilder()
	            .withNewRequiredDuringSchedulingIgnoredDuringExecution()
	                    .withNodeSelectorTerms(new NodeSelectorTermBuilder().withMatchExpressions(matchExpressions).build())
	            .endRequiredDuringSchedulingIgnoredDuringExecution()
	            .build();

六、单个pod的创建和删除

删除:

client.pods().inNamespace(namespace).delete(pod);
client.pods().inNamespace(namespace).withName(podname).delete();

创建:

Pod podToCreate = new PodBuilder()
	.withNewMetadata()
		.withName(podName)
		.withNamespace(namespace)
		.withLabels(labels)
		.withAnnotations(annotations)
	.endMetadata()
	.withNewSpec()
		.addNewContainer()
			.withName(podName)
			.withImage(imageUrl)
			.withImagePullPolicy("IfNotPresent")
			.withResources(resourceRequirements)
			.withPorts(containerPorts)
			.withEnv(envVarList)
			.withVolumeMounts(volumeMounts)
			.withCommand(commandList)
			.withArgs(argList)
		.endContainer()
		.withNodeSelector(nodeSelector)
		.withRestartPolicy("OnFailure")
		.withVolumes(volumeList)
	//如果需要容忍污点
	.addNewToleration().withEffect("NoSchedule").withOperator("Exists").endToleration()
	//节点选择策略
	.withNewAffinity().withNodeAffinity(nodeAffinity).endAffinity()
	.and().build();
Pod pod = null;
try {
    pod = client.pods().create(podToCreate);
} catch (Exception e) {

}

这里需要用到的参数和deployment的差不多,就不赘述了。

七、DaemonSet的创建

和deployment的创建大致一致,只是使用的是client.apps().daemonSets()
以及和上面的示例相比没有replicas,这里就不再做说明了。

七、给node打标签

//先查出所需node
NodeList nodeList = client.nodes().list();
//筛选出需要的node
Optional<Node> optionalNode = nodeList.getItems().stream().filter(e -> e.getMetadata().getUid().equals(indexCode)).findFirst();
if (!optionalNode.isPresent()) {
    throw new XxxException();
}
// 4. 处理node标签
Node node = optionalNode.get();
//获取原有标签
Map<String, String> labels = node.getMetadata().getLabels();
//加入新的标签
labels.put("xxx","xxx")
//设置标签
node.getMetadata().setLabels(labels);
//保存
client.nodes().createOrReplace(node);

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/764220.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【C++】使用C++在线程中动态记录数据到外部文件

在现代软件开发中&#xff0c;多线程编程已成为处理并发任务、提高程序性能的重要手段。而在多线程环境下&#xff0c;如何有效地管理和记录数据&#xff0c;尤其是将动态生成的数据安全地写入外部文件&#xff0c;是许多应用程序必须面对的问题。本文将深入探讨如何在C中使用多…

【运维】Windows server 2022 开启 telnet 功能

控制面板》启动或关闭Windows 功能 仪表盘》添加角色和功能》功能》telnet客户端

python-糖果俱乐部(赛氪OJ)

[题目描述] 为了庆祝“华为杯”的举办&#xff0c;校园中开展了许多有趣的热身小活动。小理听到这个消息非常激动&#xff0c;他赶忙去参加了糖果俱乐部的活动。 该活动的规则是这样的&#xff1a;摊位上有 n 堆糖果&#xff0c;第 i 堆糖果有 ai​ 个&#xff0c;参与的同学可…

全平台7合一自定义小程序源码系统功能强大 前后端分离 带完整的安装代码包以及搭建教程

系统概述 这款全平台 7 合一自定义小程序源码系统是专为满足各种业务需求而设计的。它整合了多种功能&#xff0c;能够在不同平台上运行&#xff0c;为用户提供了全方位的体验。无论你是企业主、开发者还是创业者&#xff0c;这款系统都能为你提供强大的支持。 代码示例 系统…

MATLAB code 生成C代码样式

Matlab code 生成C代码需要以下产品&#xff1a; MATLABMATLAB CoderC 编译器 MATLAB Coder 将查找并使用支持的已安装编译器。 可以使用 mex -setup 更改默认编译器。 在本地工作文件夹中创建文件 创建一个本地工作文件夹&#xff0c;例如 c:\ecoder\work。创建包含以下代…

【Python】Python的安装与环境搭建

个人主页&#xff1a;【&#x1f60a;个人主页】 系列专栏&#xff1a;【❤️Python】 文章目录 前言Python下载环境配置测试环境变量是否配置成功配置环境变量 运行Python交互式解释器&#xff1a;命令行脚本集成开发环境&#xff08;IDE&#xff1a;Integrated Development E…

电脑IP地址自动获取:操作指南与优势分析

在数字化时代&#xff0c;网络连接已成为我们日常生活和工作中的重要组成部分。而在建立网络连接的过程中&#xff0c;IP地址的设置无疑是至关重要的一环。IP地址&#xff0c;作为网络设备的唯一标识&#xff0c;其设置方式直接影响到网络的稳定性和安全性。本文将详细介绍如何…

大数据、人工智能、云计算、物联网、区块链序言【大数据导论】

各位大佬好 &#xff0c;这里是阿川的博客&#xff0c;祝您变得更强 个人主页&#xff1a;在线OJ的阿川 大佬的支持和鼓励&#xff0c;将是我成长路上最大的动力 阿川水平有限&#xff0c;如有错误&#xff0c;欢迎大佬指正 本篇序言前 必看 【大数据导论】—大数据序言 这是…

山东益康,聚焦绿葆医院场景媒体,用爱服务人类健康

山东益康集团创建于1983年&#xff0c;发展成为集药品研发生产、销售、特医功能食品、精细化工、医疗防护产品等多产业经营为一体的省级企业集团。益康集团紧跟国家发展战略&#xff0c;满足民众日益增长的健康需求&#xff0c;将食品生产向特医保健功能食品转型升级&#xff0…

校园兼职小程序的设计

管理员账户功能包括&#xff1a;系统首页&#xff0c;个人中心&#xff0c;商家管理&#xff0c;管理员管理&#xff0c;用户管理&#xff0c;兼职管理&#xff0c;论坛管理&#xff0c;公告管理 微信端账号功能包括&#xff1a;系统首页&#xff0c;公告&#xff0c;兼职&…

【TS】TypeScript 入门指南:强大的JavaScript超集

&#x1f308;个人主页: 鑫宝Code &#x1f525;热门专栏: 闲话杂谈&#xff5c; 炫酷HTML | JavaScript基础 ​&#x1f4ab;个人格言: "如无必要&#xff0c;勿增实体" 文章目录 TypeScript 入门指南&#xff1a;强大的JavaScript超集一、TypeScript 简介1.1 …

【Python】已解决:ValueError: If using all scalar values, you must pass an index

文章目录 一、分析问题背景二、可能出错的原因三、错误代码示例四、正确代码示例五、注意事项 已解决&#xff1a;ValueError: If using all scalar values, you must pass an index 一、分析问题背景 在Python编程中&#xff0c;尤其是当使用pandas库进行数据分析和处理时&a…

技巧类题目

目录 技巧类题目 136 只出现一次的数字 191 位1的个数 231. 2 的幂 169 多数元素 75 颜色分类 &#xff08;双指针&#xff09; 287. 寻找重复数 136 只出现一次的数字 给你一个 非空 整数数组 nums &#xff0c;除了某个元素只出现一次以外&#xff0c;其余每个元素均…

应急响应:应急响应流程,常见应急事件及处置思路

「作者简介」&#xff1a;冬奥会网络安全中国代表队&#xff0c;CSDN Top100&#xff0c;就职奇安信多年&#xff0c;以实战工作为基础著作 《网络安全自学教程》&#xff0c;适合基础薄弱的同学系统化的学习网络安全&#xff0c;用最短的时间掌握最核心的技术。 这一章节我们需…

【PWN · ret2syscall | GoPwn】[2024CISCN · 华中赛区]go_note

一道GoPwn&#xff0c;此外便是ret2syscall的利用。然而过程有不小的曲折&#xff0c;参考 返璞归真 师傅的wp&#xff0c;堪堪完成了复现。复现过程中&#xff0c;师傅也灰常热情回答我菜菜的疑问&#xff0c;感谢&#xff01;2024全国大学生信息安全竞赛&#xff08;ciscn&am…

【U8+】供应链-库存管理-库存展望

知识点:库存展望可查询展望期内存货的预计库存、可用量情况。 分析步骤一:在库存管理-设置-选项-可用量检查页签库存展望可用量公式中预计入库和预计出库进行勾选和对应仓库档案需要勾选纳入可用量计算 步骤二:库存展望查询条件维护展望日期以及存货和选择

深度学习笔记: 最详尽解释混淆矩阵 Confusion Matrix

欢迎收藏Star我的Machine Learning Blog:https://github.com/purepisces/Wenqing-Machine_Learning_Blog。如果收藏star, 有问题可以随时与我交流, 谢谢大家&#xff01; 混淆矩阵 假设我们有包含临床测量数据的医疗数据&#xff0c;例如胸痛、良好的血液循环、动脉阻塞和体重…

昇思25天学习打卡营第06天|网络构建

神经网络基础 神经网络是一种模拟人脑神经元工作方式的计算模型&#xff0c;它由多个层次的节点&#xff08;神经元&#xff09;组成&#xff0c;每个神经元接收输入、进行加权求和并经过非线性激活函数转换后输出到下一层或作为最终输出。 昇思模型中的mindspore.nn提供了常…

PHP商家来客宝小程序系统客户打卡赢霸王餐美食之旅嗨翻天

&#x1f389;商家来客宝大放送&#xff01;&#x1f37d;️ &#x1f525;开篇福利预警&#xff01; 嘿宝贝们&#xff0c;今天要给你们揭秘一个超级劲爆的吃货福利——“商家来客宝客户打卡吃霸王餐”活动&#xff01;&#x1f389; 是不是已经听到肚子咕咕叫了呢&#xff…

类和对象(提高)

类和对象&#xff08;提高&#xff09; 1、定义一个类 关键字class 6 class Data1 7 { 8 //类中 默认为私有 9 private: 10 int a;//不要给类中成员 初始化 11 protected://保护 12 int b; 13 public://公共 14 int c; 15 //在类的内部 不存在权限之分 16 void showData(void)…
最新文章