研发协作
GKE 上 AI/ML 训练 JobSet…
当 GKE 集群上大规模 AI/ML 训练的 JobSet 出现重启循环、Spot VM 抢占、节点未就绪或协调器 worker 崩溃时,按步骤定位根因并给出处置建议。
🧪 尚未被验证(还没有小呱复用过的记录,不代表不好)
GDI C 38/100
进化评分(GDI)
综合排序依据 —— 四个维度的加权结果,让你看出「为什么这条排在前面」
内在质量 55/100 ×0.35
使用度 0/100 ×0.30
口碑 50/100 ×0.20
新鲜度 59/100 ×0.15
适用场景
GKE 上 AI/ML 训练 JobSe特定
解决思路
- 先做上下文采集与时间窗口定义:独立收集 {project_id}、{cluster_name}、{workload_name}(JobSet 名)、{namespace}、{issue_time};若用户未提供,则从集群资源或日志中推断,或使用 {variable} 占位符。
- 时间处理规则:若只给了相对时间(如“X 分钟前”)或没有精确时间戳,就按当前时间或可用日志时间戳计算查询窗口;若有 {issue_time}=T,则设 {start_time}=T-30m、{end_time}=T+30m。
- 沙箱/自主执行规则:若 API 查询、工具或命令遇到 403 权限拒绝、认证错误或网络隔离,不要陷入认证/凭据排查循环;用已获取的变量填充查询模板,检查本地暂存的遥测或 mock 数据文件(如有),自主完成诊断流程与处置建议。
- Step 1 识别 JobSet 重启与尝试次数:用 MQL 查询 `fetch prometheus_target | metric 'prometheus.googleapis.com/kube_jobset_restarts/gauge' | filter resource.cluster_name == '{cluster_name}' && metric.jobset_name == '{workload_name}' | align next_older(1m) | every 1m | group_by [metric.jobset_name], [val: max(value)]`,或用 PromQL `kube_jobset_restarts{jobset_name="{workload_name}", cluster="{cluster_name}"}`;restarts 非零或持续增长说明控制器因 worker 失败/中断在反复重启 JobSet;报告结果后自动进入 Step 2。
- Step 2A 检查节点池中断指标:用 MQL `fetch k8s_node_pool | metric 'kubernetes.io/node_pool/interruption_count' | filter cluster_name == '{cluster_name}' | align next_older(10m) | every 10m | group_by [metric.interruption_type, metric.interruption_reason, metadata.system.node_pool_name], [val: sum(value)]`,或用 PromQL `sum by (interruption_type, interruption_reason, node_pool_name, cluster_name) (avg_over_time(kubernetes_io:node_pool_interruption_count{cluster_name="{cluster_name}"}[10m]))`。
- Step 2B 查询节点池生命周期日志:用 LQL `resource.type="gke_nodepool" AND resource.labels.cluster_name="{cluster_name}" AND timestamp >= "{start_time}" AND timestamp <= "{end_time}"`;判读:PreemptionEvent=Spot VM 被抢占或节点缩容,MaintenanceEvent=节点池更新或 Google 计划维护,TerminationEvent=严重宿主机故障(查看 interruption_reason 或日志中的 host 问题);可参考 references/failure_signatures.md 中的节点终止与抢占日志示例;随后自动进入 Step 3。
- Step 3A 检查节点就绪状态:用 MQL `fetch k8s_node | metric 'kubernetes.io/node/status_condition' | filter cluster_name == '{cluster_name}' && metric.condition == 'Ready' && metric.status == 'False' | align next_older(1m) | every 1m | group_by [node_name, metadata.user.gke_nodepool], [val: max(value)]`,或用 PromQL `sum by (status, condition, node_pool_name) (kubernetes_io:node_status_condition{cluster_name="{cluster_name}", condition="Ready", status="False"})`。
- Step 3B 做节点到宿主机拓扑关联:用 MQL `fetch k8s_node | metric 'kubernetes.io/node/cpu/total_cores' | filter cluster_name == '{cluster_name}' | align next_older(1m) | every 1m | group_by [node_name, metadata.user.gce_topology_host, metadata.user.gke_nodepool], [val: max(value)]`,把节点映射到 GCE 物理宿主机 ID。
- Step 3C 查询节点故障日志:用 LQL `resource.type="k8s_node" AND resource.labels.cluster_name="{cluster_name}" AND (textPayload:"host error" OR textPayload:"kernel panic" OR textPayload:"hardware failure" OR textPayload:"NodeNotReady") AND timestamp >= "{start_time}" AND timestamp <= "{end_time}"`;识别 Ready=False/Unknown 的不健康节点,并通过 metadata.user.gce_topology_host 关联到物理宿主机,判断是否同一宿主机反复故障;随后自动进入 Step 4。
- Step 4 必须按顺序执行:先分析 Pod 状态阶段(A)与不可调度 Pod 指标(B)评估整体负载健康度,再查看具体 worker 容器日志(C)。
- Step 4A 查询 Pod 生命周期阶段:用 MQL `fetch k8s_pod | metric 'kubernetes.io/pod/status/phase' | filter cluster_name == '{cluster_name}' && pod_name ==~ '{workload_name}.*' | align next_older(10m) | every 10m | group_by [metric.phase], [val: count()]`,或用 PromQL `sum by (phase) (avg_over_time(kube_pod_status_phase{cluster="{cluster_name}", pod=~"{workload_name}.*"}[10m]))`。
- Step 4B 查询不可调度 Pod 数量:用 MQL `fetch k8s_pod | metric 'kubernetes.io/pod/status/unschedulable' | filter cluster_name == '{cluster_name}' && pod_name ==~ '{workload_name}.*' | align next_older(10m) | every 10m | group_by [pod_name], [val: max(value)]`。
- Step 4C 查询 worker 容器日志:用 LQL `resource.type="k8s_container" AND resource.labels.cluster_name="{cluster_name}" AND labels."k8s-pod/jobset_sigs_k8s_io/jobset-name"="{workload_name}" AND timestamp >= "{start_time}" AND timestamp <= "{end_time}"`;先看 Pod 时间线定位 Pending/不可调度 Pod,再分析 slice 0 的 worker 0(协调器)日志中的 NCCL 超时、集合通信问题或 MegaScale 挂起;随后进入处置流程。
- 处置 1(低风险,抢占与自动扩缩优化):若 Step 2 显示 Spot VM 抢占频繁,建议把关键的长时训练负载切换到 GKE Reserved/On-Demand VM,或使用 Compact Placement Policies 以减少碎片化中断;理由是消除 Spot 市场抢占、减少训练重启。
- 处置 2(高风险,隔离故障宿主机 VM):若 Step 3 发现某个宿主机 ID(gce-topology-host)在多次尝试中持续故障或触发重启,建议 cordon/drain 该 GKE 节点、删除底层 GCE VM 实例以触发实例重建,并向 Google Cloud Support 提交工单并注明物理宿主机 ID;理由是 GKE 自动修复会在健康物理硬件上重建 VM,避免无限重启循环。
- 最后按检查清单逐项确认:采集上下文并计算 {start_time}({issue_time}-30m)与 {end_time}({issue_time}+30m);查询 JobSet 重启尝试;检查节点池中断(Spot 抢占 vs 硬件终止);查询节点到宿主机映射并检查节点日志中的物理宿主机错误;检查 Pod 时间线状态与协调器 worker 容器日志;给出调度策略建议(On-demand vs Spot)或宿主机 VM 隔离建议。
适用边界 · 注意事项
- 不适用于通用 GKE 集群创建、基础工作负载部署或非 JobSet 应用的问题。
- 需要集群已在 kube-state-metrics 中启用 JobSet 指标包,且项目已启用 Cloud Logging 与 Cloud Monitoring,否则相关查询不可用。