You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

按命名空间和原因汇总非运行Pod:Kubectl状态来源及Go实现问题

问题:复刻Kubectl非运行状态Pod汇总功能的Go实现问题

原Shell脚本功能

要实现的功能是按命名空间和异常原因汇总非运行状态的Pod,原Shell脚本如下:

kubectl get po -A --no-headers |
awk '
  BEGIN {
    SUBSEP=" "
    format = "%-20s %20s  %5s\n"
    printf format, "NAMESPACE", "STATUS", "COUNT"
  }
  !/Running/ {a[$1,$4]++}
  END {
    for (i in a) {split(i,t); printf format, t[1],t[2],a[i]}
  }
' | sort

脚本输出示例

$ notrunning

NAMESPACE                                                  STATUS  COUNT
namespace-01                                     InvalidImageName      2
namespace-02                                     InvalidImageName      1
namespace-02                                Init:ImagePullBackOff      1
namespace-03                                     CrashLoopBackOff      2
namespace-03                                     InvalidImageName      9
namespace-04                                    Init:ErrImagePull      1

自行编写的Go代码

为实现该功能编写了如下Go代码(省略部分错误检查):

type PodSummary struct {
    NotRunning int
    Summary    map[PodKey]int // Map of namespace+state to count
}

type PodKey struct {
    Namespace string
    Status    string
}

func getPodSummary(kubeconfig, cluster string) PodSummary {
    clientset, _ := getClientsetForContext(kubeconfig, cluster)
    pods, _ := clientset.CoreV1().Pods("").List(context.TODO(), metav1.ListOptions{})
    summary := PodSummary{Summary: make(map[PodKey]int)}

    for _, pod := range pods.Items {
        if pod.Status.Phase != "Running" && pod.Status.Phase != "Succeeded" {  // need to check "Completed" also?
            podNS   := pod.Namespace
            summary.NotRunning++
            var pk PodKey
            for _,containerStatus := range pod.Status.ContainerStatuses {
                if containerStatus.State.Waiting != nil {
                    pk = PodKey {podNS, string(containerStatus.State.Waiting.Reason)}
                    break
                } else {  // cannot find it, use this instead.
                    pk = PodKey {podNS, string(pod.Status.Phase)}
                }
            }
            summary.Summary[pk]++
        }
    }
    return summary
}

遇到的问题

  1. 代码仅输出Pending这类宽泛状态,无法得到CrashLoopBackOff、InvalidImageName等具体失败原因
  2. 存在Pod的.status.phase为Running,但实际状态是CrashLoopBackOff的情况:
$ k get po
NAME                     READY   STATUS             RESTARTS           AGE
notreallyrunningpod      0/1     CrashLoopBackOff   5152 (85s ago)     10d

$ jq -r '.status.phase' < notreallyrunningpod.json 
Running

$ jq -r '.status.containerStatuses | .[] | .state.waiting.reason ' < notreallyrunningpod.json 
CrashLoopBackOff

疑问

  • 如何更准确地判断Pod状态?
  • 若Pod包含多个容器,应选择哪个容器的状态进行上报?

解答

核心逻辑:Kubectl状态的来源

Kubectl展示的STATUS字段并非直接取自Pod的.status.phase,而是综合了初始化容器状态、普通容器状态和Pod phase的复合结果:

  • 若初始化容器未完成,会显示Init:<原因>
  • 若普通容器出现异常(等待、异常终止),即使Pod phase为Running(比如容器反复重启),也会显示容器的异常原因
  • 只有当所有容器都正常时,才会显示Pod的phase(如Running、Succeeded)

1. 如何准确判断Pod状态

需要调整Go代码的判断逻辑,优先级如下:

步骤1:检查初始化容器(InitContainers)

初始化容器失败会直接阻塞Pod启动,需优先处理:

  • 遍历所有pod.Status.InitContainerStatuses
  • 找到第一个处于Waiting或Terminated(非成功退出)状态的容器,取其原因并添加Init:前缀

步骤2:检查普通容器(Containers)

若初始化容器全部正常,检查普通容器:

  • 遍历所有pod.Status.ContainerStatuses
  • 优先取处于Waiting状态的容器的Reason(如CrashLoopBackOff、InvalidImageName)
  • 若容器处于Terminated状态且退出码非0,取其Reason
  • 注意:即使Pod的.status.phase为Running,只要容器处于异常状态,就需要记录该异常原因

步骤3: fallback到Pod Phase

如果所有容器都正常,但Pod phase不是Running或Succeeded,则使用Pod的Phase作为状态(如Pending、Failed)


2. 多容器状态的选择规则

遵循Kubectl的逻辑即可:

  • 优先取初始化容器的异常状态:只要有一个初始化容器未成功,就用这个容器的状态
  • 普通容器取第一个异常状态:遍历普通容器,找到第一个处于Waiting或异常Terminated的容器,用它的状态;如果多个容器异常,通常第一个出现的异常就是Pod整体状态的来源
  • 所有容器正常时用Pod Phase

修改后的Go代码示例

import (
    "context"
    "k8s.io/apimachinery/pkg/apis/meta/v1"
    "k8s.io/client-go/kubernetes"
)

type PodSummary struct {
    NotRunning int
    Summary    map[PodKey]int // Map of namespace+state to count
}

type PodKey struct {
    Namespace string
    Status    string
}

func getPodSummary(kubeconfig, cluster string) PodSummary {
    clientset, _ := getClientsetForContext(kubeconfig, cluster)
    pods, _ := clientset.CoreV1().Pods("").List(context.TODO(), metav1.ListOptions{})
    summary := PodSummary{Summary: make(map[PodKey]int)}

    for _, pod := range pods.Items {
        podNS := pod.Namespace
        var status string
        isAbnormal := false

        // 1. 检查初始化容器状态
        for _, initStatus := range pod.Status.InitContainerStatuses {
            if initStatus.State.Waiting != nil {
                status = "Init:" + initStatus.State.Waiting.Reason
                isAbnormal = true
                break
            }
            if initStatus.State.Terminated != nil && initStatus.State.Terminated.ExitCode != 0 {
                status = "Init:" + initStatus.State.Terminated.Reason
                isAbnormal = true
                break
            }
        }

        // 2. 初始化容器正常,检查普通容器
        if !isAbnormal {
            for _, containerStatus := range pod.Status.ContainerStatuses {
                // 优先处理Waiting状态(比如CrashLoopBackOff)
                if containerStatus.State.Waiting != nil {
                    status = containerStatus.State.Waiting.Reason
                    isAbnormal = true
                    break
                }
                // 处理异常终止的容器
                if containerStatus.State.Terminated != nil && containerStatus.State.Terminated.ExitCode != 0 {
                    status = containerStatus.State.Terminated.Reason
                    isAbnormal = true
                    break
                }
            }
        }

        // 3. 容器都正常,但Pod phase异常
        if !isAbnormal {
            if pod.Status.Phase != v1.PodRunning && pod.Status.Phase != v1.PodSucceeded {
                status = string(pod.Status.Phase)
                isAbnormal = true
            }
        }

        // 统计异常Pod
        if isAbnormal {
            summary.NotRunning++
            pk := PodKey{Namespace: podNS, Status: status}
            summary.Summary[pk]++
        }
    }
    return summary
}

内容的提问来源于stack exchange,提问作者bill duncan

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.26 16:17:33