Workshop: Monitor Kubernetes Cluster ด้วย Prometheus + Grafana

การรัน 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 มี label grafana_dashboard=1
  • ServiceMonitor ไม่ถูก scrape: ตรวจ label release ของ ServiceMonitor และ selector serviceMonitorSelector ของ 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 ทุกครั้ง