用Java实现kubernetes的List-Watch

这篇博客介绍了如何使用Java的kubernetes-client库来实现Kubernetes的List-Watch功能。作者强调了版本管理的重要性,特别是解决因依赖冲突导致的问题,并提供了详细的代码示例,包括自定义资源定义和事件监听处理。此外,还展示了如何处理资源的添加、更新和删除事件。
摘要由CSDN通过智能技术生成

前言

一般对于k8s的操作都用go语言,所以大部分的k8s的operator都是用的go语言,但是也有一些用Java写的kubernetes-client,其中fabric8就是一款不错的k8s-client工具,最近研究了一下k8s用Java实现List-watch,供大家参考。

依赖

有一些spring-cloud依赖中带有了kubernetes-client的版本,所以有时候你导入版本的时候总会导入4.10.3的版本,导致一些类是找不到的,就是因为版本问题(博主就是遇到了这个bug,最后排查了好长时间才解决),所以要设置版本依赖,这个依赖管理一定要放在第一个。

<dependency>
    <groupId>io.fabric8</groupId>
    <artifactId>kubernetes-client</artifactId>
</dependency>
<dependencyManagement>
        <dependencies>
            <!-- spring-cloud-dependencies里面包含了kubernetes-client-bom,并且版本为4.10.3,
            所以引入的kubernetes-client不能指定版本, 在此引入kubernetes-client的版本控制, 这个要放在第一个 -->
            <dependency>
                <groupId>io.fabric8</groupId>
                <artifactId>kubernetes-client-project</artifactId>
                <version>5.12.2</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>

            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>${spring-cloud.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-dependencies</artifactId>
                <version>${spring-boot.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>

代码

这是本人的部分测试代码,如要实际应用需要根据自定义开发。

自定义资源

@Group("harmonycloud.cn")
@Version("v1beta1")
@Kind("NodePool")
@Singular("nodepool")
@Plural("nodepools")
public class NodePool extends CustomResource<NodePoolSpec, Void> {
}
@Data
public class NodePoolSpec {

    private Map<String, String> labels;
    private List<Taint> taints;
}

@Data
public class Taint {
    private String key;
    private String value;
    private String effect;
    private String timeAdded;
}

List-watch测试代码

package com.victor.watch;

import com.alibaba.fastjson.JSON;
import com.victor.client.K8sClient;
import com.victor.common.util.ThreadHelper;
import com.victor.model.NodePool;
import io.fabric8.kubernetes.client.Watcher;
import io.fabric8.kubernetes.client.WatcherException;
import io.fabric8.kubernetes.client.informers.ResourceEventHandler;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.stereotype.Component;

import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.TimeUnit;

@Component
@Slf4j
public class ListWatch implements ApplicationRunner {

    private final BlockingQueue<NodePoolEvent> queue = new LinkedBlockingQueue<>();

    @Override
    public void run(ApplicationArguments args) {
        log.info("list-watch启动");
        // 资源被修改时,k8sClient底层会起一个Thread去回调eventReceived, 因此自己不用
        // 再重新启动一个线程
        K8sClient.getClient().customResources(NodePool.class).watch(new Watcher<>() {
            @Override
            public void eventReceived(Action action, NodePool nodePool) {
                try {
                    queue.put(new NodePoolEvent(action, nodePool));
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }

            @Override
            public void onClose(WatcherException cause) {
                log.info("Watcher close due to: ", cause);
            }
        });

        // 这个线程可以是多余的, 其实可以直接在eventReceived方法里书写逻辑, 原本watch就是阻塞的,
        // 事件是按照顺序一个一个去执行的,所以没有必再加一个阻塞队列
        /*new Thread(() -> {
            for (; ; ) {
                NodePoolEvent nodePoolEvent;
                try {
                    nodePoolEvent = queue.take();
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
                log.info("NodePool {} {}", nodePoolEvent.getAction().name(), JSON.toJSONString(nodePoolEvent.getNodePool()));
            }
        }).start();*/

        // List-Watch
        // 执行的时候这个会阻塞,只有当没有事件的时候才会继续往下执行,因此专门启动一个线程去执行它,让主线程继续执行
        ThreadHelper.run(() -> {
            K8sClient.getClient().customResources(NodePool.class).inform(new ResourceEventHandler<>() {
                @Override
                public void onAdd(NodePool nodePool) {
                    log.info("add nodepool:{}", JSON.toJSONString(nodePool));
                    try {
                        // 事件是按照阻塞队列的方式去调用的,只有当上一个执行完毕后才能执行下一个
                        TimeUnit.SECONDS.sleep(10);
                        // 即使抛出异常,也不会影响下一个事件的执行
//                        throw new RuntimeException("错误啦...");
                    } catch (InterruptedException e) {
                        throw new RuntimeException(e);
                    }
                }

                @Override
                public void onUpdate(NodePool oldNodePool, NodePool newNodePool) {
                    log.info("update nodepool oldNodePool:{}, newNodePool:{}",
                            JSON.toJSONString(oldNodePool, true), JSON.toJSONString(newNodePool, true));
                }

                @Override
                public void onDelete(NodePool nodePool, boolean deletedFinalStateUnknown) {
                    log.info("delete nodepool:{}, deletedFinalStateUnknown:{}", nodePool, deletedFinalStateUnknown);
                }
            });

            K8sClient.getClient().informers().addSharedInformerEventListener(ex ->
                    log.error("Exception occurred, message:{}", ex.getMessage(), ex));
        });

    }

    @Data
    @AllArgsConstructor
    @NoArgsConstructor
    public static class NodePoolEvent {
        private Watcher.Action action;
        private NodePool nodePool;
    }
}

Kubernetes中,可以使用YAML格式来编写资源对象的配置文件。YAML是一种简洁的非标记性语言,以数据为中心,使用空白、缩进和换行来组织数据。以下是一些关于YAML文件编写的详细信息: 1. 大小写敏感:YAML是大小写敏感的,因此在编写YAML文件时需要注意大小写的正确使用。 2. 使用缩进表示层级关系:YAML使用缩进来表示数据的层级关系,一般使用空格键进行缩进。Tab键是不被允许的。 3. 缩进的空格数目不重要:相同层级的元素只需要左侧对齐即可,一般情况下使用两个空格进行缩进。 4. 使用#表示注释:在YAML文件中,可以使用#符号来添加注释,注释内容从#字符开始一直到行尾都会被解析器忽略。 5. 特殊字符的使用:在YAML中,一些特殊字符如冒号、逗号、短横杠等需要进行特殊处理,通常在这些字符后面会缩进一个空格。 6. 对象的表示:对象使用键值对的方式表示,使用冒号结构进行分隔,例如`animal: pets`。 7. 另一种对象的写法:YAML还支持将所有键值对写在一行内的行内对象表示法,例如`hash: {name: Steve, foo: bar}`。 8. 数组的表示:数组使用连词线开头的行来表示,每个元素占据一行,例如: ``` - Cat - Dog - Goldfish ``` 9. 子成员是数组:如果数据结构的子成员是一个数组,可以在该项下面缩进一个空格,例如: ``` - - Cat - Dog - Goldfish ``` 10. 文件示例:下面是一个Kubernetes的YAML文件的示例,用于创建一个名为"kubernetes-dashboard"的命名空间: ``` apiVersion: v1 kind: Namespace metadata: name: kubernetes-dashboard ``` 以上是关于Kubernetes中YAML文件编写的一些详细信息。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *2* [kubernetes-----YAML文件编写详解](https://blog.csdn.net/qq_42761527/article/details/105897465)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 50%"] - *3* [k8s集成kubernetes-dashboard可视化界面](https://blog.csdn.net/crazy1013/article/details/126575777)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值