最近需要做一个项目,将k8s上的日志经过java工程输出到前端,大概想了下思路,首先要java连接kubernete监控到job的日志,然后通过websocket将日志持续输出到前端,因为这些从来没有写过,所以只能一点一点积累知识,一点一点攻破,目前是java连接kubernete成功了,并取到了日志,暂时记录一下
1、java连接kubernete方法:配置文件方式,这个文件需要配置到你的工程里面,怎么获取到配置文件?问你们单位的运维,告诉他在机器上帮你取这个文件~/.kube/config 运维说这个是开发环境的超级证书,这个证书只能运维取,因为估计普通测试员没有权限,运维有管理员权限的账号才能取下来
创建k8sclient文件
package com.example.demo.config;
/**
* @Author: lxx
* @Date: 2022/8/4 下午5:43
*/
import io.kubernetes.client.custom.IntOrString;
import io.kubernetes.client.openapi.ApiClient;
import io.kubernetes.client.openapi.ApiException;
import io.kubernetes.client.openapi.apis.CoreV1Api;
import io.kubernetes.client.openapi.apis.ExtensionsV1beta1Api;
import io.kubernetes.client.openapi.apis.NetworkingV1Api;
import io.kubernetes.client.openapi.models.*;
import io.kubernetes.client.util.ClientBuilder;
import io.kubernetes.client.util.KubeConfig;
import lombok.extern.slf4j.Slf4j;
import okhttp3.Call;
import org.slf4j.Logger;
import java.io.FileReader;
import java.io.IOException;
import java.util.Map;
/**
* k8s客户端
*
* @author wanghuidong
* @date 2021/6/18 14:14
*/
@Slf4j
public class K8sClient {
/**
* k8s-api客户端
*/
private ApiClient apiClient;
/**
* 构建集群POD内通过SA访问的客户端
* loading the in-cluster config, including:
* 1. service-account CA
* 2. service-account bearer-token
* 3. service-account namespace
* 4. master endpoints(ip, port) from pre-set environment variables
*/
public K8sClient() {
try {
this.apiClient = ClientBuilder.cluster().build();
} catch (IOException e) {
log.error("构建K8s-Client异常", e);
throw new RuntimeException("构建K8s-Client异常");
}
}
/**
* 构建集群外通过UA访问的客户端
* loading the out-of-cluster config, a kubeconfig from file-system
*
* @param kubeConfigPath kube连接配置文件
*/
public K8sClient(String kubeConfigPath) {
try {
this.apiClient = ClientBuilder.kubeconfig(KubeConfig.loadKubeConfig(new FileReader(kubeConfigPath))).build();
} catch (IOException e) {
log.error("读取kubeConfigPath异常", e);
throw new RuntimeException("读取kubeConfigPath异常");
} catch (Exception e) {
log.error("构建K8s-Client异常", e);
throw new RuntimeException("构建K8s-Client异常");
}
}
/**
* 获取所有的Pod
*
* @return podList
*/
public V1PodList getAllPodList() throws Exception {
// new a CoreV1Api
CoreV1Api api = new CoreV1Api(apiClient);
// invokes the CoreV1Api client
// V1Pod pod = api.readNamespacedPod("reconcile-l75kp","dev5",null,null,null);
try {
//V1PodList list = api.listPodForAllNamespaces(null, null, null, null, null, null, null, null, null, null);
V1PodList list = api.listNamespacedPod("dev5",null,null,null,null,null,null,null,null,null,null);
return list;
} catch (ApiException e) {
log.error("获取podlist异常:" + e.getResponseBody(), e);
}
return null;
}
/**
* 创建k8s service
*
* @param namespace 命名空间
* @param serviceName 服务名称
* @param port 服务端口号(和目标pod的端口号一致)
* @param selector pod标签选择器
* @return 创建成功的service对象
*/
public V1Service createService(String namespace, String serviceName, Integer port, Map<String, String> selector) {
//构建service的yaml对象
V1Service svc = new V1ServiceBuilder()
.withNewMetadata()
.withName(serviceName)
.endMetadata()
.withNewSpec()
.addNewPort()
.withProtocol("TCP")
.withPort(port)
.withTargetPort(new IntOrString(port))
.endPort()
.withSelector(selector)
.endSpec()
.build();
// Deployment and StatefulSet is defined in apps/v1, so you should use AppsV1Api instead of CoreV1API
CoreV1Api api = new CoreV1Api(apiClient);
V1Service v1Service = null;
try {
v1Service = api.createNamespacedService(namespace, svc, null, null, null);
} catch (ApiException e) {
log.error("创建service异常:" + e.getResponseBody(), e);
} catch (Exception e) {
log.error("创建service系统异常:", e);
}
return v1Service;
}
/**
* 创建k8s V1Ingress
*
* @param namespace 命名空间
* @param ingressName ingress名称
* @param annotations ingress注解
* @param path 匹配的路径
* @param serviceName 路由到的服务名称
* @param servicePort 路由到的服务端口
* @return 创建成功的ingress对象
*/
public V1Ingress createV1Ingress(String namespace, String ingressName, Map<String, String> annotations, String path,
String serviceName, Integer servicePort) {
//构建ingress的yaml对象
V1Ingress ingress = new V1IngressBuilder()
.withNewMetadata()
.withName(ingressName)
.withAnnotations(annotations)
.endMetadata()
.withNewSpec()
.addNewRule()
.withHttp(new V1HTTPIngressRuleValueBuilder().addToPaths(new V1HTTPIngressPathBuilder()
.withPath(path)
.withPathType("Prefix")
.withBackend(new V1IngressBackendBuilder()
.withService(new V1IngressServiceBackendBuilder()
.withName(serviceName)
.withPort(new V1ServiceBackendPortBuilder()
.withNumber(servicePort).build()).build()).build()).build()).build())
.endRule()
.endSpec()
.build();
//调用对应的API执行创建ingress的操作
NetworkingV1Api api = new NetworkingV1Api(apiClient);
V1Ingress v1Ingress = null;
try {
v1Ingress = api.createNamespacedIngress(namespace, ingress, null, null, null);
} catch (ApiException e) {
log.error("创建ingress异常:" + e.getResponseBody(), e);
} catch (Exception e) {
log.error("创建ingress系统异常:", e);
}
return v1Ingress;
}
/**
* 创建k8s ExtensionIngress
*
* @param namespace 命名空间
* @param ingressName ingress名称
* @param annotations ingress注解
* @param path 匹配的路径
* @param serviceName 路由到的服务名称
* @param servicePort 路由到的服务端口
* @return 创建成功的ingress对象
*/
public ExtensionsV1beta1Ingress createExtensionIngress(String namespace, String ingressName, Map<String, String> annotations, String path,
String serviceName, Integer servicePort) {
//构建ingress的yaml对象
ExtensionsV1beta1Ingress ingress = new ExtensionsV1beta1IngressBuilder()
.withNewMetadata()
.withName(ingressName)
.withAnnotations(annotations)
.endMetadata()
.withNewSpec()
.addNewRule()
.withHttp(new ExtensionsV1beta1HTTPIngressRuleValueBuilder().addToPaths(new ExtensionsV1beta1HTTPIngressPathBuilder()
.withPath(path)
.withBackend(new ExtensionsV1beta1IngressBackendBuilder()
.withServiceName(serviceName)
.withServicePort(new IntOrString(servicePort)).build()).build()).build())
.endRule()
.endSpec()
.build();
//调用对应的API执行创建ingress的操作
ExtensionsV1beta1Api api = new ExtensionsV1beta1Api(apiClient);
ExtensionsV1beta1Ingress extensionsV1beta1Ingress = null;
try {
extensionsV1beta1Ingress = api.createNamespacedIngress(namespace, ingress, null, null, null);
} catch (ApiException e) {
log.error("创建ingress异常:" + e.getResponseBody(), e);
} catch (Exception e) {
log.error("创建ingress系统异常:", e);
}
return extensionsV1beta1Ingress;
}
public V1Pod readNamespacedPod(String podName,String spaceName)throws Exception{
CoreV1Api api = new CoreV1Api(apiClient);
V1Pod pod = api.readNamespacedPod(podName,spaceName,null,null,null);
return pod;
}
public Call readNamespacedPodLogCall (String podName, String spaceName, Integer tailLines)throws Exception{
CoreV1Api api = new CoreV1Api(apiClient);
Call log = api.readNamespacedPodLogCall(podName,spaceName,null,true,null,null,null,null,null,100,null,null);
return log;
}
public String readNamespacedPodLog (String podName, String spaceName, Integer tailLines)throws Exception{
// CoreV1Api api = new CoreV1Api(apiClient);
CoreV1Api api = new CoreV1Api(apiClient);
String log = api.readNamespacedPodLog(podName,spaceName,null,true,false,1024,"true",false,null,10,false);
return log;
}
}
写单元测试
package com.example.demo;
import com.example.demo.config.K8sClient;
import io.kubernetes.client.openapi.models.V1Pod;
import io.kubernetes.client.openapi.models.V1PodList;
import org.junit.Test;
import java.io.File;
/**
* @Author:
* @Date: 2022/8/4 下午5:31
*/
public class KuberneteTest {
@Test
public void testK8sLog()throws Exception{
String kubeConfigPath = "/Users/xxx/k8s/config";
if (!new File(kubeConfigPath).exists()) {
System.out.println("kubeConfig不存在,跳过");
return;
}
K8sClient k8sClient = new K8sClient(kubeConfigPath);
String call=k8sClient.readNamespacedPodLog("test-l75kp","dev5",100);
System.out.println(call);
}
}
这样就可以连上kubernete啦,连接k8的用户名密码什么的都在/Users/xxx/k8s/config这个配置文件中,连上之后读取 space为dev5 job名为test-l75kp的日志
不要觉得代码多,亲自试一试吧!