iT邦幫忙

2026 iThome 鐵人賽

DAY 27
0

前兩天讓 build 和部署都自動化了,但新版本部署上去之後,跑得好不好?今天用 Helm 安裝 kube-prometheus-stack 這套監控系統,先學會用 PromQL 查詢 Todo App 的狀況,明天再用 Grafana 把這些資料畫成 Dashboard。


為什麼需要監控

服務跑在 K8s 上之後,要怎麼知道它的狀況?

  • CPU 和記憶體用了多少?
  • API 的回應時間是多少?
  • 每分鐘有多少請求進來?
  • 錯誤率是多少?

kubectl top pods 只能看到當下的資源使用量,沒有歷史趨勢,也看不到應用程式層面的指標。

監控系統解決這個問題:持續收集指標、儲存歷史資料、在 Dashboard 上視覺化呈現。


Prometheus 和 Grafana 的角色

Prometheus:指標收集和儲存的系統。它主動去各個服務的 /metrics 端點拉取指標(Pull-based),存成時序資料庫。

Grafana:視覺化工具。從 Prometheus 查詢資料,畫成圖表和 Dashboard。

K8s 元件 / 應用程式
  └── 暴露 /metrics 端點
           │
           │  Prometheus 定期拉取
           ▼
       Prometheus(儲存時序資料)   ← 今天
           │
           │  Grafana 查詢
           ▼
       Grafana Dashboard(視覺化)  ← 明天

kube-prometheus-stack 安裝時已經設定好 Prometheus 去收集 K8s 元件的指標(Pod 的 CPU、記憶體、重啟次數等),不需要額外設定。


實際操作

今天目標:用 Helm 安裝 kube-prometheus-stack,確認 Prometheus 收集了哪些資料,並用 PromQL 查詢 staging 的 CPU、記憶體和重啟次數。

Step1: 安裝 kube-prometheus-stack

# 1. 向本地 Helm 新增 Prometheus 社群官方維護的 Chart 儲存庫(別名取為 prometheus-community)
helm repo add prometheus-community https://prometheus-community.github.io/helm-charts

# 2. 更新本地儲存庫索引,確保拉取到最新的 Chart 版本資訊
helm repo update

# 3. 部署監控套件:
#    - Release 名稱:monitoring
#    - 安裝來源:prometheus-community/kube-prometheus-stack
#    - 安裝 Namespace:monitoring(若不存在則由 --create-namespace 自動建立)
helm install monitoring prometheus-community/kube-prometheus-stack -n monitoring --create-namespace

Release 名稱用 monitoring,後面的 Service 名稱(monitoring-grafana、monitoring-kube-prometheus-prometheus 等)都以它開頭。

PowerShell 寫成一行即可。第一次安裝要拉不少 image,可能要等幾分鐘。

確認 Pod 都 Running:

kubectl get pods -n monitoring

https://ithelp.ithome.com.tw/upload/images/20261001/20183863Th16oV6rkR.png

一個 helm install 就裝好了 Prometheus、Grafana、Alertmanager 等六個元件和一系列 CRD。如果用 kubectl apply 手動管理,要處理十幾份 YAML 和複雜的設定,這就是 Helm 在大型系統上的價值。

現在叢集同時跑 dev、staging、ArgoCD 和監控,比較吃記憶體。Pod 卡在 Pending 時,用 kubectl describe pod 看 Events,Insufficient memory/cpu 代表資源不夠。

Step2: 打開 Prometheus UI

轉發到本機 9090,終端機保持開著:

kubectl port-forward svc/monitoring-kube-prometheus-prometheus -n monitoring 9090:9090

打開 http://localhost:9090,就是 Prometheus 內建的查詢介面。

Step3: 看看 Prometheus 在收集什麼

上方選單 Status → Targets(新版介面叫 Target health),會列出 Prometheus 正在拉取的所有目標,每個目標都有一個 /metrics 端點:

  • kubelet / cAdvisor:每個 container 的 CPU、記憶體、網路使用量
  • kube-state-metrics:K8s 物件的狀態,例如 Pod 資訊、重啟次數、resources 設定
  • node-exporter:Node 本身的 CPU、記憶體、磁碟
  • apiserver、coredns 等 K8s 元件

https://ithelp.ithome.com.tw/upload/images/20261002/201838630bg9Vu4Tu6.png

