1.32 k8s开发

This commit is contained in:
dengqichen 2025-12-15 16:39:46 +08:00
parent d5188bc94d
commit a19b4b8ec8
3 changed files with 88 additions and 48 deletions

View File

@ -633,51 +633,82 @@ public class K8sServiceIntegrationImpl extends BaseExternalSystemIntegration imp
/** /**
* 重启Deployment通过更新annotation触发滚动更新 * 重启Deployment通过更新annotation触发滚动更新
*
* <p>实现原理</p>
* <ul>
* <li>使用Strategic Merge Patch方式更新Deployment的Pod模板注解</li>
* <li>添加/更新 kubectl.kubernetes.io/restartedAt 注解值为当前时间戳</li>
* <li>K8s检测到Pod模板变化后会触发滚动更新Rolling Update</li>
* </ul>
*
* <p>安全性说明</p>
* <ul>
* <li><b>Strategic Merge Patch是合并式操作</b>只更新指定字段不会影响其他配置</li>
* <li><b>不会覆盖原有内容</b>对于annotations这种map类型会合并新的key-value保留已有的annotations</li>
* <li><b>K8s官方推荐方式</b>kubectl rollout restart命令的底层实现就是这种方式</li>
* <li><b>原有YAML配置完全保留</b>包括labels环境变量资源限制挂载卷等所有配置</li>
* </ul>
*
* <p>等价于执行命令kubectl rollout restart deployment/{deploymentName} -n {namespace}</p>
*/ */
@Override @Override
public void restartDeployment(ExternalSystem externalSystem, String namespace, String deploymentName) { public void restartDeployment(ExternalSystem externalSystem, String namespace, String deploymentName) {
log.info("重启K8S Deployment集群: {}, 命名空间: {}, Deployment: {}", log.info("开始重启K8S Deployment集群: {}, 命名空间: {}, Deployment: {}",
externalSystem.getName(), namespace, deploymentName); externalSystem.getName(), namespace, deploymentName);
try { try {
K8sApiClientCache cache = getApiClientCache(externalSystem); K8sApiClientCache cache = getApiClientCache(externalSystem);
AppsV1Api api = new AppsV1Api(cache.apiClient); AppsV1Api api = new AppsV1Api(cache.apiClient);
// 构建patch内容更新spec.template.metadata.annotations添加重启时间戳 // 生成ISO 8601格式的时间戳作为重启标记
// 使用ISO 8601格式的时间戳
String timestamp = java.time.format.DateTimeFormatter.ISO_INSTANT String timestamp = java.time.format.DateTimeFormatter.ISO_INSTANT
.format(java.time.Instant.now()); .format(java.time.Instant.now());
// 构建Strategic Merge Patch内容
// 只更新 spec.template.metadata.annotations 中的 kubectl.kubernetes.io/restartedAt 字段
// 其他所有字段replicasimageenvresources等都不会受影响
String patchBody = String.format( String patchBody = String.format(
"{\"spec\":{\"template\":{\"metadata\":{\"annotations\":{\"kubectl.kubernetes.io/restartedAt\":\"%s\"}}}}}", "{\"spec\":{\"template\":{\"metadata\":{\"annotations\":{\"kubectl.kubernetes.io/restartedAt\":\"%s\"}}}}}",
timestamp timestamp
); );
// 使用strategic merge patch更新Deployment log.info("执行Strategic Merge PatchDeployment: {}/{}, 重启时间戳: {}",
api.patchNamespacedDeployment( namespace, deploymentName, timestamp);
deploymentName,
namespace, // 使用PatchUtils进行strategic merge patch
new io.kubernetes.client.custom.V1Patch(patchBody), // Strategic Merge Patch会智能合并JSON只更新指定的字段路径
null, io.kubernetes.client.util.PatchUtils.patch(
null, io.kubernetes.client.openapi.models.V1Deployment.class,
null, () -> api.patchNamespacedDeploymentCall(
null, deploymentName,
null namespace,
new io.kubernetes.client.custom.V1Patch(patchBody),
null, // pretty
null, // dryRun
null, // fieldManager
null, // fieldValidation
null, // force
null // callback
),
io.kubernetes.client.custom.V1Patch.PATCH_FORMAT_STRATEGIC_MERGE_PATCH,
api.getApiClient()
); );
log.info("重启K8S Deployment成功"); log.info("重启K8S Deployment成功集群: {}, 命名空间: {}, Deployment: {}, K8s将开始滚动更新Pod",
externalSystem.getName(), namespace, deploymentName);
} catch (ApiException e) { } catch (ApiException e) {
if (e.getCode() == 404) { if (e.getCode() == 404) {
log.warn("Deployment不存在: {}/{}", namespace, deploymentName); log.warn("重启失败Deployment不存在集群: {}, 命名空间: {}, Deployment: {}",
externalSystem.getName(), namespace, deploymentName);
throw new BusinessException(ResponseCode.K8S_RESOURCE_NOT_FOUND); throw new BusinessException(ResponseCode.K8S_RESOURCE_NOT_FOUND);
} }
log.error("重启K8S Deployment失败集群: {}, 命名空间: {}, Deployment: {}, HTTP状态码: {}, 错误信息: {}, 响应体: {}", log.error("重启K8S Deployment失败集群: {}, 命名空间: {}, Deployment: {}, HTTP状态码: {}, 错误信息: {}, 响应体: {}",
externalSystem.getName(), namespace, deploymentName, e.getCode(), e.getMessage(), e.getResponseBody(), e); externalSystem.getName(), namespace, deploymentName, e.getCode(), e.getMessage(), e.getResponseBody(), e);
throw new BusinessException(ResponseCode.K8S_OPERATION_FAILED); throw new BusinessException(ResponseCode.K8S_OPERATION_FAILED);
} catch (Exception e) { } catch (Exception e) {
log.error("重启K8S Deployment失败,集群: {}, 命名空间: {}, Deployment: {}, 错误: {}", log.error("重启K8S Deployment失败非K8s API异常,集群: {}, 命名空间: {}, Deployment: {}, 错误类型: {}, 错误信息: {}",
externalSystem.getName(), namespace, deploymentName, e.getMessage(), e); externalSystem.getName(), namespace, deploymentName, e.getClass().getSimpleName(), e.getMessage(), e);
throw new BusinessException(ResponseCode.K8S_OPERATION_FAILED); throw new BusinessException(ResponseCode.K8S_OPERATION_FAILED);
} }
} }
@ -697,16 +728,22 @@ public class K8sServiceIntegrationImpl extends BaseExternalSystemIntegration imp
// 构建patch内容更新spec.replicas // 构建patch内容更新spec.replicas
String patchBody = String.format("{\"spec\":{\"replicas\":%d}}", replicas); String patchBody = String.format("{\"spec\":{\"replicas\":%d}}", replicas);
// 使用strategic merge patch更新Deployment的scale // 使用PatchUtils进行strategic merge patch确保Content-Type正确
api.patchNamespacedDeploymentScale( io.kubernetes.client.util.PatchUtils.patch(
deploymentName, io.kubernetes.client.openapi.models.V1Scale.class,
namespace, () -> api.patchNamespacedDeploymentScaleCall(
new io.kubernetes.client.custom.V1Patch(patchBody), deploymentName,
null, namespace,
null, new io.kubernetes.client.custom.V1Patch(patchBody),
null, null, // pretty
null, null, // dryRun
null null, // fieldManager
null, // fieldValidation
null, // force
null // callback
),
io.kubernetes.client.custom.V1Patch.PATCH_FORMAT_STRATEGIC_MERGE_PATCH,
api.getApiClient()
); );
log.info("扩缩容K8S Deployment成功"); log.info("扩缩容K8S Deployment成功");

View File

@ -191,9 +191,12 @@ public class K8sDeploymentServiceImpl extends BaseServiceImpl<K8sDeployment, K8s
int syncCount = (int) deploymentResponses.size(); int syncCount = (int) deploymentResponses.size();
log.info("K8S Deployment同步完成集群: {}, Namespace: {}, 总数: {}, 保存: {}, Deployment未变化: {}, 仅更新Pod统计: {}", // 优化日志输出单个Namespace的同步日志使用DEBUG级别减少日志噪音
// 详细信息在集群级别的汇总日志中体现
log.debug("K8S Deployment同步完成集群: {}, Namespace: {}, 总数: {}, 保存: {}, Deployment未变化: {}, 仅更新Pod统计: {}",
externalSystem.getName(), namespace.getNamespaceName(), syncCount, externalSystem.getName(), namespace.getNamespaceName(), syncCount,
deploymentsToSave.size(), unchangedCount, podStatsOnlyUpdateCount); deploymentsToSave.size(), unchangedCount, podStatsOnlyUpdateCount);
return syncCount; return syncCount;
} catch (Exception e) { } catch (Exception e) {

File diff suppressed because one or more lines are too long