iT邦幫忙

2026 iThome 鐵人賽

DAY 25
0

上一篇把流程分為資料來源、查詢內容、固定規則與 AI 調查,最後再整理成報告。

今天會做一個 cli.py 作為巡檢程式的執行入口,讀取查詢設定、執行固定規則,再產生 result.json。

定義查詢內容

將所有巡檢項目放在 config/checks.yaml。以後要新增項目、調整順序或修改 query,都先改這個檔案。

檔案最上層分成 meta 和 layers:

meta:
  cluster: lab
  timezone: Asia/Taipei

layers:
  - id: data-trust
    title: Data Trustworthiness
    blocking: true
    checks:
      # 資料可信度的巡檢項目

  - id: control-plane
    title: Control Plane
    checks:
      # Control Plane 的巡檢項目

meta 記錄叢集名稱與時區。layers 用來將巡檢項目分組,例如資料可信度、Control Plane、Node、Workload、監控系統與 Kubernetes Event。

layers 的排列順序也是報告的章節順序。我把資料可信度放在最前面,因為 Prometheus 根本沒有收到完整資料時,後面的綠燈也沒有多少參考價值。blocking: true 會讓報告在這一層未通過時,直接顯示資料可信度警示。

check 的欄位設計

以 Node Ready 為例,取出主要欄位後長這樣:

- id: node-ready
  # type: promql(預設值,可以省略)
  title: 節點是否全程正常
  risk: 節點 NotReady 期間,上面的 Pod 會被驅逐或停止服務。
  advice: 確認該節點的 kubelet 狀態與網路連通性。
  unit: bool
  identity: [node]
  expr: 'min_over_time(kube_node_status_condition{condition="Ready",status="true"}[{{window}}])'
  fail_below: 1

這個 check 包含幾個重點:

  • id 是程式辨識這個項目的名稱,title 會顯示在報告上。
  • type 指定這個 check 的查詢執行方式。上面用註解標出 # type: promql,因為 promql 是預設值,實際設定可以省略。
  • expr 是實際執行的 query。Node 處於 Ready 時,這個 metric 的值是 1;Node 處於 NotReady 時,值是 0。min_over_time 會取出整個巡檢期間的最低值,所以全程 Ready 會得到 1,期間內只要曾經出現 NotReady 就會得到 0。{{window}} 會在執行時換成本次巡檢期間,例如 24h 或 72h。
  • identity 指定要用哪些 metric label 組成查詢對象的名稱,不會改變 PromQL。Node Ready 會回傳多台 Node 的結果,每筆資料都有 node label,因此設定為 identity: [node]。報告就能顯示 worker-01 = 1、worker-02 = 0,直接指出哪一台 Node 曾經 NotReady。Container 類型的資料則可以使用 [namespace, pod, container],避免不同 namespace 或 Container 的結果混在一起。
  • unit 說明數值的單位,讓後面整理結果時知道該顯示百分比、次數、秒數或 bool。
  • risk 和 advice 由人預先寫在 checks.yaml。同一個巡檢項目的風險與基本處理方向不會每天改變。把這些內容放在同一個 check 裡,之後修改 query 或門檻時,也能一起確認風險與建議是否需要調整。AI 後面只負責補充這次巡檢實際發生的狀況。
  • warn_above、fail_above、warn_below、fail_below 是判斷門檻。above 代表結果高於設定值時觸發,below 代表結果低於設定值時觸發,warn 和 fail 則決定狀態。例如:Node Ready 使用 fail_below: 1,只要查詢結果低於 1,這個項目就會判定為 FAIL。

這樣做之後,query、對象、單位、風險和建議都放在同一個 check 裡,清楚表示這個數字屬於誰、代表什麼。

不同 type 查詢的執行方式

大部分 check 可以直接執行 PromQL,有些資料要從 Loki 查詢,還有一些項目需要程式執行多次查詢後再合併結果。這些差異會透過 type 區分。沒有設定 type 時,程式預設執行 PromQL。

Kubernetes Event 儲存在 Loki,這類查詢會指定 type: logql:

- id: warning-events
  type: logql
  empty_means: pass
  expr: 'sum by (reason,namespace) (count_over_time({job="kube-events"} | logfmt | type="Warning" [{{window}}]))'
  warn_above: 0

查不到 Warning Event 時,empty_means: pass 會讓這個 check 判定為 PASS。只要查到一筆以上,warn_above: 0 就會將結果判定為 WARN。

這種查詢只會回傳產生問題的對象,沒有查到資料時才可以使用 empty_means: pass。CPU、Memory 這類平常就應該有數值的查詢,空結果仍然要判定為 UNKNOWN。

使用率類型的 check 會使用 type: usage。程式會分別查詢巡檢期間的最高值、產生報告時的目前值,以及超過警告門檻的時間。

- id: node-cpu-usage
  type: usage
  unit: ratio
  identity: [instance]
  expr: '1 - avg by (instance) (rate(node_cpu_seconds_total{mode="idle"}[5m]))'
  warn_above: 0.80
  fail_above: 0.95

報告產生時仍然超標,會依原本的門檻判定。巡檢期間曾經超標、後來已經恢復,會將當時最高值的判定降低一級。這樣可以在報告保留這次尖峰,也不會把已經恢復的問題列成當下仍在發生的 FAIL。

