按命名空间和原因汇总非运行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 }
遇到的问题
- 代码仅输出
Pending这类宽泛状态,无法得到CrashLoopBackOff、InvalidImageName等具体失败原因 - 存在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
相关产品推荐
相关产品推荐

