| @@ -33,6 +33,8 @@ import java.util.*; | |||||
| @Slf4j | @Slf4j | ||||
| @Component | @Component | ||||
| public class K8sClientUtil { | public class K8sClientUtil { | ||||
| private String http; | |||||
| private String token; | |||||
| /** | /** | ||||
| * k8s-api客户端 | * k8s-api客户端 | ||||
| */ | */ | ||||
| @@ -48,6 +50,8 @@ public class K8sClientUtil { | |||||
| */ | */ | ||||
| @Autowired | @Autowired | ||||
| public K8sClientUtil(@Value("${k8s.http}") String http, @Value("${k8s.token}") String token) { | public K8sClientUtil(@Value("${k8s.http}") String http, @Value("${k8s.token}") String token) { | ||||
| this.http = http; | |||||
| this.token = token; | |||||
| try { | try { | ||||
| this.apiClient = new ClientBuilder(). | this.apiClient = new ClientBuilder(). | ||||
| setBasePath(http).setVerifyingSsl(false). | setBasePath(http).setVerifyingSsl(false). | ||||
| @@ -132,8 +136,8 @@ public class K8sClientUtil { | |||||
| if (v1ServiceList!=null) { | if (v1ServiceList!=null) { | ||||
| for (V1Service svc : v1ServiceList.getItems()) { | for (V1Service svc : v1ServiceList.getItems()) { | ||||
| if (StringUtils.equals(svc.getMetadata().getName(), serviceName)) { | if (StringUtils.equals(svc.getMetadata().getName(), serviceName)) { | ||||
| // PVC 已存在 | |||||
| return svc; | |||||
| // SVC 已存在 | |||||
| return svc; | |||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| @@ -373,9 +377,48 @@ public class K8sClientUtil { | |||||
| // 创建配置好的Pod | // 创建配置好的Pod | ||||
| public Integer createConfiguredPod(String podName, String namespace, Integer port, String mountPath, V1PersistentVolumeClaim pvc, String image, String dataPvcName, String datasetPath, String modelPath) { | public Integer createConfiguredPod(String podName, String namespace, Integer port, String mountPath, V1PersistentVolumeClaim pvc, String image, String dataPvcName, String datasetPath, String modelPath) { | ||||
| //设置选择节点,pod反亲和性 | |||||
| Map<String, String> selector = new LinkedHashMap<>(); | Map<String, String> selector = new LinkedHashMap<>(); | ||||
| selector.put("k8s-jupyter", podName); | |||||
| selector.put("k8s-jupyter", "CPU-GPU"); | |||||
| Map<String, String> nodeSelector = new LinkedHashMap<>(); | |||||
| nodeSelector.put("resource-type", "CPU-GPU"); | |||||
| V1LabelSelectorRequirement labelSelectorRequirement = new V1LabelSelectorRequirement() | |||||
| .key("k8s-jupyter").operator("NotIn").values(Collections.singletonList("CPU-GPU")); | |||||
| V1LabelSelector labelSelector = new V1LabelSelector() | |||||
| .matchExpressions(Collections.singletonList(labelSelectorRequirement)); | |||||
| V1PodAffinityTerm podAffinityTerm = new V1PodAffinityTerm() | |||||
| .labelSelector(labelSelector) | |||||
| .namespaces(Collections.singletonList(namespace)) | |||||
| .topologyKey("kubernetes.io/hostname"); | |||||
| V1PodAffinity podAffinity = new V1PodAffinity() | |||||
| .requiredDuringSchedulingIgnoredDuringExecution(Collections.singletonList(podAffinityTerm)); | |||||
| V1LabelSelectorRequirement antiLabelSelectorRequirement = new V1LabelSelectorRequirement() | |||||
| .key("k8s-jupyter").operator("In").values(Collections.singletonList("CPU-GPU")); | |||||
| V1LabelSelector antiLabelSelector = new V1LabelSelector() | |||||
| .matchExpressions(Collections.singletonList(antiLabelSelectorRequirement)); | |||||
| V1PodAffinityTerm antiPodAffinityTerm = new V1PodAffinityTerm() | |||||
| .labelSelector(antiLabelSelector) | |||||
| .namespaces(Collections.singletonList(namespace)) | |||||
| .topologyKey("kubernetes.io/hostname"); | |||||
| // V1WeightedPodAffinityTerm weightedPodAffinityTerm = new V1WeightedPodAffinityTerm().weight(100).podAffinityTerm(podAffinityTerm); | |||||
| V1PodAntiAffinity podAntiAffinity = new V1PodAntiAffinity() | |||||
| .requiredDuringSchedulingIgnoredDuringExecution(Collections.singletonList(antiPodAffinityTerm)); | |||||
| V1Affinity v1Affinity = new V1Affinity() | |||||
| .podAffinity(podAffinity) | |||||
| .podAntiAffinity(podAntiAffinity); | |||||
| // 创建Pod | |||||
| CoreV1Api api = new CoreV1Api(apiClient); | CoreV1Api api = new CoreV1Api(apiClient); | ||||
| V1PodList v1PodList = null; | V1PodList v1PodList = null; | ||||
| try { | try { | ||||
| @@ -398,8 +441,8 @@ public class K8sClientUtil { | |||||
| // 配置卷和卷挂载 | // 配置卷和卷挂载 | ||||
| List<V1VolumeMount> volumeMounts = new ArrayList<>(); | List<V1VolumeMount> volumeMounts = new ArrayList<>(); | ||||
| volumeMounts.add(new V1VolumeMount().name("workspace").mountPath(mountPath)); | volumeMounts.add(new V1VolumeMount().name("workspace").mountPath(mountPath)); | ||||
| volumeMounts.add(new V1VolumeMount().name("minio-pvc").mountPath("/datasets").subPath(datasetPath).readOnly(true)); | |||||
| volumeMounts.add(new V1VolumeMount().name("minio-pvc").mountPath("/model").subPath(modelPath).readOnly(true)); | |||||
| volumeMounts.add(new V1VolumeMount().name("minio-pvc").mountPath("/opt/data").subPath(datasetPath).readOnly(true)); | |||||
| volumeMounts.add(new V1VolumeMount().name("minio-pvc").mountPath("/opt/model").subPath(modelPath).readOnly(true)); | |||||
| List<V1Volume> volumes = new ArrayList<>(); | List<V1Volume> volumes = new ArrayList<>(); | ||||
| volumes.add(new V1Volume().name("workspace").persistentVolumeClaim(new V1PersistentVolumeClaimVolumeSource().claimName(pvc.getMetadata().getName()))); | volumes.add(new V1Volume().name("workspace").persistentVolumeClaim(new V1PersistentVolumeClaimVolumeSource().claimName(pvc.getMetadata().getName()))); | ||||
| @@ -419,6 +462,8 @@ public class K8sClientUtil { | |||||
| .withVolumeMounts(volumeMounts) | .withVolumeMounts(volumeMounts) | ||||
| .endContainer() | .endContainer() | ||||
| .withVolumes(volumes) | .withVolumes(volumes) | ||||
| .withNodeSelector(nodeSelector) | |||||
| .withAffinity(v1Affinity) | |||||
| .endSpec() | .endSpec() | ||||
| .build(); | .build(); | ||||
| @@ -502,6 +547,28 @@ public class K8sClientUtil { | |||||
| return pod.getStatus().getPhase(); | return pod.getStatus().getPhase(); | ||||
| } | } | ||||
| /** | |||||
| * 根据Pod的名称和Namespace查询Pod的容器信息 | |||||
| * @param podName Pod的名称 | |||||
| * @param namespace Pod所在的Namespace | |||||
| */ | |||||
| public String getPodContainerId(String podName, String namespace) throws Exception { | |||||
| CoreV1Api api = new CoreV1Api(apiClient); | |||||
| V1Pod pod = api.readNamespacedPod(podName, namespace, null, null, null); | |||||
| if(pod.getStatus().getContainerStatuses().size() !=1){ | |||||
| throw new RuntimeException("容器错误"); | |||||
| } | |||||
| String containerId = pod.getStatus().getContainerStatuses().get(0).getContainerID().split("//")[1]; | |||||
| return containerId; | |||||
| } | |||||
| public String getHostIp(String podName, String namespace) throws Exception { | |||||
| CoreV1Api api = new CoreV1Api(apiClient); | |||||
| V1Pod pod = api.readNamespacedPod(podName, namespace, null, null, null); | |||||
| return pod.getStatus().getHostIP(); | |||||
| } | |||||
| public String getPodLogs(String podName,String namespace,String container,int line) { | public String getPodLogs(String podName,String namespace,String container,int line) { | ||||
| CoreV1Api api = new CoreV1Api(apiClient); | CoreV1Api api = new CoreV1Api(apiClient); | ||||
| try { | try { | ||||