狀態是 UP 代表拉取成功。之後如果查詢結果是空的,可以先來這裡確認目標有沒有正常運作。

今天用到的指標主要來自兩個地方:cAdvisor 負責「用了多少資源」,kube-state-metrics 負責「K8s 設定和狀態是什麼」。

Step4: 認識指標的類型

在查詢框輸入:

kube_pod_info{namespace="staging"}

https://ithelp.ithome.com.tw/upload/images/20261005/20183863zAcArgXNjR.png

結果的每一行代表一個 Pod。右上角的 Result series: 6 表示共有 6 筆結果,也就是 staging 裡有 6 個 Pod:3 個 frontend、2 個 api、1 個 mysql。

每一行分成兩部分:

  • 左邊:指標名稱 kube_pod_info,加上大括號裡一串 key="value"。這串叫做 label,用來描述這筆資料是誰,例如 pod="todo-staging-mysql-0" 是 Pod 名稱,node="minikube" 是它跑在哪台機器上。
  • 最右邊:紅框標示的數字,就是查到的值。

查詢時大括號裡寫的 namespace="staging",意思是「只留下 label 中 namespace 等於 staging 的資料」,這就是用 label 篩選。

你會發現每一行的值都是 1。這是因為 kube_pod_info 的用途是記錄 Pod 的基本資料,資訊都放在 label 裡,值本身沒有意義,固定是 1。

同樣是數字,看法不一樣

大部分指標的值是有意義的,但要怎麼看,取決於它屬於哪一種類型。最常見的有兩種,查詢寫法不一樣:

Gauge(量表):記錄「現在的狀態」

像體重計,數字會上下變動,看到的值本身就有意義。

例如 container_memory_working_set_bytes 的值是 524288000(約 500MB),就代表這個 container 現在用了 500MB 記憶體。這類指標直接查就好。

Counter(計數器):記錄「從開始到現在的累計總數」

像家裡的電錶,數字只會一直往上加,程式重啟時才歸零。

電錶顯示 12,345 度,你看不出家裡現在有沒有開冷氣,要看「過去一小時跑了幾度」才知道。Counter 也一樣,總數本身看不出現在的狀況,要看一段時間內增加了多少。

例如 kube_pod_container_status_restarts_total 查到 5,代表這個 container 總共重啟過 5 次。但這 5 次是上個月發生的,還是剛剛 10 分鐘內連續發生的?光看總數分不出來。

Counter 最常搭配這兩個函式,把累計值換算成變化量:

  • rate(指標[5m]):過去 5 分鐘內,平均每秒增加多少。適合看 CPU 這類「速度」。
  • increase(指標[1h]):過去 1 小時內,總共增加多少。適合看重啟次數這類「次數」。

怎麼分辨是哪一種

看名字最快:

指標名稱以 _total 結尾的通常是 counter,看到就要想到 rate 或 increase。

不確定的話,切到 Graph 分頁看線的形狀:一路往上爬的是 Counter,上下起伏的是 Gauge。

接下來就實際用這些觀念,查詢 staging 的 CPU、記憶體和重啟次數。

Step5: 一步步組出 CPU 查詢

直接看完整查詢會有點難懂,我們從原始指標開始,一層一層加上去。

開始之前,先確認 API 的 container 名稱,等一下會用它來篩選。另開一個終端機(port-forward 那個要保持開著)執行:

kubectl get deploy todo-staging-api -n staging -o jsonpath="{.spec.template.spec.containers[*].name}"

以下範例以 api 為例,名稱不同的話記得替換,否則查詢結果會是空的。

1. 原始指標

container_cpu_usage_seconds_total{namespace="staging"}

會出現很多條序列,除了 container="api",還有 container="" 這種沒有名稱的。那是 cAdvisor 額外提供的「整個 Pod 的總和」,如果不篩選掉,加總時會重複計算。

2. 篩選 container

container_cpu_usage_seconds_total{namespace="staging", container="api"}

這就只剩 API container 了。但它是 counter,最右邊的值是「啟動以來 CPU 總共工作了幾秒」,就像 Step4 說的電錶,只會一直變大,看不出現在忙不忙。

3. 用 rate 換算成每秒使用量

rate(container_cpu_usage_seconds_total{namespace="staging", container="api"}[5m])

