最近项目需要开发一个功能,需要监控 k3s 集群上每个应用的状态。因为 k3s 和 k8s 核心功能是一样的,我们可以通过 k8s 的 API 得到这些信息,而且官方也提供了 io.kubernetes 的 client-java ,所以使用这个 SDK 包就能通过 java 代码的方式获取到数据。
那么事不宜迟,马上动手开发吧!
首先,当然是在 pom.xml 引入 io.kubernetes 的 client-java。
<dependency>
<groupId>io.kubernetes</groupId>
<artifactId>client-java</artifactId>
<version>10.0.0</version>
</dependency>然后是初始化API的客户端,有三种方式。
第一种最简单的方式:使用 ClientBuilder.cluster().build() 直接进行初始化
@Slf4j
@Component
public classK3sClient{
/**
* 初始化API客户端
*/
@PostConstruct
privatevoidsetDefaultApiClient()throws IOException {
// 自动获取配置初始化
ApiClient apiClient = ClientBuilder.cluster().build();
// 创建操作类
Configuration.setDefaultApiClient(apiClient);
}
/**
* 获取所有的Pod
*
* @return podList
*/
public V1PodList getAllPodList(){
CoreV1Api api = new CoreV1Api();
try {
V1PodList list = api.listPodForAllNamespaces(null, null, null, null, null, null, null, null, null, null);
return list;
} catch (ApiException e) {
log.error("获取podlist异常:" + e.getResponseBody(), e);
}
return null;
}
}第二种方式:读取 k3s 的配置文件进行初始化 ApiClient
@Slf4j
@Component
public classK3sClient{
//pod内部指定存放k3s配置文件的路径
@Value("${k3sConf.path}")
private String k3sConfigPath;
/**
* 初始化API客户端
*/
@PostConstruct
privatevoidsetDefaultApiClient()throws IOException {
KubeConfig conf = KubeConfig.loadKubeConfig(new FileReader(k3sConfigPath));
ApiClient apiClient = ClientBuilder.kubeconfig(conf).build();
// 创建操作类
Configuration.setDefaultApiClient(apiClient);
}
/**
* 获取所有的Pod
*
* @return podList
*/
public V1PodList getAllPodList(){
CoreV1Api api = new CoreV1Api();
try {
V1PodList list = api.listPodForAllNamespaces(null, null, null, null, null, null, null, null, null, null);
return list;
} catch (ApiException e) {
log.error("获取podlist异常:" + e.getResponseBody(), e);
}
return null;
}
}k3s 集群的配置文件可以从宿主机挂到Pod内部的指定路径,最好挂的时候设置为只读权限,以免因为代码的问题误修改了配置文件导致k3s出问题。
接下来测试一下,看看能否成功初始化。
从结果可以看出初始化失败,从报错信息中可以看出连接 k3s 集群的IP是127.0.0.1,显然是不对的。因为初始化配置读的是配置文件,肯定是配置文件的问题。
那么从pod内部访问k3s集群的IP该怎么获取呢?我们可以进入pod内部查看环境变量可以发现。
那么只要对 KubeConfig 的 server 配置修改成 pod 内部能访问的 k3s 集群的 IP 和端口即可。可惜 KubeConfig 并不提供修改 server 的方法,那么我们可以参考源码的方式进行初始化。
@Slf4j
@Component
public classK3sClient{
//pod内部指定存放k3s配置文件的路径
@Value("${k3sConf.path}")
private String k3sConfigPath;
/**
* 初始化API客户端
*/
@PostConstruct
private void setDefaultApiClient() throws IOException {
Yaml yaml = new Yaml(new SafeConstructor());
Object config = yaml.load(new FileReader(k3sConfigPath));
Map<String, Object> configMap = (Map)config;
String currentContext = (String)configMap.get("current-context");
ArrayList<Object> contexts = (ArrayList)configMap.get("contexts");
ArrayList<Object> clusters = (ArrayList)configMap.get("clusters");
// 替换server地址
Map<String, Object> map = findObject(clusters, "default");
if (map != null) {
Map<String, Object> cluster = (Map)map.get("cluster");
cluster.put("server", String.format("https://%s:%s", System.getenv("KUBERNETES_SERVICE_HOST"),
System.getenv("KUBERNETES_SERVICE_PORT")));
}
ArrayList<Object> users = (ArrayList)configMap.get("users");
Object preferences = configMap.get("preferences");
// 以config作为入参创建的client对象,可以访问到K8S的API Server
KubeConfig kubeConfig = new KubeConfig(contexts, clusters, users);
kubeConfig.setContext(currentContext);
kubeConfig.setPreferences(preferences);
ApiClient client = ClientBuilder.kubeconfig(kubeConfig).build();
// 创建操作类
Configuration.setDefaultApiClient(client);
}
private Map<String, Object> findObject(ArrayList<Object> list, String name) {
if (list == null) {
return null;
} else {
Iterator var2 = list.iterator();
Map map;
do {
if (!var2.hasNext()) {
return null;
}
Object obj = var2.next();
map = (Map)obj;
} while(!name.equals(map.get("name")));
return map;
}
}
/**
* 获取所有的Pod
*
* @return podList
*/
public V1PodList getAllPodList() {
CoreV1Api api = new CoreV1Api();
try {
V1PodList list = api.listPodForAllNamespaces(null, null, null, null, null, null, null, null, null, null);
return list;
} catch (ApiException e) {
log.error("获取podlist异常:" + e.getResponseBody(), e);
}
return null;
}
}第三种方式:Token 认证的方式初始化 ApiClient
k3s 集群的地址同样可以通过环境变量中获取,而 token 是存放在 Pod 内的 /var/run/secrets/kubernetes.io/serviceaccount/token 文件。此方式要确认已经为Pod 创建好 serviceAccount。
@Slf4j
@Component
public classK3sClient{
//ApiServer地址
@Value("${k3s.apiServer}")
private String k3sApiServer;
//请求Token
@Value("${k3s.token}")
private String k3sToken;
//是否本机k3s集群
@Value("${k3s.local}")
private Boolean isK3sLocal;
/**
* 初始化API客户端
*/
@PostConstruct
privatevoidsetDefaultApiClient()throws IOException {
if (isK3sLocal) {
// 本机k3s集群地址
k3sApiServer = String.format("https://%s:%s", System.getenv("KUBERNETES_SERVICE_HOST"),
System.getenv("KUBERNETES_SERVICE_PORT"));
// 获取Token
FileReader fr = new FileReader("/var/run/secrets/kubernetes.io/serviceaccount/token");
BufferedReader br = new BufferedReader(fr);
k3sToken = br.readLine();
br.close();
}
ApiClient client = new ClientBuilder().setBasePath(k3sApiServer).setVerifyingSsl(false)
.setAuthentication(new AccessTokenAuthentication(k3sToken)).build();
// 创建操作类
Configuration.setDefaultApiClient(apiClient);
}
/**
* 获取所有的Pod
*
* @return podList
*/
public V1PodList getAllPodList(){
CoreV1Api api = new CoreV1Api();
try {
V1PodList list = api.listPodForAllNamespaces(null, null, null, null, null, null, null, null, null, null);
return list;
} catch (ApiException e) {
log.error("获取podlist异常:" + e.getResponseBody(), e);
}
return null;
}
}通过这几种方式,我们就可以从 pod 内部访问 k3s 集群 API 了。其实客户端初始化使用 ClientBuilder.cluster().build() 是最简单的,刚开始时我为了读配置文件还绕了个大弯,很多东西官方其实早就为用户考虑到了,所以使用之前要多看看官方文档。
下期给大家分享如何简单使用pysyncobj,敬请期待~
欢迎各位关注、留言,大家的支持就是我的动力!