前言
一般对于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;
}
}