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触发滚动更新
*
* <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
public void restartDeployment(ExternalSystem externalSystem, String namespace, String deploymentName) {
log.info("重启K8S Deployment集群: {}, 命名空间: {}, Deployment: {}",
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添加重启时间戳
// 使用ISO 8601格式的时间戳
// 生成ISO 8601格式的时间戳作为重启标记
String timestamp = java.time.format.DateTimeFormatter.ISO_INSTANT
.format(java.time.Instant.now());
// 构建Strategic Merge Patch内容
// 只更新 spec.template.metadata.annotations 中的 kubectl.kubernetes.io/restartedAt 字段
// 其他所有字段replicasimageenvresources等都不会受影响
String patchBody = String.format(
"{\"spec\":{\"template\":{\"metadata\":{\"annotations\":{\"kubectl.kubernetes.io/restartedAt\":\"%s\"}}}}}",
timestamp
);
// 使用strategic merge patch更新Deployment
api.patchNamespacedDeployment(
deploymentName,
namespace,
new io.kubernetes.client.custom.V1Patch(patchBody),
null,
null,
null,
null,
null
log.info("执行Strategic Merge PatchDeployment: {}/{}, 重启时间戳: {}",
namespace, deploymentName, timestamp);
// 使用PatchUtils进行strategic merge patch
// Strategic Merge Patch会智能合并JSON只更新指定的字段路径
io.kubernetes.client.util.PatchUtils.patch(
io.kubernetes.client.openapi.models.V1Deployment.class,
() -> api.patchNamespacedDeploymentCall(
deploymentName,
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) {
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);
}
log.error("重启K8S Deployment失败集群: {}, 命名空间: {}, Deployment: {}, HTTP状态码: {}, 错误信息: {}, 响应体: {}",
externalSystem.getName(), namespace, deploymentName, e.getCode(), e.getMessage(), e.getResponseBody(), e);
throw new BusinessException(ResponseCode.K8S_OPERATION_FAILED);
} catch (Exception e) {
log.error("重启K8S Deployment失败,集群: {}, 命名空间: {}, Deployment: {}, 错误: {}",
externalSystem.getName(), namespace, deploymentName, e.getMessage(), e);
log.error("重启K8S Deployment失败非K8s API异常,集群: {}, 命名空间: {}, Deployment: {}, 错误类型: {}, 错误信息: {}",
externalSystem.getName(), namespace, deploymentName, e.getClass().getSimpleName(), e.getMessage(), e);
throw new BusinessException(ResponseCode.K8S_OPERATION_FAILED);
}
}
@ -697,16 +728,22 @@ public class K8sServiceIntegrationImpl extends BaseExternalSystemIntegration imp
// 构建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
// 使用PatchUtils进行strategic merge patch确保Content-Type正确
io.kubernetes.client.util.PatchUtils.patch(
io.kubernetes.client.openapi.models.V1Scale.class,
() -> api.patchNamespacedDeploymentScaleCall(
deploymentName,
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成功");

View File

@ -191,9 +191,12 @@ public class K8sDeploymentServiceImpl extends BaseServiceImpl<K8sDeployment, K8s
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,
deploymentsToSave.size(), unchangedCount, podStatsOnlyUpdateCount);
return syncCount;
} catch (Exception e) {

File diff suppressed because one or more lines are too long