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));
|
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
|
@Override
|
||||||
protected void exportData(HttpServletResponse response, List<K8sDeploymentDTO> data) {
|
protected void exportData(HttpServletResponse response, List<K8sDeploymentDTO> data) {
|
||||||
log.info("导出K8S Deployment数据,数据量:{}", data.size());
|
log.info("导出K8S Deployment数据,数据量:{}", data.size());
|
||||||
|
|||||||
@ -31,6 +31,9 @@ public class K8sDeploymentDTO extends BaseDTO {
|
|||||||
@Schema(description = "就绪副本数")
|
@Schema(description = "就绪副本数")
|
||||||
private Integer readyReplicas;
|
private Integer readyReplicas;
|
||||||
|
|
||||||
|
@Schema(description = "总重启次数(所有Pod的重启次数总和)")
|
||||||
|
private Integer totalRestartCount;
|
||||||
|
|
||||||
@Schema(description = "容器镜像")
|
@Schema(description = "容器镜像")
|
||||||
private String image;
|
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")
|
@Column(name = "ready_replicas")
|
||||||
private Integer readyReplicas;
|
private Integer readyReplicas;
|
||||||
|
|
||||||
|
@Column(name = "total_restart_count")
|
||||||
|
private Integer totalRestartCount;
|
||||||
|
|
||||||
@Column(name = "image")
|
@Column(name = "image")
|
||||||
private String 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);
|
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;
|
package com.qqchen.deploy.backend.deploy.integration.impl;
|
||||||
|
|
||||||
import com.qqchen.deploy.backend.deploy.entity.ExternalSystem;
|
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.framework.utils.JsonUtils;
|
||||||
import com.qqchen.deploy.backend.deploy.integration.IK8sServiceIntegration;
|
import com.qqchen.deploy.backend.deploy.integration.IK8sServiceIntegration;
|
||||||
import com.qqchen.deploy.backend.deploy.integration.response.K8sDeploymentResponse;
|
import com.qqchen.deploy.backend.deploy.integration.response.K8sDeploymentResponse;
|
||||||
@ -38,6 +40,7 @@ public class K8sServiceIntegrationImpl extends BaseExternalSystemIntegration imp
|
|||||||
|
|
||||||
// K8S ApiClient缓存 - 线程安全
|
// K8S ApiClient缓存 - 线程安全
|
||||||
private static final Map<Long, K8sApiClientCache> API_CLIENT_CACHE = new ConcurrentHashMap<>();
|
private static final Map<Long, K8sApiClientCache> API_CLIENT_CACHE = new ConcurrentHashMap<>();
|
||||||
|
|
||||||
private static final long CACHE_EXPIRE_TIME = 30 * 60 * 1000; // 30分钟过期
|
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 {
|
private static class K8sApiClientCache {
|
||||||
final ApiClient apiClient;
|
final ApiClient apiClient;
|
||||||
|
|
||||||
final long expireTime;
|
final long expireTime;
|
||||||
|
|
||||||
K8sApiClientCache(ApiClient apiClient) {
|
K8sApiClientCache(ApiClient apiClient) {
|
||||||
@ -460,7 +464,7 @@ public class K8sServiceIntegrationImpl extends BaseExternalSystemIntegration imp
|
|||||||
|
|
||||||
// 状态信息
|
// 状态信息
|
||||||
if (pod.getStatus() != null) {
|
if (pod.getStatus() != null) {
|
||||||
response.setPhase(pod.getStatus().getPhase());
|
response.setPhase(PodPhaseEnum.fromCode(pod.getStatus().getPhase()));
|
||||||
response.setReason(pod.getStatus().getReason());
|
response.setReason(pod.getStatus().getReason());
|
||||||
response.setMessage(pod.getStatus().getMessage());
|
response.setMessage(pod.getStatus().getMessage());
|
||||||
response.setPodIP(pod.getStatus().getPodIP());
|
response.setPodIP(pod.getStatus().getPodIP());
|
||||||
@ -499,7 +503,7 @@ public class K8sServiceIntegrationImpl extends BaseExternalSystemIntegration imp
|
|||||||
V1ContainerState state = containerStatus.getState();
|
V1ContainerState state = containerStatus.getState();
|
||||||
if (state != null) {
|
if (state != null) {
|
||||||
if (state.getRunning() != null) {
|
if (state.getRunning() != null) {
|
||||||
containerInfo.setState("running");
|
containerInfo.setState(ContainerStateEnum.RUNNING);
|
||||||
if (state.getRunning().getStartedAt() != null) {
|
if (state.getRunning().getStartedAt() != null) {
|
||||||
containerInfo.setStartedAt(
|
containerInfo.setStartedAt(
|
||||||
LocalDateTime.ofInstant(
|
LocalDateTime.ofInstant(
|
||||||
@ -509,11 +513,11 @@ public class K8sServiceIntegrationImpl extends BaseExternalSystemIntegration imp
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
} else if (state.getWaiting() != null) {
|
} else if (state.getWaiting() != null) {
|
||||||
containerInfo.setState("waiting");
|
containerInfo.setState(ContainerStateEnum.WAITING);
|
||||||
containerInfo.setStateReason(state.getWaiting().getReason());
|
containerInfo.setStateReason(state.getWaiting().getReason());
|
||||||
containerInfo.setStateMessage(state.getWaiting().getMessage());
|
containerInfo.setStateMessage(state.getWaiting().getMessage());
|
||||||
} else if (state.getTerminated() != null) {
|
} else if (state.getTerminated() != null) {
|
||||||
containerInfo.setState("terminated");
|
containerInfo.setState(ContainerStateEnum.TERMINATED);
|
||||||
containerInfo.setStateReason(state.getTerminated().getReason());
|
containerInfo.setStateReason(state.getTerminated().getReason());
|
||||||
containerInfo.setStateMessage(state.getTerminated().getMessage());
|
containerInfo.setStateMessage(state.getTerminated().getMessage());
|
||||||
}
|
}
|
||||||
@ -586,13 +590,168 @@ public class K8sServiceIntegrationImpl extends BaseExternalSystemIntegration imp
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 创建K8S ApiClient(对外接口,使用缓存)
|
* 查询Pod日志
|
||||||
*
|
|
||||||
* @param externalSystem K8S系统配置
|
|
||||||
* @return ApiClient
|
|
||||||
*/
|
*/
|
||||||
private ApiClient createApiClient(ExternalSystem externalSystem) {
|
@Override
|
||||||
return getApiClientCache(externalSystem).apiClient;
|
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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@ -12,6 +12,7 @@ public class K8sDeploymentResponse {
|
|||||||
private Integer replicas;
|
private Integer replicas;
|
||||||
private Integer availableReplicas;
|
private Integer availableReplicas;
|
||||||
private Integer readyReplicas;
|
private Integer readyReplicas;
|
||||||
|
private Integer totalRestartCount;
|
||||||
private String image;
|
private String image;
|
||||||
private Map<String, String> labels;
|
private Map<String, String> labels;
|
||||||
private Map<String, String> selector;
|
private Map<String, String> selector;
|
||||||
|
|||||||
@ -1,5 +1,7 @@
|
|||||||
package com.qqchen.deploy.backend.deploy.integration.response;
|
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 lombok.Data;
|
||||||
|
|
||||||
import java.time.LocalDateTime;
|
import java.time.LocalDateTime;
|
||||||
@ -23,9 +25,9 @@ public class K8sPodResponse {
|
|||||||
private String namespace;
|
private String namespace;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Pod状态阶段(Running, Pending, Succeeded, Failed, Unknown)
|
* Pod状态阶段
|
||||||
*/
|
*/
|
||||||
private String phase;
|
private PodPhaseEnum phase;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Pod状态原因
|
* Pod状态原因
|
||||||
@ -123,9 +125,9 @@ public class K8sPodResponse {
|
|||||||
private String imageID;
|
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> findByExternalSystemId(Long externalSystemId);
|
||||||
|
|
||||||
List<K8sDeploymentDTO> findByNamespaceId(Long namespaceId);
|
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列表
|
* @return Pod列表
|
||||||
*/
|
*/
|
||||||
List<K8sPodResponse> listPodsByNamespace(Long namespaceId);
|
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.setK8sCreateTime(response.getCreationTimestamp());
|
||||||
deployment.setK8sUpdateTime(response.getLastUpdateTime());
|
deployment.setK8sUpdateTime(response.getLastUpdateTime());
|
||||||
deployment.setYamlConfig(response.getYamlConfig());
|
deployment.setYamlConfig(response.getYamlConfig());
|
||||||
|
|
||||||
|
// 计算并缓存总重启次数
|
||||||
|
Integer totalRestartCount = k8sServiceIntegration.calculateTotalRestartCount(
|
||||||
|
externalSystem, namespace.getNamespaceName(), response.getName()
|
||||||
|
);
|
||||||
|
deployment.setTotalRestartCount(totalRestartCount);
|
||||||
|
|
||||||
deploymentsToSave.add(deployment);
|
deploymentsToSave.add(deployment);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -222,5 +229,67 @@ public class K8sDeploymentServiceImpl extends BaseServiceImpl<K8sDeployment, K8s
|
|||||||
List<K8sDeployment> deployments = k8sDeploymentRepository.findByNamespaceId(namespaceId);
|
List<K8sDeployment> deployments = k8sDeploymentRepository.findByNamespaceId(namespaceId);
|
||||||
return k8sDeploymentConverter.toDtoList(deployments);
|
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详情
|
// 4. 调用K8s API查询Pod详情
|
||||||
return k8sServiceIntegration.getPod(externalSystem, namespace.getNamespaceName(), podName);
|
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