1.30 k8s pods查询
This commit is contained in:
parent
55c8428454
commit
81294bc1dc
@ -121,6 +121,37 @@ public class K8sDeploymentApiController extends BaseController<K8sDeployment, K8
|
||||
return Response.success(k8sPodService.getPodDetailByDeployment(deploymentId, podName));
|
||||
}
|
||||
|
||||
@Operation(summary = "查询Pod日志", description = "查询指定Pod的日志内容")
|
||||
@GetMapping("/{deploymentId}/pods/{podName}/logs")
|
||||
public Response<String> getPodLogs(
|
||||
@Parameter(description = "Deployment ID", required = true) @PathVariable Long deploymentId,
|
||||
@Parameter(description = "Pod名称", required = true) @PathVariable String podName,
|
||||
@Parameter(description = "容器名称(可选,默认第一个容器)") @RequestParam(required = false) String container,
|
||||
@Parameter(description = "返回最后N行日志(可选)") @RequestParam(required = false) Integer tail,
|
||||
@Parameter(description = "返回最近N秒的日志(可选)") @RequestParam(required = false) Integer sinceSeconds
|
||||
) {
|
||||
return Response.success(k8sPodService.getPodLogs(deploymentId, podName, container, tail, sinceSeconds));
|
||||
}
|
||||
|
||||
@Operation(summary = "重启Deployment", description = "通过更新annotation触发Deployment滚动重启")
|
||||
@PostMapping("/{id}/restart")
|
||||
public Response<Void> restartDeployment(
|
||||
@Parameter(description = "Deployment ID", required = true) @PathVariable Long id
|
||||
) {
|
||||
k8sDeploymentService.restartDeployment(id);
|
||||
return Response.success();
|
||||
}
|
||||
|
||||
@Operation(summary = "扩缩容Deployment", description = "修改Deployment的副本数")
|
||||
@PostMapping("/{id}/scale")
|
||||
public Response<Void> scaleDeployment(
|
||||
@Parameter(description = "Deployment ID", required = true) @PathVariable Long id,
|
||||
@Validated @RequestBody com.qqchen.deploy.backend.deploy.dto.ScaleDeploymentRequest request
|
||||
) {
|
||||
k8sDeploymentService.scaleDeployment(id, request.getReplicas());
|
||||
return Response.success();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void exportData(HttpServletResponse response, List<K8sDeploymentDTO> data) {
|
||||
log.info("导出K8S Deployment数据,数据量:{}", data.size());
|
||||
|
||||
@ -31,6 +31,9 @@ public class K8sDeploymentDTO extends BaseDTO {
|
||||
@Schema(description = "就绪副本数")
|
||||
private Integer readyReplicas;
|
||||
|
||||
@Schema(description = "总重启次数(所有Pod的重启次数总和)")
|
||||
private Integer totalRestartCount;
|
||||
|
||||
@Schema(description = "容器镜像")
|
||||
private String image;
|
||||
|
||||
|
||||
@ -0,0 +1,19 @@
|
||||
package com.qqchen.deploy.backend.deploy.dto;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import jakarta.validation.constraints.Min;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* Deployment扩缩容请求
|
||||
*/
|
||||
@Data
|
||||
@Schema(description = "Deployment扩缩容请求")
|
||||
public class ScaleDeploymentRequest {
|
||||
|
||||
@NotNull(message = "副本数不能为空")
|
||||
@Min(value = 0, message = "副本数不能小于0")
|
||||
@Schema(description = "目标副本数", example = "3", required = true)
|
||||
private Integer replicas;
|
||||
}
|
||||
@ -38,6 +38,9 @@ public class K8sDeployment extends Entity<Long> {
|
||||
@Column(name = "ready_replicas")
|
||||
private Integer readyReplicas;
|
||||
|
||||
@Column(name = "total_restart_count")
|
||||
private Integer totalRestartCount;
|
||||
|
||||
@Column(name = "image")
|
||||
private String image;
|
||||
|
||||
|
||||
@ -0,0 +1,56 @@
|
||||
package com.qqchen.deploy.backend.deploy.enums;
|
||||
|
||||
import lombok.Getter;
|
||||
|
||||
/**
|
||||
* 容器状态枚举
|
||||
*/
|
||||
@Getter
|
||||
public enum ContainerStateEnum {
|
||||
|
||||
/**
|
||||
* 运行中
|
||||
*/
|
||||
RUNNING("running", "运行中"),
|
||||
|
||||
/**
|
||||
* 等待中
|
||||
*/
|
||||
WAITING("waiting", "等待中"),
|
||||
|
||||
/**
|
||||
* 已终止
|
||||
*/
|
||||
TERMINATED("terminated", "已终止"),
|
||||
|
||||
/**
|
||||
* 未知状态
|
||||
*/
|
||||
UNKNOWN("unknown", "未知状态");
|
||||
|
||||
private final String code;
|
||||
private final String description;
|
||||
|
||||
ContainerStateEnum(String code, String description) {
|
||||
this.code = code;
|
||||
this.description = description;
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据code获取枚举
|
||||
*
|
||||
* @param code 状态码
|
||||
* @return 容器状态枚举
|
||||
*/
|
||||
public static ContainerStateEnum fromCode(String code) {
|
||||
if (code == null) {
|
||||
return UNKNOWN;
|
||||
}
|
||||
for (ContainerStateEnum state : values()) {
|
||||
if (state.code.equalsIgnoreCase(code)) {
|
||||
return state;
|
||||
}
|
||||
}
|
||||
return UNKNOWN;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,61 @@
|
||||
package com.qqchen.deploy.backend.deploy.enums;
|
||||
|
||||
import lombok.Getter;
|
||||
|
||||
/**
|
||||
* Pod阶段枚举
|
||||
*/
|
||||
@Getter
|
||||
public enum PodPhaseEnum {
|
||||
|
||||
/**
|
||||
* 等待中
|
||||
*/
|
||||
PENDING("Pending", "等待中"),
|
||||
|
||||
/**
|
||||
* 运行中
|
||||
*/
|
||||
RUNNING("Running", "运行中"),
|
||||
|
||||
/**
|
||||
* 成功
|
||||
*/
|
||||
SUCCEEDED("Succeeded", "成功"),
|
||||
|
||||
/**
|
||||
* 失败
|
||||
*/
|
||||
FAILED("Failed", "失败"),
|
||||
|
||||
/**
|
||||
* 未知状态
|
||||
*/
|
||||
UNKNOWN("Unknown", "未知状态");
|
||||
|
||||
private final String code;
|
||||
private final String description;
|
||||
|
||||
PodPhaseEnum(String code, String description) {
|
||||
this.code = code;
|
||||
this.description = description;
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据code获取枚举
|
||||
*
|
||||
* @param code 阶段码
|
||||
* @return Pod阶段枚举
|
||||
*/
|
||||
public static PodPhaseEnum fromCode(String code) {
|
||||
if (code == null) {
|
||||
return UNKNOWN;
|
||||
}
|
||||
for (PodPhaseEnum phase : values()) {
|
||||
if (phase.code.equalsIgnoreCase(code)) {
|
||||
return phase;
|
||||
}
|
||||
}
|
||||
return UNKNOWN;
|
||||
}
|
||||
}
|
||||
@ -75,6 +75,50 @@ public interface IK8sServiceIntegration extends IExternalSystemIntegration {
|
||||
*/
|
||||
K8sPodResponse getPod(ExternalSystem externalSystem, String namespace, String podName);
|
||||
|
||||
/**
|
||||
* 查询Pod日志
|
||||
*
|
||||
* @param externalSystem K8S系统配置
|
||||
* @param namespace 命名空间名称
|
||||
* @param podName Pod名称
|
||||
* @param container 容器名称(可选,默认第一个容器)
|
||||
* @param tail 返回最后N行日志(可选)
|
||||
* @param sinceSeconds 返回最近N秒的日志(可选)
|
||||
* @param follow 是否持续输出日志(可选,默认false)
|
||||
* @return Pod日志内容
|
||||
*/
|
||||
String getPodLogs(ExternalSystem externalSystem, String namespace, String podName,
|
||||
String container, Integer tail, Integer sinceSeconds, Boolean follow);
|
||||
|
||||
/**
|
||||
* 重启Deployment(通过更新annotation触发滚动更新)
|
||||
*
|
||||
* @param externalSystem K8S系统配置
|
||||
* @param namespace 命名空间名称
|
||||
* @param deploymentName Deployment名称
|
||||
*/
|
||||
void restartDeployment(ExternalSystem externalSystem, String namespace, String deploymentName);
|
||||
|
||||
/**
|
||||
* 扩缩容Deployment
|
||||
*
|
||||
* @param externalSystem K8S系统配置
|
||||
* @param namespace 命名空间名称
|
||||
* @param deploymentName Deployment名称
|
||||
* @param replicas 目标副本数
|
||||
*/
|
||||
void scaleDeployment(ExternalSystem externalSystem, String namespace, String deploymentName, Integer replicas);
|
||||
|
||||
/**
|
||||
* 计算Deployment下所有Pod的总重启次数
|
||||
*
|
||||
* @param externalSystem K8S系统配置
|
||||
* @param namespace 命名空间名称
|
||||
* @param deploymentName Deployment名称
|
||||
* @return 总重启次数
|
||||
*/
|
||||
Integer calculateTotalRestartCount(ExternalSystem externalSystem, String namespace, String deploymentName);
|
||||
|
||||
/**
|
||||
* 获取系统类型
|
||||
*
|
||||
|
||||
@ -1,6 +1,8 @@
|
||||
package com.qqchen.deploy.backend.deploy.integration.impl;
|
||||
|
||||
import com.qqchen.deploy.backend.deploy.entity.ExternalSystem;
|
||||
import com.qqchen.deploy.backend.deploy.enums.ContainerStateEnum;
|
||||
import com.qqchen.deploy.backend.deploy.enums.PodPhaseEnum;
|
||||
import com.qqchen.deploy.backend.framework.utils.JsonUtils;
|
||||
import com.qqchen.deploy.backend.deploy.integration.IK8sServiceIntegration;
|
||||
import com.qqchen.deploy.backend.deploy.integration.response.K8sDeploymentResponse;
|
||||
@ -38,6 +40,7 @@ public class K8sServiceIntegrationImpl extends BaseExternalSystemIntegration imp
|
||||
|
||||
// K8S ApiClient缓存 - 线程安全
|
||||
private static final Map<Long, K8sApiClientCache> API_CLIENT_CACHE = new ConcurrentHashMap<>();
|
||||
|
||||
private static final long CACHE_EXPIRE_TIME = 30 * 60 * 1000; // 30分钟过期
|
||||
|
||||
/**
|
||||
@ -45,6 +48,7 @@ public class K8sServiceIntegrationImpl extends BaseExternalSystemIntegration imp
|
||||
*/
|
||||
private static class K8sApiClientCache {
|
||||
final ApiClient apiClient;
|
||||
|
||||
final long expireTime;
|
||||
|
||||
K8sApiClientCache(ApiClient apiClient) {
|
||||
@ -93,24 +97,24 @@ public class K8sServiceIntegrationImpl extends BaseExternalSystemIntegration imp
|
||||
@Override
|
||||
public boolean testConnection(ExternalSystem system) {
|
||||
log.info("测试K8S连接,集群: {}", system.getName());
|
||||
|
||||
|
||||
try {
|
||||
String config = system.getConfig();
|
||||
if (config == null || config.trim().isEmpty()) {
|
||||
throw new BusinessException(ResponseCode.K8S_CONFIG_EMPTY);
|
||||
}
|
||||
|
||||
|
||||
// 创建K8S ApiClient并测试连接(直接使用config作为kubeconfig)
|
||||
ApiClient client = Config.fromConfig(new StringReader(config));
|
||||
client.setConnectTimeout(15000); // 15秒连接超时
|
||||
client.setReadTimeout(30000); // 30秒读取超时
|
||||
|
||||
|
||||
VersionApi versionApi = new VersionApi(client);
|
||||
VersionInfo version = versionApi.getCode();
|
||||
log.info("K8S集群连接成功,版本: {}", version.getGitVersion());
|
||||
|
||||
|
||||
return true;
|
||||
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("K8S连接测试失败,集群: {}, 错误: {}", system.getName(), e.getMessage(), e);
|
||||
return false;
|
||||
@ -123,26 +127,26 @@ public class K8sServiceIntegrationImpl extends BaseExternalSystemIntegration imp
|
||||
@Override
|
||||
public List<K8sNamespaceResponse> listNamespaces(ExternalSystem externalSystem) {
|
||||
log.info("查询K8S命名空间,集群: {}", externalSystem.getName());
|
||||
|
||||
|
||||
try {
|
||||
K8sApiClientCache cache = getApiClientCache(externalSystem);
|
||||
CoreV1Api api = new CoreV1Api(cache.apiClient);
|
||||
|
||||
|
||||
V1NamespaceList namespaceList = api.listNamespace(
|
||||
null, null, null, null, null, null, null, null, null, null
|
||||
);
|
||||
|
||||
|
||||
List<K8sNamespaceResponse> namespaces = new ArrayList<>();
|
||||
for (V1Namespace ns : namespaceList.getItems()) {
|
||||
K8sNamespaceResponse response = new K8sNamespaceResponse();
|
||||
response.setName(ns.getMetadata().getName());
|
||||
|
||||
|
||||
if (ns.getStatus() != null && ns.getStatus().getPhase() != null) {
|
||||
response.setStatus(ns.getStatus().getPhase());
|
||||
}
|
||||
|
||||
|
||||
response.setLabels(ns.getMetadata().getLabels());
|
||||
|
||||
|
||||
if (ns.getMetadata().getCreationTimestamp() != null) {
|
||||
response.setCreationTimestamp(
|
||||
LocalDateTime.ofInstant(
|
||||
@ -151,20 +155,20 @@ public class K8sServiceIntegrationImpl extends BaseExternalSystemIntegration imp
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
// 序列化为YAML配置
|
||||
try {
|
||||
response.setYamlConfig(Yaml.dump(ns));
|
||||
} catch (Exception e) {
|
||||
log.warn("序列化Namespace为YAML失败: {}", ns.getMetadata().getName(), e);
|
||||
}
|
||||
|
||||
|
||||
namespaces.add(response);
|
||||
}
|
||||
|
||||
|
||||
log.info("查询到 {} 个命名空间", namespaces.size());
|
||||
return namespaces;
|
||||
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("查询K8S命名空间失败,集群: {}, 错误: {}", externalSystem.getName(), e.getMessage(), e);
|
||||
throw new BusinessException(ResponseCode.K8S_NAMESPACE_SYNC_FAILED);
|
||||
@ -177,45 +181,45 @@ public class K8sServiceIntegrationImpl extends BaseExternalSystemIntegration imp
|
||||
@Override
|
||||
public List<K8sDeploymentResponse> listDeployments(ExternalSystem externalSystem, String namespace) {
|
||||
log.info("查询K8S Deployment,集群: {}, 命名空间: {}", externalSystem.getName(), namespace);
|
||||
|
||||
|
||||
try {
|
||||
K8sApiClientCache cache = getApiClientCache(externalSystem);
|
||||
AppsV1Api api = new AppsV1Api(cache.apiClient);
|
||||
|
||||
|
||||
V1DeploymentList deploymentList = api.listNamespacedDeployment(
|
||||
namespace, null, null, null, null, null, null, null, null, null, null
|
||||
);
|
||||
|
||||
|
||||
List<K8sDeploymentResponse> deployments = new ArrayList<>();
|
||||
for (V1Deployment deployment : deploymentList.getItems()) {
|
||||
K8sDeploymentResponse response = new K8sDeploymentResponse();
|
||||
response.setName(deployment.getMetadata().getName());
|
||||
response.setNamespace(deployment.getMetadata().getNamespace());
|
||||
|
||||
|
||||
if (deployment.getSpec() != null) {
|
||||
response.setReplicas(deployment.getSpec().getReplicas());
|
||||
}
|
||||
|
||||
|
||||
if (deployment.getStatus() != null) {
|
||||
response.setAvailableReplicas(deployment.getStatus().getAvailableReplicas());
|
||||
response.setReadyReplicas(deployment.getStatus().getReadyReplicas());
|
||||
}
|
||||
|
||||
|
||||
response.setLabels(deployment.getMetadata().getLabels());
|
||||
|
||||
|
||||
if (deployment.getSpec() != null && deployment.getSpec().getSelector() != null) {
|
||||
response.setSelector(deployment.getSpec().getSelector().getMatchLabels());
|
||||
}
|
||||
|
||||
|
||||
// 获取第一个容器的镜像
|
||||
if (deployment.getSpec() != null
|
||||
if (deployment.getSpec() != null
|
||||
&& deployment.getSpec().getTemplate() != null
|
||||
&& deployment.getSpec().getTemplate().getSpec() != null
|
||||
&& deployment.getSpec().getTemplate().getSpec().getContainers() != null
|
||||
&& deployment.getSpec().getTemplate().getSpec().getContainers() != null
|
||||
&& !deployment.getSpec().getTemplate().getSpec().getContainers().isEmpty()) {
|
||||
response.setImage(deployment.getSpec().getTemplate().getSpec().getContainers().get(0).getImage());
|
||||
}
|
||||
|
||||
|
||||
if (deployment.getMetadata().getCreationTimestamp() != null) {
|
||||
response.setCreationTimestamp(
|
||||
LocalDateTime.ofInstant(
|
||||
@ -224,22 +228,22 @@ public class K8sServiceIntegrationImpl extends BaseExternalSystemIntegration imp
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
// 序列化为YAML配置
|
||||
try {
|
||||
response.setYamlConfig(Yaml.dump(deployment));
|
||||
} catch (Exception e) {
|
||||
log.warn("序列化Deployment为YAML失败: {}", deployment.getMetadata().getName(), e);
|
||||
}
|
||||
|
||||
|
||||
deployments.add(response);
|
||||
}
|
||||
|
||||
|
||||
log.info("查询到 {} 个Deployment", deployments.size());
|
||||
return deployments;
|
||||
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("查询K8S Deployment失败,集群: {}, 命名空间: {}, 错误: {}",
|
||||
log.error("查询K8S Deployment失败,集群: {}, 命名空间: {}, 错误: {}",
|
||||
externalSystem.getName(), namespace, e.getMessage(), e);
|
||||
throw new BusinessException(ResponseCode.K8S_DEPLOYMENT_SYNC_FAILED);
|
||||
}
|
||||
@ -251,45 +255,45 @@ public class K8sServiceIntegrationImpl extends BaseExternalSystemIntegration imp
|
||||
@Override
|
||||
public List<K8sDeploymentResponse> listAllDeployments(ExternalSystem externalSystem) {
|
||||
log.info("查询所有K8S Deployment,集群: {}", externalSystem.getName());
|
||||
|
||||
|
||||
try {
|
||||
K8sApiClientCache cache = getApiClientCache(externalSystem);
|
||||
AppsV1Api api = new AppsV1Api(cache.apiClient);
|
||||
|
||||
|
||||
V1DeploymentList deploymentList = api.listDeploymentForAllNamespaces(
|
||||
null, null, null, null, null, null, null, null, null, null
|
||||
);
|
||||
|
||||
|
||||
List<K8sDeploymentResponse> deployments = new ArrayList<>();
|
||||
for (V1Deployment deployment : deploymentList.getItems()) {
|
||||
K8sDeploymentResponse response = new K8sDeploymentResponse();
|
||||
response.setName(deployment.getMetadata().getName());
|
||||
response.setNamespace(deployment.getMetadata().getNamespace());
|
||||
|
||||
|
||||
if (deployment.getSpec() != null) {
|
||||
response.setReplicas(deployment.getSpec().getReplicas());
|
||||
}
|
||||
|
||||
|
||||
if (deployment.getStatus() != null) {
|
||||
response.setAvailableReplicas(deployment.getStatus().getAvailableReplicas());
|
||||
response.setReadyReplicas(deployment.getStatus().getReadyReplicas());
|
||||
}
|
||||
|
||||
|
||||
response.setLabels(deployment.getMetadata().getLabels());
|
||||
|
||||
|
||||
if (deployment.getSpec() != null && deployment.getSpec().getSelector() != null) {
|
||||
response.setSelector(deployment.getSpec().getSelector().getMatchLabels());
|
||||
}
|
||||
|
||||
|
||||
// 获取第一个容器的镜像
|
||||
if (deployment.getSpec() != null
|
||||
if (deployment.getSpec() != null
|
||||
&& deployment.getSpec().getTemplate() != null
|
||||
&& deployment.getSpec().getTemplate().getSpec() != null
|
||||
&& deployment.getSpec().getTemplate().getSpec().getContainers() != null
|
||||
&& deployment.getSpec().getTemplate().getSpec().getContainers() != null
|
||||
&& !deployment.getSpec().getTemplate().getSpec().getContainers().isEmpty()) {
|
||||
response.setImage(deployment.getSpec().getTemplate().getSpec().getContainers().get(0).getImage());
|
||||
}
|
||||
|
||||
|
||||
if (deployment.getMetadata().getCreationTimestamp() != null) {
|
||||
response.setCreationTimestamp(
|
||||
LocalDateTime.ofInstant(
|
||||
@ -298,20 +302,20 @@ public class K8sServiceIntegrationImpl extends BaseExternalSystemIntegration imp
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
// 序列化为YAML配置
|
||||
try {
|
||||
response.setYamlConfig(Yaml.dump(deployment));
|
||||
} catch (Exception e) {
|
||||
log.warn("序列化Deployment为YAML失败: {}", deployment.getMetadata().getName(), e);
|
||||
}
|
||||
|
||||
|
||||
deployments.add(response);
|
||||
}
|
||||
|
||||
|
||||
log.info("查询到 {} 个Deployment", deployments.size());
|
||||
return deployments;
|
||||
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("查询所有K8S Deployment失败,集群: {}, 错误: {}", externalSystem.getName(), e.getMessage(), e);
|
||||
throw new BusinessException(ResponseCode.K8S_DEPLOYMENT_SYNC_FAILED);
|
||||
@ -324,25 +328,25 @@ public class K8sServiceIntegrationImpl extends BaseExternalSystemIntegration imp
|
||||
@Override
|
||||
public List<K8sPodResponse> listPods(ExternalSystem externalSystem, String namespace) {
|
||||
log.info("查询K8S Pod,集群: {}, 命名空间: {}", externalSystem.getName(), namespace);
|
||||
|
||||
|
||||
try {
|
||||
K8sApiClientCache cache = getApiClientCache(externalSystem);
|
||||
CoreV1Api api = new CoreV1Api(cache.apiClient);
|
||||
|
||||
|
||||
V1PodList podList = api.listNamespacedPod(
|
||||
namespace, null, null, null, null, null, null, null, null, null, null
|
||||
);
|
||||
|
||||
|
||||
List<K8sPodResponse> pods = new ArrayList<>();
|
||||
for (V1Pod pod : podList.getItems()) {
|
||||
pods.add(convertPodToResponse(pod));
|
||||
}
|
||||
|
||||
|
||||
log.info("查询到 {} 个Pod", pods.size());
|
||||
return pods;
|
||||
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("查询K8S Pod失败,集群: {}, 命名空间: {}, 错误: {}",
|
||||
log.error("查询K8S Pod失败,集群: {}, 命名空间: {}, 错误: {}",
|
||||
externalSystem.getName(), namespace, e.getMessage(), e);
|
||||
throw new BusinessException(ResponseCode.K8S_OPERATION_FAILED);
|
||||
}
|
||||
@ -353,53 +357,53 @@ public class K8sServiceIntegrationImpl extends BaseExternalSystemIntegration imp
|
||||
*/
|
||||
@Override
|
||||
public List<K8sPodResponse> listPodsByDeployment(ExternalSystem externalSystem, String namespace, String deploymentName) {
|
||||
log.info("查询K8S Deployment的Pod,集群: {}, 命名空间: {}, Deployment: {}",
|
||||
log.info("查询K8S Deployment的Pod,集群: {}, 命名空间: {}, Deployment: {}",
|
||||
externalSystem.getName(), namespace, deploymentName);
|
||||
|
||||
|
||||
try {
|
||||
K8sApiClientCache cache = getApiClientCache(externalSystem);
|
||||
|
||||
|
||||
// 1. 先查询Deployment获取selector
|
||||
AppsV1Api appsApi = new AppsV1Api(cache.apiClient);
|
||||
V1Deployment deployment = appsApi.readNamespacedDeployment(deploymentName, namespace, null);
|
||||
|
||||
if (deployment.getSpec() == null || deployment.getSpec().getSelector() == null
|
||||
|
||||
if (deployment.getSpec() == null || deployment.getSpec().getSelector() == null
|
||||
|| deployment.getSpec().getSelector().getMatchLabels() == null) {
|
||||
log.warn("Deployment没有selector: {}/{}", namespace, deploymentName);
|
||||
return new ArrayList<>();
|
||||
}
|
||||
|
||||
|
||||
// 2. 构建label selector
|
||||
Map<String, String> matchLabels = deployment.getSpec().getSelector().getMatchLabels();
|
||||
String labelSelector = matchLabels.entrySet().stream()
|
||||
.map(entry -> entry.getKey() + "=" + entry.getValue())
|
||||
.reduce((a, b) -> a + "," + b)
|
||||
.orElse("");
|
||||
|
||||
|
||||
// 3. 使用label selector查询Pod
|
||||
CoreV1Api coreApi = new CoreV1Api(cache.apiClient);
|
||||
V1PodList podList = coreApi.listNamespacedPod(
|
||||
namespace, null, null, null, null, labelSelector, null, null, null, null, null
|
||||
);
|
||||
|
||||
|
||||
List<K8sPodResponse> pods = new ArrayList<>();
|
||||
for (V1Pod pod : podList.getItems()) {
|
||||
pods.add(convertPodToResponse(pod));
|
||||
}
|
||||
|
||||
|
||||
log.info("查询到 {} 个Pod", pods.size());
|
||||
return pods;
|
||||
|
||||
|
||||
} catch (ApiException e) {
|
||||
if (e.getCode() == 404) {
|
||||
log.warn("Deployment不存在: {}/{}", namespace, deploymentName);
|
||||
throw new BusinessException(ResponseCode.K8S_RESOURCE_NOT_FOUND);
|
||||
}
|
||||
log.error("查询K8S Deployment的Pod失败,集群: {}, 命名空间: {}, Deployment: {}, 错误: {}",
|
||||
log.error("查询K8S Deployment的Pod失败,集群: {}, 命名空间: {}, Deployment: {}, 错误: {}",
|
||||
externalSystem.getName(), namespace, deploymentName, e.getMessage(), e);
|
||||
throw new BusinessException(ResponseCode.K8S_OPERATION_FAILED);
|
||||
} catch (Exception e) {
|
||||
log.error("查询K8S Deployment的Pod失败,集群: {}, 命名空间: {}, Deployment: {}, 错误: {}",
|
||||
log.error("查询K8S Deployment的Pod失败,集群: {}, 命名空间: {}, Deployment: {}, 错误: {}",
|
||||
externalSystem.getName(), namespace, deploymentName, e.getMessage(), e);
|
||||
throw new BusinessException(ResponseCode.K8S_OPERATION_FAILED);
|
||||
}
|
||||
@ -410,27 +414,27 @@ public class K8sServiceIntegrationImpl extends BaseExternalSystemIntegration imp
|
||||
*/
|
||||
@Override
|
||||
public K8sPodResponse getPod(ExternalSystem externalSystem, String namespace, String podName) {
|
||||
log.info("查询K8S Pod详情,集群: {}, 命名空间: {}, Pod: {}",
|
||||
log.info("查询K8S Pod详情,集群: {}, 命名空间: {}, Pod: {}",
|
||||
externalSystem.getName(), namespace, podName);
|
||||
|
||||
|
||||
try {
|
||||
K8sApiClientCache cache = getApiClientCache(externalSystem);
|
||||
CoreV1Api api = new CoreV1Api(cache.apiClient);
|
||||
|
||||
|
||||
V1Pod pod = api.readNamespacedPod(podName, namespace, null);
|
||||
|
||||
|
||||
return convertPodToResponse(pod);
|
||||
|
||||
|
||||
} catch (ApiException e) {
|
||||
if (e.getCode() == 404) {
|
||||
log.warn("Pod不存在: {}/{}", namespace, podName);
|
||||
throw new BusinessException(ResponseCode.K8S_POD_NOT_FOUND);
|
||||
}
|
||||
log.error("查询K8S Pod详情失败,集群: {}, 命名空间: {}, Pod: {}, 错误: {}",
|
||||
log.error("查询K8S Pod详情失败,集群: {}, 命名空间: {}, Pod: {}, 错误: {}",
|
||||
externalSystem.getName(), namespace, podName, e.getMessage(), e);
|
||||
throw new BusinessException(ResponseCode.K8S_OPERATION_FAILED);
|
||||
} catch (Exception e) {
|
||||
log.error("查询K8S Pod详情失败,集群: {}, 命名空间: {}, Pod: {}, 错误: {}",
|
||||
log.error("查询K8S Pod详情失败,集群: {}, 命名空间: {}, Pod: {}, 错误: {}",
|
||||
externalSystem.getName(), namespace, podName, e.getMessage(), e);
|
||||
throw new BusinessException(ResponseCode.K8S_OPERATION_FAILED);
|
||||
}
|
||||
@ -441,13 +445,13 @@ public class K8sServiceIntegrationImpl extends BaseExternalSystemIntegration imp
|
||||
*/
|
||||
private K8sPodResponse convertPodToResponse(V1Pod pod) {
|
||||
K8sPodResponse response = new K8sPodResponse();
|
||||
|
||||
|
||||
// 基本信息
|
||||
response.setName(pod.getMetadata().getName());
|
||||
response.setNamespace(pod.getMetadata().getNamespace());
|
||||
response.setLabels(pod.getMetadata().getLabels());
|
||||
response.setAnnotations(pod.getMetadata().getAnnotations());
|
||||
|
||||
|
||||
// 时间信息
|
||||
if (pod.getMetadata().getCreationTimestamp() != null) {
|
||||
response.setCreationTimestamp(
|
||||
@ -457,15 +461,15 @@ public class K8sServiceIntegrationImpl extends BaseExternalSystemIntegration imp
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
// 状态信息
|
||||
if (pod.getStatus() != null) {
|
||||
response.setPhase(pod.getStatus().getPhase());
|
||||
response.setPhase(PodPhaseEnum.fromCode(pod.getStatus().getPhase()));
|
||||
response.setReason(pod.getStatus().getReason());
|
||||
response.setMessage(pod.getStatus().getMessage());
|
||||
response.setPodIP(pod.getStatus().getPodIP());
|
||||
response.setHostIP(pod.getStatus().getHostIP());
|
||||
|
||||
|
||||
if (pod.getStatus().getStartTime() != null) {
|
||||
response.setStartTime(
|
||||
LocalDateTime.ofInstant(
|
||||
@ -474,13 +478,13 @@ public class K8sServiceIntegrationImpl extends BaseExternalSystemIntegration imp
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
// 容器状态
|
||||
if (pod.getStatus().getContainerStatuses() != null) {
|
||||
List<K8sPodResponse.ContainerInfo> containers = new ArrayList<>();
|
||||
int totalRestartCount = 0;
|
||||
boolean allReady = true;
|
||||
|
||||
|
||||
for (V1ContainerStatus containerStatus : pod.getStatus().getContainerStatuses()) {
|
||||
K8sPodResponse.ContainerInfo containerInfo = new K8sPodResponse.ContainerInfo();
|
||||
containerInfo.setName(containerStatus.getName());
|
||||
@ -489,17 +493,17 @@ public class K8sServiceIntegrationImpl extends BaseExternalSystemIntegration imp
|
||||
containerInfo.setReady(containerStatus.getReady());
|
||||
containerInfo.setRestartCount(containerStatus.getRestartCount());
|
||||
containerInfo.setContainerID(containerStatus.getContainerID());
|
||||
|
||||
|
||||
totalRestartCount += containerStatus.getRestartCount();
|
||||
if (!containerStatus.getReady()) {
|
||||
allReady = false;
|
||||
}
|
||||
|
||||
|
||||
// 容器状态
|
||||
V1ContainerState state = containerStatus.getState();
|
||||
if (state != null) {
|
||||
if (state.getRunning() != null) {
|
||||
containerInfo.setState("running");
|
||||
containerInfo.setState(ContainerStateEnum.RUNNING);
|
||||
if (state.getRunning().getStartedAt() != null) {
|
||||
containerInfo.setStartedAt(
|
||||
LocalDateTime.ofInstant(
|
||||
@ -509,57 +513,57 @@ public class K8sServiceIntegrationImpl extends BaseExternalSystemIntegration imp
|
||||
);
|
||||
}
|
||||
} else if (state.getWaiting() != null) {
|
||||
containerInfo.setState("waiting");
|
||||
containerInfo.setState(ContainerStateEnum.WAITING);
|
||||
containerInfo.setStateReason(state.getWaiting().getReason());
|
||||
containerInfo.setStateMessage(state.getWaiting().getMessage());
|
||||
} else if (state.getTerminated() != null) {
|
||||
containerInfo.setState("terminated");
|
||||
containerInfo.setState(ContainerStateEnum.TERMINATED);
|
||||
containerInfo.setStateReason(state.getTerminated().getReason());
|
||||
containerInfo.setStateMessage(state.getTerminated().getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
containers.add(containerInfo);
|
||||
}
|
||||
|
||||
|
||||
response.setContainers(containers);
|
||||
response.setRestartCount(totalRestartCount);
|
||||
response.setReady(allReady);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Spec信息
|
||||
if (pod.getSpec() != null) {
|
||||
response.setNodeName(pod.getSpec().getNodeName());
|
||||
|
||||
|
||||
// 容器资源配置
|
||||
if (pod.getSpec().getContainers() != null && response.getContainers() != null) {
|
||||
for (int i = 0; i < pod.getSpec().getContainers().size() && i < response.getContainers().size(); i++) {
|
||||
V1Container container = pod.getSpec().getContainers().get(i);
|
||||
K8sPodResponse.ContainerInfo containerInfo = response.getContainers().get(i);
|
||||
|
||||
|
||||
if (container.getResources() != null) {
|
||||
if (container.getResources().getRequests() != null) {
|
||||
containerInfo.setCpuRequest(
|
||||
container.getResources().getRequests().get("cpu") != null
|
||||
? container.getResources().getRequests().get("cpu").toSuffixedString()
|
||||
container.getResources().getRequests().get("cpu") != null
|
||||
? container.getResources().getRequests().get("cpu").toSuffixedString()
|
||||
: null
|
||||
);
|
||||
containerInfo.setMemoryRequest(
|
||||
container.getResources().getRequests().get("memory") != null
|
||||
? container.getResources().getRequests().get("memory").toSuffixedString()
|
||||
container.getResources().getRequests().get("memory") != null
|
||||
? container.getResources().getRequests().get("memory").toSuffixedString()
|
||||
: null
|
||||
);
|
||||
}
|
||||
if (container.getResources().getLimits() != null) {
|
||||
containerInfo.setCpuLimit(
|
||||
container.getResources().getLimits().get("cpu") != null
|
||||
? container.getResources().getLimits().get("cpu").toSuffixedString()
|
||||
container.getResources().getLimits().get("cpu") != null
|
||||
? container.getResources().getLimits().get("cpu").toSuffixedString()
|
||||
: null
|
||||
);
|
||||
containerInfo.setMemoryLimit(
|
||||
container.getResources().getLimits().get("memory") != null
|
||||
? container.getResources().getLimits().get("memory").toSuffixedString()
|
||||
container.getResources().getLimits().get("memory") != null
|
||||
? container.getResources().getLimits().get("memory").toSuffixedString()
|
||||
: null
|
||||
);
|
||||
}
|
||||
@ -567,47 +571,202 @@ public class K8sServiceIntegrationImpl extends BaseExternalSystemIntegration imp
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Owner信息
|
||||
if (pod.getMetadata().getOwnerReferences() != null && !pod.getMetadata().getOwnerReferences().isEmpty()) {
|
||||
V1OwnerReference owner = pod.getMetadata().getOwnerReferences().get(0);
|
||||
response.setOwnerKind(owner.getKind());
|
||||
response.setOwnerName(owner.getName());
|
||||
}
|
||||
|
||||
|
||||
// 序列化为YAML
|
||||
try {
|
||||
response.setYamlConfig(Yaml.dump(pod));
|
||||
} catch (Exception e) {
|
||||
log.warn("序列化Pod为YAML失败: {}", pod.getMetadata().getName(), e);
|
||||
}
|
||||
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建K8S ApiClient(对外接口,使用缓存)
|
||||
*
|
||||
* @param externalSystem K8S系统配置
|
||||
* @return ApiClient
|
||||
* 查询Pod日志
|
||||
*/
|
||||
private ApiClient createApiClient(ExternalSystem externalSystem) {
|
||||
return getApiClientCache(externalSystem).apiClient;
|
||||
@Override
|
||||
public String getPodLogs(ExternalSystem externalSystem, String namespace, String podName,
|
||||
String container, Integer tail, Integer sinceSeconds, Boolean follow) {
|
||||
log.info("查询K8S Pod日志,集群: {}, 命名空间: {}, Pod: {}, 容器: {}",
|
||||
externalSystem.getName(), namespace, podName, container);
|
||||
|
||||
try {
|
||||
K8sApiClientCache cache = getApiClientCache(externalSystem);
|
||||
CoreV1Api api = new CoreV1Api(cache.apiClient);
|
||||
|
||||
// 查询Pod日志
|
||||
String logs = api.readNamespacedPodLog(
|
||||
podName, // Pod名称
|
||||
namespace, // 命名空间
|
||||
container, // 容器名称(可选)
|
||||
follow != null && follow, // 是否持续输出
|
||||
null, // insecureSkipTLSVerifyBackend
|
||||
null, // limitBytes
|
||||
"false", // pretty
|
||||
false, // previous(是否查询上一个容器的日志)
|
||||
sinceSeconds, // sinceSeconds
|
||||
tail, // tail
|
||||
false // timestamps
|
||||
);
|
||||
|
||||
log.info("查询Pod日志成功,日志长度: {}", logs != null ? logs.length() : 0);
|
||||
return logs != null ? logs : "";
|
||||
|
||||
} catch (ApiException e) {
|
||||
if (e.getCode() == 404) {
|
||||
log.warn("Pod不存在: {}/{}", namespace, podName);
|
||||
throw new BusinessException(ResponseCode.K8S_POD_NOT_FOUND);
|
||||
}
|
||||
log.error("查询K8S Pod日志失败,集群: {}, 命名空间: {}, Pod: {}, 错误: {}",
|
||||
externalSystem.getName(), namespace, podName, e.getMessage(), e);
|
||||
throw new BusinessException(ResponseCode.K8S_OPERATION_FAILED);
|
||||
} catch (Exception e) {
|
||||
log.error("查询K8S Pod日志失败,集群: {}, 命名空间: {}, Pod: {}, 错误: {}",
|
||||
externalSystem.getName(), namespace, podName, e.getMessage(), e);
|
||||
throw new BusinessException(ResponseCode.K8S_OPERATION_FAILED);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 重启Deployment(通过更新annotation触发滚动更新)
|
||||
*/
|
||||
@Override
|
||||
public void restartDeployment(ExternalSystem externalSystem, String namespace, String deploymentName) {
|
||||
log.info("重启K8S Deployment,集群: {}, 命名空间: {}, Deployment: {}",
|
||||
externalSystem.getName(), namespace, deploymentName);
|
||||
|
||||
try {
|
||||
K8sApiClientCache cache = getApiClientCache(externalSystem);
|
||||
AppsV1Api api = new AppsV1Api(cache.apiClient);
|
||||
|
||||
// 构建patch内容:更新spec.template.metadata.annotations添加重启时间戳
|
||||
String patchBody = String.format(
|
||||
"{\"spec\":{\"template\":{\"metadata\":{\"annotations\":{\"kubectl.kubernetes.io/restartedAt\":\"%s\"}}}}}",
|
||||
LocalDateTime.now().toString()
|
||||
);
|
||||
|
||||
// 使用strategic merge patch更新Deployment
|
||||
api.patchNamespacedDeployment(
|
||||
deploymentName,
|
||||
namespace,
|
||||
new io.kubernetes.client.custom.V1Patch(patchBody),
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null
|
||||
);
|
||||
|
||||
log.info("重启K8S Deployment成功");
|
||||
|
||||
} catch (ApiException e) {
|
||||
if (e.getCode() == 404) {
|
||||
log.warn("Deployment不存在: {}/{}", namespace, deploymentName);
|
||||
throw new BusinessException(ResponseCode.K8S_RESOURCE_NOT_FOUND);
|
||||
}
|
||||
log.error("重启K8S Deployment失败,集群: {}, 命名空间: {}, Deployment: {}, 错误: {}",
|
||||
externalSystem.getName(), namespace, deploymentName, e.getMessage(), e);
|
||||
throw new BusinessException(ResponseCode.K8S_OPERATION_FAILED);
|
||||
} catch (Exception e) {
|
||||
log.error("重启K8S Deployment失败,集群: {}, 命名空间: {}, Deployment: {}, 错误: {}",
|
||||
externalSystem.getName(), namespace, deploymentName, e.getMessage(), e);
|
||||
throw new BusinessException(ResponseCode.K8S_OPERATION_FAILED);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 扩缩容Deployment
|
||||
*/
|
||||
@Override
|
||||
public void scaleDeployment(ExternalSystem externalSystem, String namespace, String deploymentName, Integer replicas) {
|
||||
log.info("扩缩容K8S Deployment,集群: {}, 命名空间: {}, Deployment: {}, 目标副本数: {}",
|
||||
externalSystem.getName(), namespace, deploymentName, replicas);
|
||||
|
||||
try {
|
||||
K8sApiClientCache cache = getApiClientCache(externalSystem);
|
||||
AppsV1Api api = new AppsV1Api(cache.apiClient);
|
||||
|
||||
// 构建patch内容:更新spec.replicas
|
||||
String patchBody = String.format("{\"spec\":{\"replicas\":%d}}", replicas);
|
||||
|
||||
// 使用strategic merge patch更新Deployment的scale
|
||||
api.patchNamespacedDeploymentScale(
|
||||
deploymentName,
|
||||
namespace,
|
||||
new io.kubernetes.client.custom.V1Patch(patchBody),
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null
|
||||
);
|
||||
|
||||
log.info("扩缩容K8S Deployment成功");
|
||||
|
||||
} catch (ApiException e) {
|
||||
if (e.getCode() == 404) {
|
||||
log.warn("Deployment不存在: {}/{}", namespace, deploymentName);
|
||||
throw new BusinessException(ResponseCode.K8S_RESOURCE_NOT_FOUND);
|
||||
}
|
||||
log.error("扩缩容K8S Deployment失败,集群: {}, 命名空间: {}, Deployment: {}, 错误: {}",
|
||||
externalSystem.getName(), namespace, deploymentName, e.getMessage(), e);
|
||||
throw new BusinessException(ResponseCode.K8S_OPERATION_FAILED);
|
||||
} catch (Exception e) {
|
||||
log.error("扩缩容K8S Deployment失败,集群: {}, 命名空间: {}, Deployment: {}, 错误: {}",
|
||||
externalSystem.getName(), namespace, deploymentName, e.getMessage(), e);
|
||||
throw new BusinessException(ResponseCode.K8S_OPERATION_FAILED);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 计算Deployment下所有Pod的总重启次数
|
||||
*/
|
||||
@Override
|
||||
public Integer calculateTotalRestartCount(ExternalSystem externalSystem, String namespace, String deploymentName) {
|
||||
log.debug("计算Deployment的总重启次数,集群: {}, 命名空间: {}, Deployment: {}",
|
||||
externalSystem.getName(), namespace, deploymentName);
|
||||
|
||||
try {
|
||||
// 查询Deployment下的所有Pod
|
||||
List<K8sPodResponse> pods = listPodsByDeployment(externalSystem, namespace, deploymentName);
|
||||
|
||||
// 累加所有Pod的重启次数
|
||||
int totalRestartCount = pods.stream()
|
||||
.mapToInt(pod -> pod.getRestartCount() != null ? pod.getRestartCount() : 0)
|
||||
.sum();
|
||||
|
||||
log.debug("Deployment总重启次数: {}", totalRestartCount);
|
||||
return totalRestartCount;
|
||||
|
||||
} catch (Exception e) {
|
||||
log.warn("计算Deployment总重启次数失败,集群: {}, 命名空间: {}, Deployment: {}, 错误: {}",
|
||||
externalSystem.getName(), namespace, deploymentName, e.getMessage());
|
||||
// 计算失败时返回null,不影响同步流程
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建K8S ApiClient(内部实现,不使用缓存)
|
||||
*
|
||||
*
|
||||
* @param externalSystem K8S系统配置
|
||||
* @return ApiClient
|
||||
*/
|
||||
private ApiClient createApiClientInternal(ExternalSystem externalSystem) throws Exception {
|
||||
String config = externalSystem.getConfig();
|
||||
|
||||
|
||||
if (config == null || config.trim().isEmpty()) {
|
||||
throw new BusinessException(ResponseCode.K8S_CONFIG_EMPTY);
|
||||
}
|
||||
|
||||
|
||||
// 直接使用config作为kubeconfig内容
|
||||
ApiClient client = Config.fromConfig(new StringReader(config));
|
||||
client.setConnectTimeout(15000); // 15秒连接超时
|
||||
|
||||
@ -12,6 +12,7 @@ public class K8sDeploymentResponse {
|
||||
private Integer replicas;
|
||||
private Integer availableReplicas;
|
||||
private Integer readyReplicas;
|
||||
private Integer totalRestartCount;
|
||||
private String image;
|
||||
private Map<String, String> labels;
|
||||
private Map<String, String> selector;
|
||||
|
||||
@ -1,5 +1,7 @@
|
||||
package com.qqchen.deploy.backend.deploy.integration.response;
|
||||
|
||||
import com.qqchen.deploy.backend.deploy.enums.ContainerStateEnum;
|
||||
import com.qqchen.deploy.backend.deploy.enums.PodPhaseEnum;
|
||||
import lombok.Data;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
@ -23,9 +25,9 @@ public class K8sPodResponse {
|
||||
private String namespace;
|
||||
|
||||
/**
|
||||
* Pod状态阶段(Running, Pending, Succeeded, Failed, Unknown)
|
||||
* Pod状态阶段
|
||||
*/
|
||||
private String phase;
|
||||
private PodPhaseEnum phase;
|
||||
|
||||
/**
|
||||
* Pod状态原因
|
||||
@ -123,9 +125,9 @@ public class K8sPodResponse {
|
||||
private String imageID;
|
||||
|
||||
/**
|
||||
* 容器状态(running, waiting, terminated)
|
||||
* 容器状态
|
||||
*/
|
||||
private String state;
|
||||
private ContainerStateEnum state;
|
||||
|
||||
/**
|
||||
* 是否就绪
|
||||
|
||||
@ -20,4 +20,19 @@ public interface IK8sDeploymentService extends IBaseService<K8sDeployment, K8sDe
|
||||
List<K8sDeploymentDTO> findByExternalSystemId(Long externalSystemId);
|
||||
|
||||
List<K8sDeploymentDTO> findByNamespaceId(Long namespaceId);
|
||||
|
||||
/**
|
||||
* 重启Deployment
|
||||
*
|
||||
* @param deploymentId Deployment ID
|
||||
*/
|
||||
void restartDeployment(Long deploymentId);
|
||||
|
||||
/**
|
||||
* 扩缩容Deployment
|
||||
*
|
||||
* @param deploymentId Deployment ID
|
||||
* @param replicas 目标副本数
|
||||
*/
|
||||
void scaleDeployment(Long deploymentId, Integer replicas);
|
||||
}
|
||||
|
||||
@ -53,4 +53,16 @@ public interface IK8sPodService {
|
||||
* @return Pod列表
|
||||
*/
|
||||
List<K8sPodResponse> listPodsByNamespace(Long namespaceId);
|
||||
|
||||
/**
|
||||
* 查询Pod日志
|
||||
*
|
||||
* @param deploymentId Deployment ID
|
||||
* @param podName Pod名称
|
||||
* @param container 容器名称(可选)
|
||||
* @param tail 返回最后N行日志(可选)
|
||||
* @param sinceSeconds 返回最近N秒的日志(可选)
|
||||
* @return Pod日志内容
|
||||
*/
|
||||
String getPodLogs(Long deploymentId, String podName, String container, Integer tail, Integer sinceSeconds);
|
||||
}
|
||||
|
||||
@ -99,6 +99,13 @@ public class K8sDeploymentServiceImpl extends BaseServiceImpl<K8sDeployment, K8s
|
||||
deployment.setK8sCreateTime(response.getCreationTimestamp());
|
||||
deployment.setK8sUpdateTime(response.getLastUpdateTime());
|
||||
deployment.setYamlConfig(response.getYamlConfig());
|
||||
|
||||
// 计算并缓存总重启次数
|
||||
Integer totalRestartCount = k8sServiceIntegration.calculateTotalRestartCount(
|
||||
externalSystem, namespace.getNamespaceName(), response.getName()
|
||||
);
|
||||
deployment.setTotalRestartCount(totalRestartCount);
|
||||
|
||||
deploymentsToSave.add(deployment);
|
||||
}
|
||||
|
||||
@ -222,5 +229,67 @@ public class K8sDeploymentServiceImpl extends BaseServiceImpl<K8sDeployment, K8s
|
||||
List<K8sDeployment> deployments = k8sDeploymentRepository.findByNamespaceId(namespaceId);
|
||||
return k8sDeploymentConverter.toDtoList(deployments);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void restartDeployment(Long deploymentId) {
|
||||
log.info("重启Deployment,deploymentId: {}", deploymentId);
|
||||
|
||||
// 1. 查询K8sDeployment
|
||||
K8sDeployment deployment = k8sDeploymentRepository.findById(deploymentId)
|
||||
.orElseThrow(() -> new BusinessException(ResponseCode.K8S_RESOURCE_NOT_FOUND));
|
||||
|
||||
// 2. 查询K8sNamespace
|
||||
K8sNamespace namespace = k8sNamespaceRepository.findById(deployment.getNamespaceId())
|
||||
.orElseThrow(() -> new BusinessException(ResponseCode.K8S_RESOURCE_NOT_FOUND));
|
||||
|
||||
// 3. 查询ExternalSystem
|
||||
ExternalSystem externalSystem = externalSystemRepository.findById(deployment.getExternalSystemId())
|
||||
.orElseThrow(() -> new BusinessException(ResponseCode.EXTERNAL_SYSTEM_NOT_FOUND));
|
||||
|
||||
// 4. 调用K8s API重启Deployment
|
||||
k8sServiceIntegration.restartDeployment(
|
||||
externalSystem,
|
||||
namespace.getNamespaceName(),
|
||||
deployment.getDeploymentName()
|
||||
);
|
||||
|
||||
log.info("Deployment重启成功,deploymentId: {}", deploymentId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void scaleDeployment(Long deploymentId, Integer replicas) {
|
||||
log.info("扩缩容Deployment,deploymentId: {}, replicas: {}", deploymentId, replicas);
|
||||
|
||||
// 参数校验
|
||||
if (replicas == null || replicas < 0) {
|
||||
throw new BusinessException(ResponseCode.INVALID_PARAM);
|
||||
}
|
||||
|
||||
// 1. 查询K8sDeployment
|
||||
K8sDeployment deployment = k8sDeploymentRepository.findById(deploymentId)
|
||||
.orElseThrow(() -> new BusinessException(ResponseCode.K8S_RESOURCE_NOT_FOUND));
|
||||
|
||||
// 2. 查询K8sNamespace
|
||||
K8sNamespace namespace = k8sNamespaceRepository.findById(deployment.getNamespaceId())
|
||||
.orElseThrow(() -> new BusinessException(ResponseCode.K8S_RESOURCE_NOT_FOUND));
|
||||
|
||||
// 3. 查询ExternalSystem
|
||||
ExternalSystem externalSystem = externalSystemRepository.findById(deployment.getExternalSystemId())
|
||||
.orElseThrow(() -> new BusinessException(ResponseCode.EXTERNAL_SYSTEM_NOT_FOUND));
|
||||
|
||||
// 4. 调用K8s API扩缩容Deployment
|
||||
k8sServiceIntegration.scaleDeployment(
|
||||
externalSystem,
|
||||
namespace.getNamespaceName(),
|
||||
deployment.getDeploymentName(),
|
||||
replicas
|
||||
);
|
||||
|
||||
// 5. 更新本地数据库记录
|
||||
deployment.setReplicas(replicas);
|
||||
k8sDeploymentRepository.save(deployment);
|
||||
|
||||
log.info("Deployment扩缩容成功,deploymentId: {}, replicas: {}", deploymentId, replicas);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -129,4 +129,33 @@ public class K8sPodServiceImpl implements IK8sPodService {
|
||||
// 4. 调用K8s API查询Pod详情
|
||||
return k8sServiceIntegration.getPod(externalSystem, namespace.getNamespaceName(), podName);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getPodLogs(Long deploymentId, String podName, String container, Integer tail, Integer sinceSeconds) {
|
||||
log.info("查询Pod日志,deploymentId: {}, podName: {}, container: {}, tail: {}, sinceSeconds: {}",
|
||||
deploymentId, podName, container, tail, sinceSeconds);
|
||||
|
||||
// 1. 查询K8sDeployment
|
||||
K8sDeployment deployment = k8sDeploymentRepository.findById(deploymentId)
|
||||
.orElseThrow(() -> new BusinessException(ResponseCode.K8S_RESOURCE_NOT_FOUND));
|
||||
|
||||
// 2. 查询K8sNamespace
|
||||
K8sNamespace namespace = k8sNamespaceRepository.findById(deployment.getNamespaceId())
|
||||
.orElseThrow(() -> new BusinessException(ResponseCode.K8S_RESOURCE_NOT_FOUND));
|
||||
|
||||
// 3. 查询ExternalSystem
|
||||
ExternalSystem externalSystem = externalSystemRepository.findById(deployment.getExternalSystemId())
|
||||
.orElseThrow(() -> new BusinessException(ResponseCode.EXTERNAL_SYSTEM_NOT_FOUND));
|
||||
|
||||
// 4. 调用K8s API查询Pod日志
|
||||
return k8sServiceIntegration.getPodLogs(
|
||||
externalSystem,
|
||||
namespace.getNamespaceName(),
|
||||
podName,
|
||||
container,
|
||||
tail,
|
||||
sinceSeconds,
|
||||
false // follow参数设为false,不持续输出
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user