From c594dca0ec33f6a26043e4fc26610ecba13e4825 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E8=A5=BF=E5=A4=A7=E9=94=90?= <1070211640@qq.com>
Date: Tue, 27 Aug 2024 15:55:13 +0800
Subject: [PATCH] =?UTF-8?q?1=E3=80=81=E6=B7=BB=E5=8A=A0=E9=80=89=E6=8B=A9?=
=?UTF-8?q?=E9=95=9C=E5=83=8F=E5=90=AF=E5=8A=A8=E7=8E=AF=E5=A2=83=E5=8A=9F?=
=?UTF-8?q?=E8=83=BD=E3=80=82=202=E3=80=81pod=E9=80=89=E6=8B=A9GPU?=
=?UTF-8?q?=E8=8A=82=E7=82=B9=E9=83=A8=E7=BD=B2=E3=80=82=203=E3=80=81?=
=?UTF-8?q?=E5=A2=9E=E5=8A=A0=E4=BF=9D=E5=AD=98=E9=95=9C=E5=83=8F=E6=8E=A5?=
=?UTF-8?q?=E5=8F=A3=E3=80=82?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
ruoyi-modules/management-platform/pom.xml | 13 ++-
.../controller/image/ImageController.java | 9 +-
.../ruoyi/platform/service/ImageService.java | 3 +-
.../service/impl/ImageServiceImpl.java | 29 ++++++
.../service/impl/JupyterServiceImpl.java | 2 +-
.../platform/utils/DockerClientUtil.java | 98 +++++++++++++++++++
.../ruoyi/platform/utils/K8sClientUtil.java | 28 +++++-
.../java/com/ruoyi/platform/vo/ImageVo.java | 14 ++-
ruoyi-modules/ruoyi-job/pom.xml | 10 +-
9 files changed, 196 insertions(+), 10 deletions(-)
create mode 100644 ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/utils/DockerClientUtil.java
diff --git a/ruoyi-modules/management-platform/pom.xml b/ruoyi-modules/management-platform/pom.xml
index 37234ad0..c004f9ae 100644
--- a/ruoyi-modules/management-platform/pom.xml
+++ b/ruoyi-modules/management-platform/pom.xml
@@ -140,7 +140,12 @@
com.github.docker-java
docker-java
- 3.1.1
+ 3.2.13
+
+
+ com.github.docker-java
+ docker-java-transport-httpclient5
+ 3.2.13
com.baomidou
@@ -216,6 +221,12 @@
3.0.8
compile
+
+ commons-lang
+ commons-lang
+ 2.6
+ compile
+
diff --git a/ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/controller/image/ImageController.java b/ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/controller/image/ImageController.java
index a0b117d8..d389bad8 100644
--- a/ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/controller/image/ImageController.java
+++ b/ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/controller/image/ImageController.java
@@ -78,16 +78,15 @@ public class ImageController extends BaseController {
* @param image 实体
* @return 新增结果
*/
- @PostMapping
@ApiOperation("新增镜像,不包含镜像版本")
public GenericsAjaxResult add(@RequestBody Image image) {
return genericsSuccess(this.imageService.insert(image));
}
/**
- * 新增镜像和版本
*
* @param imageVo 实体
+ * 新增镜像和版本 @PostMapping
* @return 新增结果
*/
@PostMapping("/addImageAndVersion")
@@ -149,5 +148,11 @@ public class ImageController extends BaseController {
return genericsSuccess(this.imageService.uploadImageFiles(file));
}
+
+ @PostMapping("/saveImage")
+ @ApiOperation(value = "保存环境为镜像", notes = "docker commit方式保存,并推送到horbor")
+ public GenericsAjaxResult saveImage(ImageVo imageVo){
+ return genericsSuccess(this.imageService.saveImage(imageVo));
+ }
}
diff --git a/ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/service/ImageService.java b/ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/service/ImageService.java
index 59614ac9..fb8984ed 100644
--- a/ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/service/ImageService.java
+++ b/ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/service/ImageService.java
@@ -93,6 +93,5 @@ public interface ImageService {
Map createImageFromNet(String imageName, String imageTag, String NetPath) throws Exception;
Map uploadImageFiles(MultipartFile file) throws Exception;
-
-
+ String saveImage(ImageVo imageVo);
}
diff --git a/ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/service/impl/ImageServiceImpl.java b/ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/service/impl/ImageServiceImpl.java
index d95609ee..17bfdd48 100644
--- a/ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/service/impl/ImageServiceImpl.java
+++ b/ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/service/impl/ImageServiceImpl.java
@@ -9,12 +9,14 @@ import com.ruoyi.platform.mapper.ImageVersionDao;
import com.ruoyi.platform.service.ImageService;
import com.ruoyi.platform.service.ImageVersionService;
import com.ruoyi.platform.service.MinioService;
+import com.ruoyi.platform.utils.DockerClientUtil;
import com.ruoyi.platform.utils.FileUtil;
import com.ruoyi.platform.utils.K8sClientUtil;
import com.ruoyi.platform.vo.ImageVo;
import com.ruoyi.system.api.model.LoginUser;
import io.kubernetes.client.openapi.models.V1PersistentVolumeClaim;
import io.kubernetes.client.openapi.models.V1Pod;
+import lombok.Synchronized;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.domain.Page;
@@ -50,6 +52,9 @@ public class ImageServiceImpl implements ImageService {
private ImageVersionDao imageVersionDao;
@Resource
private K8sClientUtil k8sClientUtil;
+ @Resource
+ private DockerClientUtil dockerClientUtil;
+
@Resource
private MinioService minioService;
@Value("${harbor.bucketName}")
@@ -75,6 +80,8 @@ public class ImageServiceImpl implements ImageService {
private String proxyUrl;
@Value("${minio.pvcName}")
private String pvcName;
+ @Value("${jupyter.namespace}")
+ private String namespace;
/**
* 通过ID查询单条数据
*
@@ -350,4 +357,26 @@ public class ImageServiceImpl implements ImageService {
String path = loginUser.getUsername()+"/"+file.getOriginalFilename();
return minioService.uploadFile(bucketName, path, file);
}
+
+ @Override
+ @Synchronized
+ public String saveImage(ImageVo imageVo) {
+ if(imageDao.getByName(imageVo.getName()) != null){
+ throw new IllegalStateException("镜像名称已存在");
+ }
+ LoginUser loginUser = SecurityUtils.getLoginUser();
+ String username = loginUser.getUsername().toLowerCase();
+ String podName = username +"-editor-pod" + "-" + imageVo.getDevEnvironmentId();
+
+ try {
+ String containerId = k8sClientUtil.getPodContainerId(podName, namespace);
+ String hostIp = k8sClientUtil.getHostIp(podName, namespace);
+
+ dockerClientUtil.commitImage(imageVo,containerId,hostIp,username);
+ dockerClientUtil.pushImageToHorbor(imageVo,hostIp);
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ return null;
+ }
}
diff --git a/ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/service/impl/JupyterServiceImpl.java b/ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/service/impl/JupyterServiceImpl.java
index be55468a..aa59b6ae 100644
--- a/ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/service/impl/JupyterServiceImpl.java
+++ b/ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/service/impl/JupyterServiceImpl.java
@@ -100,7 +100,7 @@ public class JupyterServiceImpl implements JupyterService {
//TODO 设置镜像可配置,这里先用默认镜像启动pod
// 调用修改后的 createPod 方法,传入额外的参数
- Integer podPort = k8sClientUtil.createConfiguredPod(podName, namespace, port, mountPath, pvc, image, minioPvcName, datasetPath, modelPath);
+ Integer podPort = k8sClientUtil.createConfiguredPod(podName, namespace, port, mountPath, pvc, devEnvironment.getImage(), minioPvcName, datasetPath, modelPath);
String url = masterIp + ":" + podPort;
redisService.setCacheObject(podName,masterIp + ":" + podPort);
devEnvironment.setStatus("Pending");
diff --git a/ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/utils/DockerClientUtil.java b/ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/utils/DockerClientUtil.java
new file mode 100644
index 00000000..15a1134c
--- /dev/null
+++ b/ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/utils/DockerClientUtil.java
@@ -0,0 +1,98 @@
+package com.ruoyi.platform.utils;
+
+import com.github.dockerjava.api.DockerClient;
+import com.github.dockerjava.api.command.CommitCmd;
+import com.github.dockerjava.api.model.AuthConfig;
+import com.github.dockerjava.core.DefaultDockerClientConfig;
+import com.github.dockerjava.core.DockerClientConfig;
+import com.github.dockerjava.core.DockerClientImpl;
+import com.github.dockerjava.httpclient5.ApacheDockerHttpClient;
+import com.github.dockerjava.transport.DockerHttpClient;
+import com.ruoyi.platform.vo.ImageVo;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.stereotype.Component;
+
+import java.time.Duration;
+
+@Slf4j
+@Component
+public class DockerClientUtil {
+
+ @Value("${harbor.bucketName}")
+ private String bucketName;
+ @Value("${harbor.repository}")
+ private String repository;
+ @Value("${harbor.harborUrl}")
+ private String harborUrl;
+ @Value("${harbor.harborUser}")
+ private String harborUser;
+ @Value("${harbor.harborpassword}")
+ private String harborpassword;
+
+ public DockerClient getDockerClient(String dockerServerUrl) {
+
+ //创建DefaultDockerClientConfig(指定docker服务器的配置)
+ DockerClientConfig config = DefaultDockerClientConfig
+ .createDefaultConfigBuilder()
+ .withDockerHost("tcp://"+ dockerServerUrl +":2375")
+ .withDockerTlsVerify(false)
+ .withApiVersion("1.40")
+// .withDockerCertPath(dcokerCertPath)
+// .withRegistryUsername(registryUser)
+// .withRegistryPassword(registryPass)
+// .withRegistryEmail(registryMail)
+// .withRegistryUrl(registryUrl)
+ .build();
+
+ //创建DockerHttpClient
+ DockerHttpClient httpClient = new ApacheDockerHttpClient.Builder()
+ .dockerHost(config.getDockerHost())
+ .sslConfig(config.getSSLConfig())
+ .maxConnections(1000)
+ .connectionTimeout(Duration.ofSeconds(300))
+ .responseTimeout(Duration.ofSeconds(450))
+ .build();
+
+ //创建DockerClient
+ return DockerClientImpl.getInstance(config, httpClient);
+
+ }
+
+ public String commitImage(ImageVo imageVo, String containerId, String hostIp, String userName) {
+ DockerClient dockerClient = getDockerClient(hostIp);
+
+// dockerClient.startContainerCmd(containerId).exec();
+ // 提交容器为镜像,这里的"new_image"和"new_tag"是新镜像的名字和标签
+ CommitCmd commitCmd = dockerClient.commitCmd(containerId)
+ .withRepository(imageVo.getName())
+ .withTag(imageVo.getTagName())
+ .withAuthor(userName)
+ .withMessage(imageVo.getDescription());
+ String exec = commitCmd.exec();
+ return exec;
+ }
+
+ public void pushImageToHorbor(ImageVo imageVo, String hostIp) {
+
+ DockerClient dockerClient = getDockerClient(hostIp);
+ //Harbor登录信息
+ AuthConfig autoConfig = new AuthConfig().withRegistryAddress(harborUrl).withUsername(harborUser).withPassword(harborpassword);
+
+
+ String localImageName = imageVo.getName() + ":" + imageVo.getTagName();
+ String imageName = harborUrl + "/" + bucketName + "/" + imageVo.getName();
+
+ //给镜像打上tag
+ dockerClient.tagImageCmd(localImageName, imageName, imageVo.getTagName()).exec();
+ //推送镜像至镜像仓库
+ try {
+ dockerClient.pushImageCmd(imageName).withAuthConfig(autoConfig).start().awaitCompletion();
+ //push成功后,删除本地加载的镜像
+ dockerClient.removeImageCmd(localImageName).exec();
+ } catch (InterruptedException e) {
+ throw new RuntimeException("推送镜像失败:"+e);
+ }
+
+ }
+}
\ No newline at end of file
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 23903e2a..e2f1f6ac 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
@@ -136,7 +136,7 @@ public class K8sClientUtil {
if (v1ServiceList!=null) {
for (V1Service svc : v1ServiceList.getItems()) {
if (StringUtils.equals(svc.getMetadata().getName(), serviceName)) {
- // PVC 已存在
+ // SVC 已存在
return svc;
}
}
@@ -380,6 +380,9 @@ public class K8sClientUtil {
Map selector = new LinkedHashMap<>();
selector.put("k8s-jupyter", podName);
+ Map nodeSelector = new LinkedHashMap<>();
+ nodeSelector.put("resource-type", "CPU-GPU");
+
CoreV1Api api = new CoreV1Api(apiClient);
V1PodList v1PodList = null;
try {
@@ -423,6 +426,7 @@ public class K8sClientUtil {
.withVolumeMounts(volumeMounts)
.endContainer()
.withVolumes(volumes)
+ .withNodeSelector(nodeSelector)
.endSpec()
.build();
@@ -506,6 +510,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 {
diff --git a/ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/vo/ImageVo.java b/ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/vo/ImageVo.java
index c4b313c2..6ee88868 100644
--- a/ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/vo/ImageVo.java
+++ b/ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/vo/ImageVo.java
@@ -43,7 +43,7 @@ public class ImageVo implements Serializable {
/**
* 镜像tag名称
*/
- @ApiModelProperty(name = "tag_name")
+ @ApiModelProperty(name = "tagName")
private String tagName;
/**
@@ -64,6 +64,8 @@ public class ImageVo implements Serializable {
private String path;
+ @ApiModelProperty(value = "环境id")
+ private String devEnvironmentId;
// public Integer getId() {
// return id;
// }
@@ -147,7 +149,17 @@ public class ImageVo implements Serializable {
public String getPath() {
return path;
}
+
public void setPath(String path) {
this.path = path;
}
+
+ public String getDevEnvironmentId() {
+ return devEnvironmentId;
+ }
+
+ public void setDevEnvironmentId(String devEnvironmentId) {
+ this.devEnvironmentId = devEnvironmentId;
+ }
+
}
diff --git a/ruoyi-modules/ruoyi-job/pom.xml b/ruoyi-modules/ruoyi-job/pom.xml
index e295d995..7b22bc72 100644
--- a/ruoyi-modules/ruoyi-job/pom.xml
+++ b/ruoyi-modules/ruoyi-job/pom.xml
@@ -65,7 +65,7 @@
com.mysql
mysql-connector-j
-
+
com.ruoyi
@@ -77,7 +77,13 @@
com.ruoyi
ruoyi-common-swagger
-
+
+
+
+ com.ruoyi
+ ruoyi-common-datasource
+
+