| @@ -118,12 +118,20 @@ public class ImageController { | |||||
| return AjaxResult.success(this.imageService.removeById(id)); | return AjaxResult.success(this.imageService.removeById(id)); | ||||
| } | } | ||||
| @PostMapping("/net") | |||||
| @ApiOperation("从本地上传构建镜像") | |||||
| public AjaxResult createImageFromNet(@RequestParam("name") String imageName, | |||||
| @RequestParam("tag") String imageTag, | |||||
| @RequestParam("path") String path) throws Exception { | |||||
| return AjaxResult.success(this.imageService.createImageFromNet(imageName,imageTag,path)); | |||||
| } | |||||
| @PostMapping("/local") | @PostMapping("/local") | ||||
| @ApiOperation("从本地上传构建镜像") | @ApiOperation("从本地上传构建镜像") | ||||
| public AjaxResult createImageFromLocal(@RequestParam("name") String imageName, | public AjaxResult createImageFromLocal(@RequestParam("name") String imageName, | ||||
| @RequestParam("tag") String imageTag, | @RequestParam("tag") String imageTag, | ||||
| @RequestParam("description") String imageDescription){ | |||||
| return AjaxResult.success(this.imageService.createImageFromLocal(imageName,imageTag,imageDescription)); | |||||
| @RequestParam("path") String path) throws Exception { | |||||
| return AjaxResult.success(this.imageService.createImageFromLocal(imageName,imageTag,path)); | |||||
| } | } | ||||
| @@ -8,4 +8,8 @@ import org.springframework.web.bind.annotation.RestController; | |||||
| @RequestMapping("tensorBoard") | @RequestMapping("tensorBoard") | ||||
| @Api("流水线管理") | @Api("流水线管理") | ||||
| public class tensorBoard { | public class tensorBoard { | ||||
| //状态查询接口 | |||||
| //启动tensorBoard接口 | |||||
| } | } | ||||
| @@ -71,11 +71,11 @@ public interface ImageService { | |||||
| * 本地上传构建镜像 | * 本地上传构建镜像 | ||||
| * @param imageName | * @param imageName | ||||
| * @param imageTag | * @param imageTag | ||||
| * @param imageDescription | |||||
| * @param path | |||||
| * @return | * @return | ||||
| */ | */ | ||||
| String createImageFromLocal(String imageName, String imageTag, String imageDescription); | |||||
| String createImageFromLocal(String imageName, String imageTag, String path) throws Exception; | |||||
| String createImageFromNet(String imageName, String imageTag, String NetPath) throws Exception; | |||||
| Map<String, String> uploadImageFiles(MultipartFile file) throws Exception; | Map<String, String> uploadImageFiles(MultipartFile file) throws Exception; | ||||
| } | } | ||||
| @@ -8,9 +8,12 @@ import com.ruoyi.platform.service.ImageService; | |||||
| import com.ruoyi.platform.service.ImageVersionService; | import com.ruoyi.platform.service.ImageVersionService; | ||||
| import com.ruoyi.platform.service.MinioService; | import com.ruoyi.platform.service.MinioService; | ||||
| import com.ruoyi.platform.utils.FileUtil; | import com.ruoyi.platform.utils.FileUtil; | ||||
| import com.ruoyi.platform.utils.K8sClientUtil; | |||||
| import com.ruoyi.platform.vo.ImageVo; | import com.ruoyi.platform.vo.ImageVo; | ||||
| import com.ruoyi.system.api.model.LoginUser; | import com.ruoyi.system.api.model.LoginUser; | ||||
| import io.kubernetes.client.openapi.models.V1Pod; | |||||
| import org.apache.commons.lang3.StringUtils; | import org.apache.commons.lang3.StringUtils; | ||||
| import org.springframework.beans.factory.annotation.Value; | |||||
| import org.springframework.stereotype.Service; | import org.springframework.stereotype.Service; | ||||
| import org.springframework.data.domain.Page; | import org.springframework.data.domain.Page; | ||||
| import org.springframework.data.domain.PageImpl; | import org.springframework.data.domain.PageImpl; | ||||
| @@ -43,11 +46,22 @@ public class ImageServiceImpl implements ImageService { | |||||
| @Resource | @Resource | ||||
| private ImageVersionDao imageVersionDao; | private ImageVersionDao imageVersionDao; | ||||
| @Resource | @Resource | ||||
| private MinioService minioService; | private MinioService minioService; | ||||
| @Value("${harbor.bucketName}") | |||||
| private String 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; | |||||
| @Value("${harbor.deploymentName}") | |||||
| private String deploymentName; | |||||
| @Value("${harbor.serviceNS}") | |||||
| private String serviceNS; | |||||
| /** | /** | ||||
| * 通过ID查询单条数据 | * 通过ID查询单条数据 | ||||
| * | * | ||||
| @@ -178,15 +192,68 @@ public class ImageServiceImpl implements ImageService { | |||||
| } | } | ||||
| @Override | @Override | ||||
| public String createImageFromLocal(String imageName, String imageTag, String fileName) { | |||||
| // TODO 检查环境是否存在,如果不存在,请开启容器 | |||||
| // TODO 在容器的/data/admin 目录下执行命令 docker load -i fileName 得到返回的镜像名字name:tag | |||||
| // TODO 在容器里执行 docker tag name:tag nexus3.kube-system.svc:8083/imageName:imageTag | |||||
| public String createImageFromNet(String imageName, String imageTag, String netPath) throws Exception { | |||||
| // 得到容器 | |||||
| V1Pod pod = K8sClientUtil.getNSPodList(serviceNS, deploymentName); | |||||
| if (pod == null) { | |||||
| throw new Exception("镜像推送服务不存在"); | |||||
| } | |||||
| String loginCmd = "docker login -u " + harborUser +" -p "+harborpassword+" "+harborUrl; | |||||
| // 执行命令 docker login -u admin -p Harbor12345 172.20.32.187 | |||||
| String loginlog = K8sClientUtil.executeCommand(pod,loginCmd); | |||||
| // 在这个容器的/data/admin 目录下执行命令 docker load -i fileName 得到返回的镜像名字name:tag | |||||
| String username = SecurityUtils.getLoginUser().getUsername(); | |||||
| // | |||||
| String logs2 = K8sClientUtil.executeCommand(pod,"docker pull "+netPath); | |||||
| // 在容器里执行 docker tag name:tag nexus3.kube-system.svc:8083/imageName:imageTag | |||||
| if (StringUtils.isNoneBlank(logs2)){ | |||||
| String substring = logs2.substring(logs2.indexOf(harborUrl), logs2.length()); | |||||
| String cleanedString = substring.replaceAll("(\\r|\\n)", ""); | |||||
| String cmd1 = "docker tag " + cleanedString+ " " + harborUrl+"/"+repository+"/"+username+"/" + imageName + imageTag; | |||||
| String imageUrl = harborUrl+"/"+repository+"/"+username+"/" + imageName + imageTag; | |||||
| String cmd2 = "docker push " + imageUrl; | |||||
| String s = K8sClientUtil.executeCommand(pod, cmd1); | |||||
| if (StringUtils.isNotEmpty(K8sClientUtil.executeCommand(pod, cmd2))){ | |||||
| return imageUrl; | |||||
| }else { | |||||
| throw new Exception("拉取公网镜像失败,请检查网络或者镜像地址"); | |||||
| } | |||||
| }else { | |||||
| throw new Exception("拉取公网镜像失败,请检查网络或者镜像地址"); | |||||
| } | |||||
| } | |||||
| return null; | |||||
| @Override | |||||
| public String createImageFromLocal(String imageName, String imageTag, String path) throws Exception { | |||||
| // 得到容器 | |||||
| V1Pod pod = K8sClientUtil.getNSPodList(serviceNS, deploymentName); | |||||
| if (pod == null) { | |||||
| throw new Exception("镜像推送服务不存在"); | |||||
| } | |||||
| String loginCmd = "docker login -u " + harborUser +" -p "+harborpassword+" "+harborUrl; | |||||
| // 执行命令 docker login -u admin -p Harbor12345 172.20.32.187 | |||||
| String loginlog = K8sClientUtil.executeCommand(pod,loginCmd); | |||||
| // 在这个容器的/data/admin 目录下执行命令 docker load -i fileName 得到返回的镜像名字name:tag | |||||
| String username = SecurityUtils.getLoginUser().getUsername(); | |||||
| // | |||||
| String filePath = "/data/argo-workflow/" + bucketName + "/" +path; | |||||
| String logs2 = K8sClientUtil.executeCommand(pod,"docker load -i "+filePath); | |||||
| // 在容器里执行 docker tag name:tag nexus3.kube-system.svc:8083/imageName:imageTag | |||||
| if (StringUtils.isNoneBlank(logs2)){ | |||||
| String substring = logs2.substring(logs2.indexOf(harborUrl), logs2.length()); | |||||
| String cleanedString = substring.replaceAll("(\\r|\\n)", ""); | |||||
| String cmd1 = "docker tag " + cleanedString+ " " + harborUrl+"/"+repository+"/"+username+"/" + imageName + imageTag; | |||||
| String imageUrl = harborUrl+"/"+repository+"/"+username+"/" + imageName + imageTag; | |||||
| String cmd2 = "docker push " + imageUrl; | |||||
| String s = K8sClientUtil.executeCommand(pod, cmd1); | |||||
| if (StringUtils.isNotEmpty(K8sClientUtil.executeCommand(pod, cmd2))){ | |||||
| return imageUrl; | |||||
| }else { | |||||
| throw new Exception("解析镜像压缩包失败,请检查镜像文件"); | |||||
| } | |||||
| }else { | |||||
| throw new Exception("解析镜像压缩包失败,请检查镜像文件"); | |||||
| } | |||||
| } | } | ||||
| @Override | @Override | ||||
| @@ -1,5 +1,6 @@ | |||||
| package com.ruoyi.platform.utils; | package com.ruoyi.platform.utils; | ||||
| import io.kubernetes.client.Exec; | |||||
| import io.kubernetes.client.custom.IntOrString; | import io.kubernetes.client.custom.IntOrString; | ||||
| import io.kubernetes.client.custom.Quantity; | import io.kubernetes.client.custom.Quantity; | ||||
| import io.kubernetes.client.openapi.ApiClient; | import io.kubernetes.client.openapi.ApiClient; | ||||
| @@ -13,6 +14,9 @@ import org.apache.commons.lang.StringUtils; | |||||
| import org.springframework.beans.factory.annotation.Value; | import org.springframework.beans.factory.annotation.Value; | ||||
| import org.springframework.stereotype.Component; | import org.springframework.stereotype.Component; | ||||
| import java.io.BufferedReader; | |||||
| import java.io.IOException; | |||||
| import java.io.InputStreamReader; | |||||
| import java.util.HashMap; | import java.util.HashMap; | ||||
| import java.util.LinkedHashMap; | import java.util.LinkedHashMap; | ||||
| import java.util.Map; | import java.util.Map; | ||||
| @@ -48,7 +52,7 @@ public class K8sClientUtil { | |||||
| try { | try { | ||||
| this.apiClient = new ClientBuilder(). | this.apiClient = new ClientBuilder(). | ||||
| setBasePath("https://172.20.32.181:6443").setVerifyingSsl(false). | setBasePath("https://172.20.32.181:6443").setVerifyingSsl(false). | ||||
| setAuthentication(new AccessTokenAuthentication("eyJhbGciOiJSUzI1NiIsImtpZCI6IjRWcFBPWl9YSFFxQ2tVanRuNHdRT1dnUlJNTnB2bG5TQlVSRjNKdExWNDQifQ.eyJpc3MiOiJrdWJlcm5ldGVzL3NlcnZpY2VhY2NvdW50Iiwia3ViZXJuZXRlcy5pby9zZXJ2aWNlYWNjb3VudC9uYW1lc3BhY2UiOiJrdWJlLXN5c3RlbSIsImt1YmVybmV0ZXMuaW8vc2VydmljZWFjY291bnQvc2VjcmV0Lm5hbWUiOiJkZWZhdWx0LXRva2VuLXNteGxuIiwia3ViZXJuZXRlcy5pby9zZXJ2aWNlYWNjb3VudC9zZXJ2aWNlLWFjY291bnQubmFtZSI6ImRlZmF1bHQiLCJrdWJlcm5ldGVzLmlvL3NlcnZpY2VhY2NvdW50L3NlcnZpY2UtYWNjb3VudC51aWQiOiIyZWY1ZjdkMC0zMTdkLTQxN2UtOWY4Ni1mYjA1OTFhYWVhZDQiLCJzdWIiOiJzeXN0ZW06c2VydmljZWFjY291bnQ6a3ViZS1zeXN0ZW06ZGVmYXVsdCJ9.GMpReYK7YJ0nNy-F6VrUJQzjWQiSauAOeq0-DT8ik2Lx8f2eznYEm_3cHX4kIn_nYgfxo857urcHt4Ft6IgVtWzxLzVTCQVaNP_H2J8bnJn8W2tUKXzF_3d_GwO75H7kN8P3aoShULrOLpiIf3o3Az28_gwHkwCnd42npcKrCXfAKj8A2U7-KUFQXXA-etrWSw81C5t8ziL-2xaiDgwD3ewH-TNYsOpyWjGopNTxJn1F7GyJ7xDlmMJOaZhSnOrDggB7lqDEsE68YmZtqB7lcSaZHnKzvNhEdbKri4R7_urpjttz_k6qcfIi-l6GwPtJLatsPDg3OL3FFnzjvArJ-A")).build(); | |||||
| setAuthentication(new AccessTokenAuthentication("eyJhbGciOiJSUzI1NiIsImtpZCI6IjRWcFBPWl9YSFFxQ2tVanRuNHdRT1dnUlJNTnB2bG5TQlVSRjNKdExWNDQifQ.eyJpc3MiOiJrdWJlcm5ldGVzL3NlcnZpY2VhY2NvdW50Iiwia3ViZXJuZXRlcy5pby9zZXJ2aWNlYWNjb3VudC9uYW1lc3BhY2UiOiJkZWZhdWx0Iiwia3ViZXJuZXRlcy5pby9zZXJ2aWNlYWNjb3VudC9zZWNyZXQubmFtZSI6ImFkbWluLXNlcnZpY2UtYWNjb3VudC10b2tlbi14ZDk5eiIsImt1YmVybmV0ZXMuaW8vc2VydmljZWFjY291bnQvc2VydmljZS1hY2NvdW50Lm5hbWUiOiJhZG1pbi1zZXJ2aWNlLWFjY291bnQiLCJrdWJlcm5ldGVzLmlvL3NlcnZpY2VhY2NvdW50L3NlcnZpY2UtYWNjb3VudC51aWQiOiJmMGEzNmYyMS01MjQyLTQ4MTAtYWVmZS0xOTEwOTZlZjc5YmUiLCJzdWIiOiJzeXN0ZW06c2VydmljZWFjY291bnQ6ZGVmYXVsdDphZG1pbi1zZXJ2aWNlLWFjY291bnQifQ.fo-Wf0-5-IRC5fhRh65yfqCJqKfE9MrNFIXL2fd1CqVAHD7JBpWO2IsFiSmz9Bm7VfLmFAp2NB7DjW4ZLjC7ODiGhpSseBP8x4ceFuHL6pRGUsEBvHQBBBuQcGhNOcsxIDHnDqUdUzoLprj223lMZNTQowITuqYFU4GVbethyEuS6G5Wh9KHI3KYHFtG4_AeWBgI5Ppz8pDrhHzSFWTFbzxQ3RPGEwF0V-9wEtdrSYnfETi3rdRWif9W4a0RW8HwD9Gf7UCYcyFOs7e5_3-IvmctS85g87PYIfHXMhu_kOw-_Il4bkwPEK5uiBFDw0M1-s9YP-F9r5sXXvOJlsAr1g")).build(); | |||||
| } catch (Exception e) { | } catch (Exception e) { | ||||
| log.error("构建K8s-Client异常", e); | log.error("构建K8s-Client异常", e); | ||||
| throw new RuntimeException("构建K8s-Client异常"); | throw new RuntimeException("构建K8s-Client异常"); | ||||
| @@ -62,16 +66,16 @@ public class K8sClientUtil { | |||||
| * | * | ||||
| * @param kubeConfigPath kube连接配置文件 | * @param kubeConfigPath kube连接配置文件 | ||||
| */ | */ | ||||
| public K8sClientUtil(String kubeConfigPath) { | |||||
| try { | |||||
| this.apiClient = new ClientBuilder(). | |||||
| setBasePath("https://172.20.32.181:6443").setVerifyingSsl(false). | |||||
| setAuthentication(new AccessTokenAuthentication("eyJhbGciOiJSUzI1NiIsImtpZCI6IjRWcFBPWl9YSFFxQ2tVanRuNHdRT1dnUlJNTnB2bG5TQlVSRjNKdExWNDQifQ.eyJpc3MiOiJrdWJlcm5ldGVzL3NlcnZpY2VhY2NvdW50Iiwia3ViZXJuZXRlcy5pby9zZXJ2aWNlYWNjb3VudC9uYW1lc3BhY2UiOiJrdWJlLXN5c3RlbSIsImt1YmVybmV0ZXMuaW8vc2VydmljZWFjY291bnQvc2VjcmV0Lm5hbWUiOiJkZWZhdWx0LXRva2VuLXNteGxuIiwia3ViZXJuZXRlcy5pby9zZXJ2aWNlYWNjb3VudC9zZXJ2aWNlLWFjY291bnQubmFtZSI6ImRlZmF1bHQiLCJrdWJlcm5ldGVzLmlvL3NlcnZpY2VhY2NvdW50L3NlcnZpY2UtYWNjb3VudC51aWQiOiIyZWY1ZjdkMC0zMTdkLTQxN2UtOWY4Ni1mYjA1OTFhYWVhZDQiLCJzdWIiOiJzeXN0ZW06c2VydmljZWFjY291bnQ6a3ViZS1zeXN0ZW06ZGVmYXVsdCJ9.GMpReYK7YJ0nNy-F6VrUJQzjWQiSauAOeq0-DT8ik2Lx8f2eznYEm_3cHX4kIn_nYgfxo857urcHt4Ft6IgVtWzxLzVTCQVaNP_H2J8bnJn8W2tUKXzF_3d_GwO75H7kN8P3aoShULrOLpiIf3o3Az28_gwHkwCnd42npcKrCXfAKj8A2U7-KUFQXXA-etrWSw81C5t8ziL-2xaiDgwD3ewH-TNYsOpyWjGopNTxJn1F7GyJ7xDlmMJOaZhSnOrDggB7lqDEsE68YmZtqB7lcSaZHnKzvNhEdbKri4R7_urpjttz_k6qcfIi-l6GwPtJLatsPDg3OL3FFnzjvArJ-A")).build(); | |||||
| } catch (Exception e) { | |||||
| log.error("构建K8s-Client异常", e); | |||||
| throw new RuntimeException("构建K8s-Client异常"); | |||||
| } | |||||
| } | |||||
| // public K8sClientUtil(String kubeConfigPath) { | |||||
| // try { | |||||
| // this.apiClient = new ClientBuilder(). | |||||
| // setBasePath("https://172.20.32.181:6443").setVerifyingSsl(false). | |||||
| // setAuthentication(new AccessTokenAuthentication("eyJhbGciOiJSUzI1NiIsImtpZCI6IjRWcFBPWl9YSFFxQ2tVanRuNHdRT1dnUlJNTnB2bG5TQlVSRjNKdExWNDQifQ.eyJpc3MiOiJrdWJlcm5ldGVzL3NlcnZpY2VhY2NvdW50Iiwia3ViZXJuZXRlcy5pby9zZXJ2aWNlYWNjb3VudC9uYW1lc3BhY2UiOiJrdWJlLXN5c3RlbSIsImt1YmVybmV0ZXMuaW8vc2VydmljZWFjY291bnQvc2VjcmV0Lm5hbWUiOiJkZWZhdWx0LXRva2VuLXNteGxuIiwia3ViZXJuZXRlcy5pby9zZXJ2aWNlYWNjb3VudC9zZXJ2aWNlLWFjY291bnQubmFtZSI6ImRlZmF1bHQiLCJrdWJlcm5ldGVzLmlvL3NlcnZpY2VhY2NvdW50L3NlcnZpY2UtYWNjb3VudC51aWQiOiIyZWY1ZjdkMC0zMTdkLTQxN2UtOWY4Ni1mYjA1OTFhYWVhZDQiLCJzdWIiOiJzeXN0ZW06c2VydmljZWFjY291bnQ6a3ViZS1zeXN0ZW06ZGVmYXVsdCJ9.GMpReYK7YJ0nNy-F6VrUJQzjWQiSauAOeq0-DT8ik2Lx8f2eznYEm_3cHX4kIn_nYgfxo857urcHt4Ft6IgVtWzxLzVTCQVaNP_H2J8bnJn8W2tUKXzF_3d_GwO75H7kN8P3aoShULrOLpiIf3o3Az28_gwHkwCnd42npcKrCXfAKj8A2U7-KUFQXXA-etrWSw81C5t8ziL-2xaiDgwD3ewH-TNYsOpyWjGopNTxJn1F7GyJ7xDlmMJOaZhSnOrDggB7lqDEsE68YmZtqB7lcSaZHnKzvNhEdbKri4R7_urpjttz_k6qcfIi-l6GwPtJLatsPDg3OL3FFnzjvArJ-A")).build(); | |||||
| // } catch (Exception e) { | |||||
| // log.error("构建K8s-Client异常", e); | |||||
| // throw new RuntimeException("构建K8s-Client异常"); | |||||
| // } | |||||
| // } | |||||
| /** | /** | ||||
| * 获取所有的Pod | * 获取所有的Pod | ||||
| @@ -269,4 +273,52 @@ public class K8sClientUtil { | |||||
| V1Service service = createService(namespace, podName + "-svc", port, selector); | V1Service service = createService(namespace, podName + "-svc", port, selector); | ||||
| return service.getSpec().getPorts().get(0).getNodePort(); | return service.getSpec().getPorts().get(0).getNodePort(); | ||||
| } | } | ||||
| /** | |||||
| * 根据获取namespace,deploymentName的Pod Name | |||||
| * | |||||
| * @return podList | |||||
| */ | |||||
| public static V1Pod getNSPodList(String namespace,String deploymentName) throws Exception { | |||||
| // new a CoreV1Api | |||||
| CoreV1Api api = new CoreV1Api(apiClient); | |||||
| V1PodList v1PodList = null; | |||||
| try { | |||||
| v1PodList = api.listNamespacedPod(namespace, null, null, null, null, null, null, null, null, null, null); | |||||
| } catch (ApiException e) { | |||||
| log.error("获取 POD 异常:", e); | |||||
| } | |||||
| // invokes the CoreV1Api client | |||||
| for (V1Pod item : v1PodList.getItems()) { | |||||
| if (item.getMetadata().getGenerateName().startsWith(deploymentName)) { | |||||
| // 找到匹配的Pod,获取其名称 | |||||
| return item; | |||||
| } | |||||
| } | |||||
| return null; | |||||
| } | |||||
| public static String executeCommand(V1Pod item, String command) { | |||||
| try { | |||||
| // 创建API实例 | |||||
| // 创建Exec实例 | |||||
| Exec exec = new Exec(apiClient); | |||||
| String[] cmd = { "/bin/sh", "-c", command}; | |||||
| Process proc = exec.exec(item, cmd, false); | |||||
| // 读取输出 | |||||
| BufferedReader reader = new BufferedReader(new InputStreamReader(proc.getInputStream())); | |||||
| StringBuilder builder = new StringBuilder(); | |||||
| String line = null; | |||||
| while ((line = reader.readLine()) != null) { | |||||
| builder.append(line); | |||||
| builder.append(System.getProperty("line.separator")); | |||||
| } | |||||
| return builder.toString(); | |||||
| } catch (Exception e) { | |||||
| log.error("执行命令异常", e); | |||||
| throw new RuntimeException("执行命令异常"); | |||||
| } | |||||
| } | |||||
| } | } | ||||
| @@ -0,0 +1,13 @@ | |||||
| apiVersion: rbac.authorization.k8s.io/v1 | |||||
| kind: ClusterRoleBinding | |||||
| metadata: | |||||
| name: admin-service-account-binding | |||||
| roleRef: | |||||
| apiGroup: rbac.authorization.k8s.io | |||||
| kind: ClusterRole | |||||
| name: cluster-admin | |||||
| subjects: | |||||
| - kind: ServiceAccount | |||||
| name: admin-service-account | |||||
| namespace: default | |||||