工作負載副本需要分別查詢 Deployment、DaemonSet 與 StatefulSet,再合併就緒數量與期望數量,因此使用專用的 workload_ready:

- id: workload-replicas
  type: workload_ready
  identity: [kind, namespace, workload]

程式讀到 type 後,就知道這個 check 要執行 PromQL、LogQL,或交給專用的查詢流程處理。

下一步說明查回來的 metrics 如何整理成可以判斷的結果。

判斷巡檢項目是否通過

每個 check 查詢完成後,程式會取出回傳結果,逐筆套用 checks.yaml 裡的門檻。同一個 check 有多筆結果時,再取其中最嚴重的狀態作為這個 check 的最後結果。

以 Container restart 為例,取出與判斷有關的設定:

- id: container-restarts
  identity: [namespace, pod, container]
  empty_means: pass
  expr: 'sum by (namespace,pod,container) (increase(kube_pod_container_status_restarts_total[{{window}}])) > 0'
  warn_above: 0
  fail_above: 5

這條 query 會依 namespace、pod、container 分組,每個 Container 的重啟次數都會成為一筆查詢結果。empty_means: pass 代表沒有查到任何結果時,就代表沒有 Container 在巡檢期間重啟,這個 check 可以判定為 PASS。

CPU、Memory 這類應該持續有數值的查詢,空結果代表資料不足,會判定為 UNKNOWN。warn_above: 0 代表只要曾經重啟就要注意,fail_above: 5 代表重啟超過五次就判定為 FAIL。

假設一個 Container 重啟一次,另一個 Container 重啟六次,程式會先將兩筆結果判定為 WARN 與 FAIL,再將整個 container-restarts 判定為 FAIL。查詢失敗或回傳的數值無法使用時,程式會將這個 check 標記為 UNKNOWN。

程式會依照 checks.yaml 的順序重複這個流程,直到所有 check 都完成,再把每個 check 的查詢與判斷結果組成這次巡檢的結果。

執行巡檢並產生 result.json

查詢內容與門檻都準備好後,就可以設定 Prometheus、Loki 的位置並執行巡檢:

PROM_URL=http://localhost:19090 \
LOKI_URL=http://localhost:13100 \
./.venv/bin/python cli.py report

所有 check 完成後,程式會將結果寫入:

reports/YYYY-MM-DD/result.json

result.json 會保留本次巡檢期間、每個 check 的狀態、實際執行的 query、查詢對象、判斷門檻,以及每一筆查詢結果(samples)。以 Container restart 為例,下面使用簡化的示意資料說明 result.json 的結構:

{
  "cluster": "lab",
  "window_start": "2026-08-19T00:00:00+00:00",
  "window_end": "2026-08-20T00:00:00+00:00",
  "data_trustworthy": true,
  "results": [
    {
      "id": "container-restarts",
      "layer": "workloads",
      "title": "容器重啟次數",
      "status": "FAIL",
      "summary": "2 項未通過",
      "identity": [
        "namespace",
        "pod",
        "container"
      ],
      "unit": "count",
      "thresholds": {
        "warn_above": 0,
        "fail_above": 5
      },
      "evidence": {
        "query": "sum by (namespace,pod,container) (increase(...)) > 0",
        "query_type": "promql",
        "samples": [
          {
            "labels": {
              "namespace": "lite-bank",
              "pod": "user-service-123",
              "container": "user-service"
            },
            "value": 1,
            "status": "WARN"
          },
          {
            "labels": {
              "namespace": "kube-system",
              "pod": "kube-vip-456",
              "container": "kube-vip"
            },
            "value": 6,
            "status": "FAIL"
          }
        ]
      },
      "investigation": null
    }
  ]
}

這些欄位分別代表:

  • cluster、window_start、window_end 記錄這份結果屬於哪個叢集,以及本次巡檢涵蓋的時間。
  • data_trustworthy 代表資料可信度相關的 check 是否全部通過。
  • results 保存所有 check 的結果,順序與 checks.yaml 相同。
  • status、summary 是固定規則產生的狀態與簡短說明。
  • identity 指出組成查詢對象名稱的 labels。
  • unit、thresholds 記錄數值單位與本次使用的判斷門檻。
  • evidence 保存實際執行的 query、query 類型與每一筆查詢結果(samples)。
  • labels、value、status 分別表示這筆結果屬於哪個對象、查到的數值,以及套用門檻後的狀態。
  • investigation 是保存 AI 調查結果的位置。

這個階段的 investigation 還是 null,因為目前只完成固定規則的判斷,AI 還沒有開始調查。接下來 AI 會先找出 WARN、FAIL 與 UNKNOWN,再使用工具補查其他資料,最後將結果寫回 investigation。

總結

到這裡,巡檢程式已經完成查詢、套用門檻,並將所有結果寫入 result.json。每個狀態都來自固定規則,使用相同資料重跑就會得到相同結果。

這時 investigation 還是 null。下一篇會繼續實作 AI 調查流程,說明 Skill 如何使用提供的調查工具、補查未通過的項目,再將調查結果寫回 result.json 並產生最後的巡檢報告。

今天就先寫到這,我們明天見!


上一篇
Day 24:設計 AI Kubernetes 每日巡檢架構
下一篇
Day 26:實作 AI 調查工具與 Skill 流程
系列文
讓 AI 接手工程師的 SOP:30 天 AI 自動化實戰 共 30 篇
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言