加上 rate() 之後,值就從一直變大的累計秒數,變成「過去 5 分鐘平均每秒用了多少 CPU」。結果的單位是 core,0.1 代表 0.1 core,也就是 100m,可以直接和 Deployment 裡設定的 CPU requests 對照。[5m] 是計算的時間區間,區間越短越即時但越抖動,5 分鐘是常用的平衡點。

4. 依 Pod 分組

sum by (pod) (rate(container_cpu_usage_seconds_total{namespace="staging", container="api"}[5m]))

sum by (pod) 把同一個 Pod 的序列加總,結果每個 Pod 一條線,label 也只保留 pod,畫圖時乾淨很多。

點查詢框下方的 Graph 分頁,就能看到隨時間變化的曲線。

https://ithelp.ithome.com.tw/upload/images/20261005/20183863kZXNHkr3hz.png

Step6: 記憶體和重啟次數

同樣的思路,再看三個常用查詢:

# API 每個 Pod 的記憶體使用量(來自 cAdvisor,gauge,不需要 rate)
sum by (pod) (container_memory_working_set_bytes{namespace="staging", container="api"})

# API 每個 Pod 的 memory limit(來自 kube-state-metrics)
sum by (pod) (kube_pod_container_resource_limits{namespace="staging", container="api", resource="memory"})

# staging 每個 Pod 過去 1 小時的重啟次數(counter,用 increase)
sum by (pod) (increase(kube_pod_container_status_restarts_total{namespace="staging"}[1h]))

記憶體用 working_set_bytes 而不是 usage_bytes,後者包含可回收的快取,數字會偏高。K8s 判斷是否 OOM 也是看 working set。

kube_pod_container_resource_limits 同時記錄了 CPU 和記憶體的 limit,所以要加上 resource="memory" 指定只取記憶體。

網路上有些範例會用 cAdvisor 的 container_spec_memory_limit_bytes 查 limit,但在 kube-prometheus-stack 裡會查不到資料。因為它和 kube-state-metrics 的資料重複,kube-prometheus-stack 預設在收集時就把 container_spec 開頭的指標丟掉了。查不到資料時,除了檢查拼字和 label,也要想到「這個指標可能根本沒被收集」。

兩個查詢相除,就得到「記憶體使用率」:

sum by (pod) (container_memory_working_set_bytes{namespace="staging", container="api"})
/
sum by (pod) (kube_pod_container_resource_limits{namespace="staging", container="api", resource="memory"})

使用量來自 cAdvisor,limit 來自 kube-state-metrics,剛好對應 Step3 提到的兩個來源。

結果是 0 到 1 之間的數字,0.8 代表已經用了 limit 的 80%。這個值越接近 1,container 就越接近被 OOMKilled。

如果 container 沒有設定 memory limit,kube_pod_container_resource_limits 不會有這筆資料,相除結果會是空的。查不到東西時,先單獨查分母確認,再回頭檢查 Chart 的 resources 設定。

這些查詢是明天 Dashboard 和 Day 29 告警規則的基礎。

練習結束後,到 port-forward 的終端機按 Ctrl + C 停掉。


延伸筆記:常用 PromQL 速查

寫法 用途
指標{label="值"} 用 label 篩選
指標{label=~"todo-.*"} 用正規表示式篩選
rate(counter[5m]) counter 換算成每秒平均增加量
increase(counter[1h]) counter 在一段時間內的總增加量
sum by (pod) (...) 依指定 label 分組加總
A / B 兩組序列相除,label 相同的會配對
topk(3, ...) 取數值最大的前 3 條

小結

  • Prometheus 定期從 /metrics 端點拉取指標,存成時序資料
  • Targets 頁面可以確認 Prometheus 正在收集哪些來源
  • Gauge 直接看,Counter 要搭配 rate 或 increase
  • PromQL:用 namespace、container 篩選,sum by (pod) 依 Pod 分組
  • 記憶體使用量看 cAdvisor 的 working_set_bytes,limit 看 kube-state-metrics,相除就是離 OOM 還有多遠

明天會用 Grafana 把今天的查詢畫成 Todo App 專屬的 Dashboard。


上一篇
Day 26|GitOps(ArgoCD)
系列文
從零學 K8s|30 天核心概念 × 實作,新手也能真正掌握 Kubernetes 共 27 篇
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言