K8s 排障经常需要在 describe、logs、events 和 node 之间来回切换。这个项目将重复的信息收集与初步判断做成小工具,同时验证 AI 在运维诊断中适合承担哪些工作、不适合越过哪些边界。

READING NOTES

本文要点

  • 第一版只处理五类常见故障,不追求万能
  • 采集和规则判断必须确定,模型只负责整理和补充推理
  • 所有结论引用证据,工具默认没有集群写权限

01|先缩小第一版目标

如果目标是“输入任何问题都能诊断”,项目范围会迅速失控。第一版限定为 Pod Pending、CrashLoopBackOff、ImagePullBackOff、探针失败和 Node NotReady。输入 namespace 与 Pod 名称,输出 Markdown 报告。

这个范围虽然不大,但覆盖 Kubernetes API、异常处理、日志解析、规则匹配、模型调用和报告生成,足以形成一个完整的 AI Ops 最小可用项目。

02|项目目录怎么拆

项目没有一开始就引入复杂框架。先让每个模块只做一件事,命令行跑通后,再考虑 Web 页面。当前目录结构如下:

k8s-doctor/
├── app.py                 # 命令行入口
├── collectors/
│   ├── pod.py             # Pod 状态与容器退出原因
│   ├── events.py          # Event 时间线
│   ├── logs.py            # 当前与上一次容器日志
│   └── node.py            # 节点条件、污点与资源
├── rules/
│   ├── scheduler.py       # Pending / 调度失败
│   ├── image.py           # 镜像拉取失败
│   └── restart.py         # 重启与退出码
├── llm/
│   └── analyzer.py        # 组织上下文并调用模型
├── reports/
│   └── markdown.py        # 生成报告
└── tests/
    └── fixtures/          # 脱敏后的故障样本

03|先把证据采对

采集层使用 Kubernetes Python Client,不直接解析 kubectl 的终端文本。API 返回对象更适合后续结构化处理。下面是一个精简后的 Pod 采集函数:

from kubernetes import client, config

def collect_pod(namespace: str, name: str) -> dict:
    config.load_kube_config()
    core = client.CoreV1Api()
    pod = core.read_namespaced_pod(name=name, namespace=namespace)

    containers = []
    for status in pod.status.container_statuses or []:
        waiting = status.state.waiting
        terminated = status.last_state.terminated
        containers.append({
            "name": status.name,
            "ready": status.ready,
            "restart_count": status.restart_count,
            "waiting_reason": waiting.reason if waiting else None,
            "last_exit_code": terminated.exit_code if terminated else None,
        })

    return {
        "namespace": namespace,
        "name": name,
        "phase": pod.status.phase,
        "node": pod.spec.node_name,
        "conditions": [
            {"type": item.type, "status": item.status, "reason": item.reason}
            for item in pod.status.conditions or []
        ],
        "containers": containers,
    }
FIELD NOTE这里最容易踩的坑是字段可能为空。Event、container_statuses、last_state 都不能假设一定存在,否则工具诊断故障 Pod 时自己先报错。

04|事件要按时间线整理

K8s Event 会重复聚合,同一个原因可能只有一条记录但 count 很高。应保留 reason、message、count、firstTimestamp 和 lastTimestamp,并按最后发生时间排序。模型不需要全部日志,只需要与故障窗口有关的证据。

def normalize_events(items) -> list[dict]:
    events = []
    for item in items:
        events.append({
            "reason": item.reason,
            "message": item.message,
            "count": item.count or 1,
            "first_seen": str(item.first_timestamp or ""),
            "last_seen": str(item.last_timestamp or item.event_time or ""),
        })
    return sorted(events, key=lambda x: x["last_seen"])

05|规则层先处理确定的问题

镜像不存在、PVC 未绑定、节点资源不足,这些信息在 Event 中已经很明确,没有必要每次都调用模型。每条规则设置 ID、严重度、命中证据和建议,后续报告可以直接引用。

def check_pending(evidence: dict) -> list[dict]:
    findings = []
    text = "\n".join(e["message"] for e in evidence["events"])

    rules = [
        ("P001", "Insufficient cpu", "节点可分配 CPU 不足"),
        ("P002", "Insufficient memory", "节点可分配内存不足"),
        ("P003", "unbound immediate PersistentVolumeClaims", "PVC 尚未绑定"),
        ("P004", "had taint", "Pod 未容忍节点污点"),
    ]

    for rule_id, keyword, conclusion in rules:
        if keyword.lower() in text.lower():
            findings.append({
                "rule_id": rule_id,
                "conclusion": conclusion,
                "evidence": keyword,
                "risk": "medium",
            })
    return findings

06|什么时候才调用模型

规则运行后,将 Pod 摘要、最近事件、关键日志、节点状态和命中的规则组合成证据包。模型必须按固定 JSON 结构返回结论、证据 ID、建议、风险和验证方法。证据不足时,应返回需要补充什么,不能编造命令。

SYSTEM_PROMPT = """
你是 Kubernetes 运维分析助手。
只能根据 evidence 回答,不要补造集群事实。
每条结论必须引用 evidence_id。
不要执行变更,只给检查或候选修复步骤。
证据不足时,把 status 设为 need_more_evidence。
"""

payload = {
    "symptom": "Pod 一直处于 Pending",
    "evidence": evidence_items,
    "rule_findings": findings,
    "output_schema": {
        "status": "diagnosed | need_more_evidence",
        "conclusion": "string",
        "evidence_ids": ["E01"],
        "steps": ["string"],
        "risk": "low | medium | high",
        "verify": ["string"],
    },
}

07|一份报告长什么样

报告首页只放现象、最可能原因、证据和下一步。原始 JSON 与完整日志放附件。命令默认只读;涉及 rollout restart、修改资源、删除 Pod 或调整节点时,只生成候选动作并明确需要人工审批。

  • 现象:default/api-7d9xx Pending 12 分钟。
  • 结论:没有节点满足内存请求,置信度高。
  • 证据:E03 调度事件包含 Insufficient memory,重复 18 次。
  • 建议:核对 requests 是否合理,再检查节点可分配内存与扩容计划。
  • 验证:Pod 成功调度且 Ready,相关 Event 不再新增。

08|目前还没解决的问题

这个工具目前适合做故障信息收集和第一轮判断,还不能替代有经验的 K8s 工程师。跨 namespace 的调用链、CNI/CNI 插件问题、存储后端问题,需要接 Prometheus、日志平台和更多组件信息。

下一步优先完善测试样本。每修复一个真实问题,就把脱敏后的证据包和正确结论放入 fixtures,避免以后修改规则时破坏已经能够诊断的问题。

FIELD NOTE这个项目最有价值的部分,不是做出一个“AI 页面”,而是将依赖经验的排障顺序转化为可检查的数据结构和代码。

SOURCES / 资料核对

相关官方资料

产品功能会随版本和授权变化。下面列出撰写时核对的资料, 实际交付仍以项目版本的兼容矩阵和正式文档为准。