diff --git a/ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/utils/K8sClientUtil.java b/ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/utils/K8sClientUtil.java index aea538f7..06e40622 100644 --- a/ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/utils/K8sClientUtil.java +++ b/ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/utils/K8sClientUtil.java @@ -33,6 +33,8 @@ import java.util.*; @Slf4j @Component public class K8sClientUtil { + private String http; + private String token; /** * k8s-api客户端 */ @@ -48,6 +50,8 @@ public class K8sClientUtil { */ @Autowired public K8sClientUtil(@Value("${k8s.http}") String http, @Value("${k8s.token}") String token) { + this.http = http; + this.token = token; try { this.apiClient = new ClientBuilder(). setBasePath(http).setVerifyingSsl(false). @@ -132,8 +136,8 @@ public class K8sClientUtil { if (v1ServiceList!=null) { for (V1Service svc : v1ServiceList.getItems()) { if (StringUtils.equals(svc.getMetadata().getName(), serviceName)) { - // PVC 已存在 - return svc; + // SVC 已存在 + return svc; } } } @@ -373,9 +377,48 @@ public class K8sClientUtil { // 创建配置好的Pod public Integer createConfiguredPod(String podName, String namespace, Integer port, String mountPath, V1PersistentVolumeClaim pvc, String image, String dataPvcName, String datasetPath, String modelPath) { + + //设置选择节点,pod反亲和性 Map selector = new LinkedHashMap<>(); - selector.put("k8s-jupyter", podName); + selector.put("k8s-jupyter", "CPU-GPU"); + Map 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); V1PodList v1PodList = null; try { @@ -398,8 +441,8 @@ public class K8sClientUtil { // 配置卷和卷挂载 List volumeMounts = new ArrayList<>(); 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 volumes = new ArrayList<>(); volumes.add(new V1Volume().name("workspace").persistentVolumeClaim(new V1PersistentVolumeClaimVolumeSource().claimName(pvc.getMetadata().getName()))); @@ -419,6 +462,8 @@ public class K8sClientUtil { .withVolumeMounts(volumeMounts) .endContainer() .withVolumes(volumes) + .withNodeSelector(nodeSelector) + .withAffinity(v1Affinity) .endSpec() .build(); @@ -502,6 +547,28 @@ public class K8sClientUtil { 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) { CoreV1Api api = new CoreV1Api(apiClient); try {