การรัน Kubernetes cluster ใน production โดยไม่มี monitoring ที่เหมาะสมเปรียบเหมือนการขับรถโดยปิดตา เราไม่เห็น pod ที่กำลัง crash loop ไม่รู้ว่า node ไหน CPU แตะ 90% และไม่ทราบว่า HPA ทำงานถูกต้องหรือเปล่า ผลคือเมื่อเกิดปัญหาผู้ใช้งานจะเจอ error ก่อนทีม DevOps รับรู้ การติดตั้ง Prometheus กับ Grafana ที่ออกแบบสำหรับ K8s โดยเฉพาะจึงเป็นสิ่งที่ควรมีตั้งแต่วันแรกของ cluster
บทความ workshop นี้จะพาคุณติดตั้ง kube-prometheus-stack ด้วย Helm ซึ่งเป็นชุดสำเร็จรูปที่ Grafana Labs และ Prometheus Community รับรอง มาพร้อม dashboard และ alert ที่ปรับจูนสำหรับ K8s โดยตรง จบครบภายในไม่กี่คำสั่ง แล้วต่อยอดด้วยการเพิ่ม service monitoring ของแอปพลิเคชันเอง
ภาพรวม Stack ที่จะติดตั้ง
kube-prometheus-stack เป็น Helm chart ที่รวมทุกชิ้นส่วนที่จำเป็นสำหรับการ monitor cluster ไว้ด้วยกัน เมื่อติดตั้งครั้งเดียวจะได้หลายตัวพร้อม integration ทำงานประสานกัน
- Prometheus Operator: จัดการ Prometheus instance ผ่าน CRD เช่น ServiceMonitor, PodMonitor, PrometheusRule
- Prometheus Server: ตัวเก็บและ query metrics
- kube-state-metrics: export state ของ object ใน cluster เช่น Deployment, Pod, Node
- node-exporter: export hardware metrics ของ node ทุกเครื่อง
- Grafana: พร้อม dashboard สำเร็จรูปหลายสิบอันสำหรับ K8s
- Alertmanager: จัดการ alert และ routing ไปปลายทาง
Prerequisites
ก่อนเริ่ม ตรวจให้แน่ใจว่ามีสภาพแวดล้อมพร้อม ถ้ายังไม่มี cluster จริงก็ใช้ minikube, kind หรือ k3s ทดสอบได้ สิ่งที่ต้องเตรียม
- Kubernetes cluster 1.28+ (minikube, kind, k3s, EKS, GKE, AKS หรือ on-prem)
kubectlที่ config ชี้ไป cluster ได้ ตรวจด้วยkubectl get nodes- Helm 3.12+ — ติดตั้งตาม official docs
- RAM รวมใน cluster อย่างน้อย 4 GB, disk 20 GB สำหรับ Prometheus data
- พื้นฐาน Kubernetes object: Pod, Service, Deployment, Namespace
ขั้นตอนที่ 1: เพิ่ม Helm Repository
Helm chart อยู่ที่ repo prometheus-community ต้องเพิ่มเข้ามาก่อน จากนั้น update index ให้ Helm เห็น chart version ล่าสุด
helm repo add prometheus-community https://prometheus-community.github.io/helm-charts
helm repo update
helm search repo prometheus-community/kube-prometheus-stack
ขั้นตอนที่ 2: เตรียม values.yaml
chart นี้มี option เยอะมาก การ override ค่าบางส่วนผ่าน values.yaml ช่วยให้การ upgrade ในอนาคตง่ายกว่าการ override ผ่าน --set ทุกครั้ง ตัวอย่างไฟล์เริ่มต้นที่พอใช้งานจริง
prometheus:
prometheusSpec:
retention: 15d
storageSpec:
volumeClaimTemplate:
spec:
storageClassName: standard
accessModes: ["ReadWriteOnce"]
resources:
requests:
storage: 20Gi
resources:
requests:
cpu: 200m
memory: 512Mi
limits:
memory: 2Gi
grafana:
adminPassword: ChangeMeStrong!
persistence:
enabled: true
size: 5Gi
service:
type: ClusterIP
alertmanager:
alertmanagerSpec:
storage:
volumeClaimTemplate:
spec:
accessModes: ["ReadWriteOnce"]
resources:
requests:
storage: 5Gi
nodeExporter:
enabled: true
kubeStateMetrics:
enabled: true
ขั้นตอนที่ 3: Install Stack
สร้าง namespace แยกเพื่อให้จัดการง่าย จากนั้น install chart โดยชี้ไปที่ values.yaml ที่เตรียมไว้ ใช้เวลาประมาณ 2-3 นาทีกว่า pod จะ ready ครบ
kubectl create namespace monitoring
helm install kps prometheus-community/kube-prometheus-stack \
--namespace monitoring \
--values values.yaml \
--version 62.4.0
kubectl -n monitoring get pods -w
รอจน pod ทุกตัวอยู่ในสถานะ Running แล้ว READY เป็น 1/1 หรือ 2/2 ตามแต่ตัว ถ้ามี pod ค้างในสถานะ Pending นาน ตรวจ kubectl describe pod เพื่อดูสาเหตุ ส่วนใหญ่คือ storage class ไม่ถูก หรือ node ไม่มี resource พอ
ขั้นตอนที่ 4: เข้า Grafana UI
Service ของ Grafana ถูกตั้งเป็น ClusterIP ดังนั้นต้อง port-forward ออกมาใช้งานชั่วคราวใน dev environment หรือต่อ Ingress สำหรับ production
kubectl -n monitoring port-forward svc/kps-grafana 3000:80
เปิด browser ไปที่ http://localhost:3000 แล้ว login ด้วย username admin กับ password ที่ตั้งใน values.yaml เข้าเมนู Dashboards ระบบจะมี folder “Kubernetes” และ “Node Exporter” พร้อม dashboard นับสิบรายการให้เลือกใช้ทันที เช่น Cluster Compute Resources, Namespace Workloads, Node Exporter Full
ขั้นตอนที่ 5: Scrape Metrics ของแอปพลิเคชันเอง
ตัว chart จะ monitor component ของ K8s ให้แล้ว แต่แอปพลิเคชันของคุณต้องบอก Prometheus ด้วย ServiceMonitor CRD เพื่อให้ Prometheus Operator รู้ว่าต้อง scrape endpoint ไหน
สมมติคุณมี Deployment ชื่อ my-app ที่ expose metrics ที่ path /metrics port 8080 ให้สร้าง Service กับ ServiceMonitor ดังนี้
apiVersion: v1
kind: Service
metadata:
name: my-app-metrics
namespace: default
labels:
app: my-app
spec:
selector:
app: my-app
ports:
- name: metrics
port: 8080
targetPort: 8080
---
apiVersion: monitoring.coreos.com/v1
kind: ServiceMonitor
metadata:
name: my-app
namespace: monitoring
labels:
release: kps
spec:
namespaceSelector:
matchNames:
- default
selector:
matchLabels:
app: my-app
endpoints:
- port: metrics
interval: 30s
path: /metrics
label release: kps สำคัญมาก เพราะ Prometheus Operator ที่ติดมาจะ scan เฉพาะ ServiceMonitor ที่มี label นี้เท่านั้น ถ้าลืมจะไม่เกิดการ scrape ตรวจว่าได้ target แล้วด้วยการ port-forward ไปที่ Prometheus UI
kubectl -n monitoring port-forward svc/kps-kube-prometheus-stack-prometheus 9090
เปิด http://localhost:9090/targets มองหา target ที่ขึ้นต้นด้วย serviceMonitor/monitoring/my-app ถ้าสถานะเป็น UP แปลว่า scrape สำเร็จแล้ว
ขั้นตอนที่ 6: เขียน Alert Rules ด้วย PrometheusRule
การตั้ง alert ให้ใช้ CRD ชื่อ PrometheusRule แทนการแก้ไฟล์ config โดยตรง เพราะ Operator จะ watch object นี้แล้ว inject เข้า Prometheus ให้เอง ตัวอย่าง alert สำหรับ pod ที่ restart บ่อย
apiVersion: monitoring.coreos.com/v1
kind: PrometheusRule
metadata:
name: app-rules
namespace: monitoring
labels:
release: kps
spec:
groups:
- name: app.rules
rules:
- alert: PodRestartingTooOften
expr: rate(kube_pod_container_status_restarts_total[15m]) > 0
for: 10m
labels:
severity: warning
annotations:
summary: "Pod {{ $labels.pod }} restarting frequently"
description: "Pod has restarted more than once in the last 15 minutes."
- alert: HighMemoryUsage
expr: (container_memory_working_set_bytes / container_spec_memory_limit_bytes) > 0.9
for: 5m
labels:
severity: warning
annotations:
summary: "Container {{ $labels.container }} memory usage > 90%"
ขั้นตอนที่ 7: Best Practice สำหรับ Production
Setup ข้างต้นใช้งานได้จริงใน dev และ staging แต่สำหรับ production ที่มีหลาย node และ namespace ควรปรับเพิ่มเติมเพื่อความเสถียรและปลอดภัย
- ตั้ง retention ของ Prometheus อยู่ที่ 15-30 วัน และ remote write ไปยัง long-term storage เช่น Thanos, Cortex, Mimir
- ใช้ Ingress + TLS + OAuth2 Proxy ป้องกันหน้า Grafana จากคน outside
- แยก node pool สำหรับ monitoring workload ด้วย node selector เพื่อไม่ให้แย่ง resource แอปหลัก
- ตั้ง resource request/limit ทุก component ใน stack ให้เหมาะกับ cluster
- Backup volume ของ Prometheus และ Grafana ด้วย Velero หรือ snapshot ของ cloud provider
- Version pinning ใน Helm โดยใช้
--versionเสมอ จะได้ upgrade ควบคุมได้
Troubleshooting ที่พบบ่อย
- Pod ค้าง Pending: ตรวจ PVC pending — อาจไม่มี default storage class หรือ size ไม่พอ ใช้
kubectl -n monitoring get pvc - Grafana ไม่โหลด dashboard: ตรวจว่า sidecar
grafana-sc-dashboardทำงาน และ ConfigMap ของ dashboard มี labelgrafana_dashboard=1 - ServiceMonitor ไม่ถูก scrape: ตรวจ label
releaseของ ServiceMonitor และ selectorserviceMonitorSelectorของ Prometheus CR - High memory: Prometheus ใช้ memory เพิ่มเมื่อ cardinality ของ label สูง ลดได้ด้วยการ relabel หรือ drop metric ที่ไม่จำเป็น
- Alert ไม่ยิง: ดู Alertmanager UI ที่
/#/alertsและตรวจ PrometheusRule ว่าอยู่ใน namespace ที่ Prometheus watch หรือไม่
สรุป
การใช้ kube-prometheus-stack ทำให้ทีมมี foundation ด้าน monitoring ที่ครบถ้วนภายในไม่กี่คำสั่ง โดยไม่ต้องเขียน manifest ยาวเหยียดเอง มี dashboard พร้อมใช้งานและมี Operator จัดการ CRD ต่าง ๆ ช่วยลด maintenance ระยะยาวได้มาก สิ่งสำคัญคือต้องเข้าใจโครงสร้าง CRD (ServiceMonitor, PodMonitor, PrometheusRule) และ label selector ให้ชัด เพราะทุก integration ทำงานผ่านกลไกนี้
หลังจากพื้นฐานใช้งานได้ควรต่อยอดด้วย Loki สำหรับ log, Tempo สำหรับ trace, และต่อ Alertmanager ไป Slack หรือ PagerDuty เพื่อให้ทีม on-call ได้รับแจ้งทันที cluster ที่มี observability ครบจะแก้ปัญหาได้ไวและมีความเชื่อมั่นในการ deploy ทุกครั้ง

