Prometheus + Grafana — คู่มือ Monitoring Stack ฉบับสมบูรณ์สำหรับ Production 2026
"ถ้าคุณ monitor ไม่ได้ คุณก็ manage ไม่ได้" — นี่คือหลักการที่ผมยึดถือมาตลอด 30 ปีในวงการ IT ผมเคยเห็นระบบล่มตอนตี 3 แล้วไม่มีใครรู้จนกว่าลูกค้าจะโทรมาด่าตอนเช้า เคยเห็น server ที่ disk เต็ม 100% แต่ไม่มี alert เคยเห็น memory leak ที่ค่อยๆ กิน RAM จนระบบ crash ทุกสัปดาห์ ทุกปัญหาเหล่านี้แก้ได้ด้วย Prometheus + Grafana — monitoring stack ที่ดีที่สุดในปี 2026
Prometheus เป็น time-series database ที่ออกแบบมาเพื่อ monitoring โดยเฉพาะ เกิดจาก SoundCloud ในปี 2012 ปัจจุบันเป็น CNCF graduated project เช่นเดียวกับ Kubernetes ส่วน Grafana เป็น visualization platform ที่แสดงข้อมูลจาก Prometheus เป็น dashboard สวยงามและ actionable
สิ่งที่จะได้เรียนรู้:
- Prometheus Architecture และหลักการทำงาน
- ติดตั้ง Prometheus + Grafana แบบ step-by-step
- PromQL — ภาษา query ที่ต้องเชี่ยวชาญ
- Exporters สำหรับทุก service
- Alertmanager — ตั้ง alert ที่ไม่ spam
- Grafana Dashboard ที่ใช้จริงใน production
- High Availability Setup
- Long-term Storage ด้วย Thanos/Mimir
Prometheus Architecture
Prometheus ทำงานแบบ pull model — มัน scrape (ดึง) metrics จาก targets เป็นระยะ (default ทุก 15 วินาที) แทนที่จะให้ targets push metrics เข้ามา ข้อดีของ pull model คือ:
- Prometheus ควบคุม scrape interval ได้เอง
- ตรวจจับ target ที่ล่มได้ทันที (scrape failed)
- ไม่ต้อง configure targets ให้รู้จัก Prometheus
- ง่ายต่อการ debug — เปิด browser ไปที่ /metrics ของ target ได้เลย
Components หลัก
# Prometheus Architecture
#
# ┌─────────────┐ scrape ┌──────────────┐
# │ Prometheus │ ──────────────→ │ Targets │
# │ Server │ │ (exporters) │
# │ │ └──────────────┘
# │ ┌────────┐ │
# │ │ TSDB │ │ query ┌──────────────┐
# │ │(storage)│ │ ←──────────── │ Grafana │
# │ └────────┘ │ └──────────────┘
# │ │
# │ ┌────────┐ │ alert ┌──────────────┐
# │ │ Rules │ │ ──────────────→ │ Alertmanager │
# │ └────────┘ │ │ → Slack │
# └─────────────┘ │ → PagerDuty │
# │ → Email │
# └──────────────┘
ติดตั้ง Prometheus
Step 1: สร้าง User และ Directory
# สร้าง prometheus user (no login)
sudo useradd -r -s /sbin/nologin prometheus
# สร้าง directories
sudo mkdir -p /etc/prometheus /var/lib/prometheus
sudo chown prometheus:prometheus /var/lib/prometheus
Step 2: ดาวน์โหลดและติดตั้ง
# ดาวน์โหลด Prometheus ล่าสุด
PROM_VERSION="2.50.1"
wget https://github.com/prometheus/prometheus/releases/download/v${PROM_VERSION}/prometheus-${PROM_VERSION}.linux-amd64.tar.gz
tar xzf prometheus-${PROM_VERSION}.linux-amd64.tar.gz
cd prometheus-${PROM_VERSION}.linux-amd64
# Copy binaries
sudo cp prometheus promtool /usr/local/bin/
sudo cp -r consoles console_libraries /etc/prometheus/
# ตรวจสอบ
prometheus --version
# prometheus, version 2.50.1
Step 3: Configuration
# /etc/prometheus/prometheus.yml
global:
scrape_interval: 15s
evaluation_interval: 15s
scrape_timeout: 10s
# External labels สำหรับ federation/remote write
external_labels:
cluster: production
region: ap-southeast-1
# Alert rules
rule_files:
- "/etc/prometheus/rules/*.yml"
# Alertmanager
alerting:
alertmanagers:
- static_configs:
- targets:
- 'localhost:9093'
# Scrape configs
scrape_configs:
# Prometheus ตัวเอง
- job_name: 'prometheus'
static_configs:
- targets: ['localhost:9090']
# Node Exporter (Linux metrics)
- job_name: 'node-exporter'
static_configs:
- targets:
- '10.0.1.10:9100'
- '10.0.1.11:9100'
- '10.0.1.12:9100'
- '10.0.2.10:9100'
- '10.0.2.11:9100'
relabel_configs:
- source_labels: [__address__]
regex: '(.*):\d+'
target_label: instance
replacement: '${1}'
# Nginx
- job_name: 'nginx'
static_configs:
- targets: ['10.0.1.10:9113']
# Redis
- job_name: 'redis'
static_configs:
- targets: ['10.0.1.20:9121']
# PostgreSQL
- job_name: 'postgresql'
static_configs:
- targets: ['10.0.1.30:9187']
# Application (custom metrics)
- job_name: 'app'
metrics_path: /metrics
static_configs:
- targets:
- '10.0.1.10:3000'
- '10.0.1.11:3000'
- '10.0.1.12:3000'
# Kubernetes Service Discovery (ถ้าใช้ K8s)
- job_name: 'kubernetes-pods'
kubernetes_sd_configs:
- role: pod
relabel_configs:
- source_labels: [__meta_kubernetes_pod_annotation_prometheus_io_scrape]
action: keep
regex: true
- source_labels: [__meta_kubernetes_pod_annotation_prometheus_io_path]
action: replace
target_label: __metrics_path__
regex: (.+)
Step 4: Systemd Service
# /etc/systemd/system/prometheus.service
[Unit]
Description=Prometheus Monitoring
Wants=network-online.target
After=network-online.target
[Service]
User=prometheus
Group=prometheus
Type=simple
ExecStart=/usr/local/bin/prometheus \
--config.file=/etc/prometheus/prometheus.yml \
--storage.tsdb.path=/var/lib/prometheus \
--storage.tsdb.retention.time=30d \
--storage.tsdb.retention.size=50GB \
--web.console.templates=/etc/prometheus/consoles \
--web.console.libraries=/etc/prometheus/console_libraries \
--web.enable-lifecycle \
--web.enable-admin-api
ExecReload=/bin/kill -HUP $MAINPID
Restart=always
RestartSec=5
[Install]
WantedBy=multi-user.target
sudo systemctl daemon-reload
sudo systemctl enable --now prometheus
# ตรวจสอบ
curl http://localhost:9090/-/healthy
# Prometheus Server is Healthy.
ผู้เชี่ยวชาญแนะนำ - siamlancard
แนะนำ: | |
🎬 วิดีโอที่เกี่ยวข้อง — YouTube @icafefx
Node Exporter — Monitor Linux Servers
# ติดตั้ง Node Exporter บนทุก server
NODE_VERSION="1.7.0"
wget https://github.com/prometheus/node_exporter/releases/download/v${NODE_VERSION}/node_exporter-${NODE_VERSION}.linux-amd64.tar.gz
tar xzf node_exporter-${NODE_VERSION}.linux-amd64.tar.gz
sudo cp node_exporter-${NODE_VERSION}.linux-amd64/node_exporter /usr/local/bin/
# Systemd service
cat << 'EOF' | sudo tee /etc/systemd/system/node-exporter.service
[Unit]
Description=Node Exporter
After=network.target
[Service]
User=prometheus
ExecStart=/usr/local/bin/node_exporter \
--collector.systemd \
--collector.processes \
--collector.tcpstat \
--no-collector.infiniband \
--no-collector.nfs \
--no-collector.nfsd
Restart=always
[Install]
WantedBy=multi-user.target
EOF
sudo systemctl enable --now node-exporter
# ทดสอบ
curl http://localhost:9100/metrics | head -20
PromQL — ภาษา Query ที่ต้องเชี่ยวชาญ
PromQL เป็นภาษา query ของ Prometheus ที่ทรงพลังมาก ถ้าเชี่ยวชาญ PromQL คุณจะสร้าง dashboard และ alert ได้ทุกอย่าง
พื้นฐาน
# Instant vector — ค่าปัจจุบัน
node_cpu_seconds_total
# Range vector — ค่าในช่วงเวลา
node_cpu_seconds_total[5m]
# Filter ด้วย labels
node_cpu_seconds_total{mode="idle", instance="10.0.1.10"}
# Regex match
node_cpu_seconds_total{mode=~"idle|iowait"}
# Negative match
node_cpu_seconds_total{mode!="idle"}
Functions ที่ใช้บ่อยที่สุด
# rate() — คำนวณ per-second rate จาก counter
# CPU utilization (%)
100 - (avg by(instance) (rate(node_cpu_seconds_total{mode="idle"}[5m])) * 100)
# Memory utilization (%)
(1 - (node_memory_MemAvailable_bytes / node_memory_MemTotal_bytes)) * 100
# Disk utilization (%)
(1 - (node_filesystem_avail_bytes{mountpoint="/"} / node_filesystem_size_bytes{mountpoint="/"})) * 100
# Network traffic (bytes/sec)
rate(node_network_receive_bytes_total{device="eth0"}[5m])
rate(node_network_transmit_bytes_total{device="eth0"}[5m])
# HTTP request rate
rate(http_requests_total[5m])
# HTTP error rate (%)
rate(http_requests_total{status=~"5.."}[5m]) / rate(http_requests_total[5m]) * 100
# P99 latency
histogram_quantile(0.99, rate(http_request_duration_seconds_bucket[5m]))
# P95 latency
histogram_quantile(0.95, rate(http_request_duration_seconds_bucket[5m]))
# increase() — total increase ในช่วงเวลา
increase(http_requests_total[1h]) # จำนวน requests ใน 1 ชั่วโมงที่ผ่านมา
# predict_linear() — ทำนายอนาคต
# ทำนายว่า disk จะเต็มเมื่อไหร่
predict_linear(node_filesystem_avail_bytes{mountpoint="/"}[6h], 24*3600) < 0
# ถ้า < 0 = disk จะเต็มภายใน 24 ชั่วโมง!
Aggregation
# sum — รวมทุก instances
sum(rate(http_requests_total[5m]))
# avg — เฉลี่ย
avg(node_load1)
# max/min
max(node_memory_MemTotal_bytes)
# count — นับจำนวน time series
count(up == 1) # จำนวน targets ที่ online
# topk — top N
topk(5, rate(http_requests_total[5m])) # 5 endpoints ที่มี traffic สูงสุด
# Group by
sum by(status_code) (rate(http_requests_total[5m]))
sum by(instance) (rate(node_cpu_seconds_total{mode!="idle"}[5m]))
(อ้างอิง: grafana tempo traces production setup guide)
ติดตั้ง Grafana
# Ubuntu
sudo apt install -y apt-transport-https software-properties-common
wget -q -O - https://apt.grafana.com/gpg.key | sudo gpg --dearmor -o /usr/share/keyrings/grafana.gpg
echo "deb [signed-by=/usr/share/keyrings/grafana.gpg] https://apt.grafana.com stable main" | sudo tee /etc/apt/sources.list.d/grafana.list
sudo apt update
sudo apt install -y grafana
# Start
sudo systemctl enable --now grafana-server
# เข้า http://your-server:3000
# Default login: admin / admin (เปลี่ยนทันที!)
เพิ่ม Prometheus Data Source
# Grafana → Configuration → Data Sources → Add data source
# Type: Prometheus
# URL: http://localhost:9090
# Access: Server (default)
# Save & Test
Import Dashboard ที่ดีที่สุด
ไม่ต้องสร้าง dashboard เอง — ใช้ community dashboards:
- Node Exporter Full (ID: 1860) — dashboard สำหรับ Linux servers ที่ดีที่สุด
- Nginx (ID: 12708) — Nginx metrics
- Redis (ID: 11835) — Redis metrics
- PostgreSQL (ID: 9628) — PostgreSQL metrics
- Docker (ID: 893) — Docker container metrics
- Kubernetes (ID: 315) — K8s cluster overview
# Import: Grafana → Dashboards → Import → ใส่ ID → Load → Select data source → Import
Alertmanager — ตั้ง Alert ที่ไม่ Spam
ติดตั้ง Alertmanager
ALERT_VERSION="0.27.0"
wget https://github.com/prometheus/alertmanager/releases/download/v${ALERT_VERSION}/alertmanager-${ALERT_VERSION}.linux-amd64.tar.gz
tar xzf alertmanager-${ALERT_VERSION}.linux-amd64.tar.gz
sudo cp alertmanager-${ALERT_VERSION}.linux-amd64/alertmanager /usr/local/bin/
sudo mkdir -p /etc/alertmanager
Alertmanager Configuration
# /etc/alertmanager/alertmanager.yml
global:
resolve_timeout: 5m
slack_api_url: 'https://hooks.slack.com/services/YOUR/SLACK/WEBHOOK'
route:
receiver: 'slack-default'
group_by: ['alertname', 'cluster', 'service']
group_wait: 30s
group_interval: 5m
repeat_interval: 4h
routes:
# Critical alerts → PagerDuty + Slack
- match:
severity: critical
receiver: 'pagerduty-critical'
repeat_interval: 1h
# Warning alerts → Slack only
- match:
severity: warning
receiver: 'slack-warning'
repeat_interval: 4h
receivers:
- name: 'slack-default'
slack_configs:
- channel: '#alerts'
title: '{{ .GroupLabels.alertname }}'
text: >-
{{ range .Alerts }}
*Alert:* {{ .Annotations.summary }}
*Instance:* {{ .Labels.instance }}
*Severity:* {{ .Labels.severity }}
{{ end }}
- name: 'slack-warning'
slack_configs:
- channel: '#alerts-warning'
color: '#FFA500'
title: '⚠️ {{ .GroupLabels.alertname }}'
text: '{{ range .Alerts }}{{ .Annotations.summary }}{{ end }}'
- name: 'pagerduty-critical'
pagerduty_configs:
- service_key: 'YOUR_PAGERDUTY_SERVICE_KEY'
severity: critical
# Inhibition rules — ป้องกัน alert ซ้ำซ้อน
inhibit_rules:
- source_match:
severity: 'critical'
target_match:
severity: 'warning'
equal: ['alertname', 'instance']
Alert Rules ที่ต้องมี
# /etc/prometheus/rules/infrastructure.yml
groups:
- name: infrastructure
rules:
# === CPU ===
- alert: HighCPUUsage
expr: 100 - (avg by(instance) (rate(node_cpu_seconds_total{mode="idle"}[5m])) * 100) > 80
for: 10m
labels:
severity: warning
annotations:
summary: "CPU usage > 80% on {{ $labels.instance }}"
description: "CPU usage is {{ $value | printf \"%.1f\" }}%"
- alert: CriticalCPUUsage
expr: 100 - (avg by(instance) (rate(node_cpu_seconds_total{mode="idle"}[5m])) * 100) > 95
for: 5m
labels:
severity: critical
annotations:
summary: "CPU usage > 95% on {{ $labels.instance }}"
# === Memory ===
- alert: HighMemoryUsage
expr: (1 - (node_memory_MemAvailable_bytes / node_memory_MemTotal_bytes)) * 100 > 85
for: 10m
labels:
severity: warning
annotations:
summary: "Memory usage > 85% on {{ $labels.instance }}"
# === Disk ===
- alert: DiskSpaceLow
expr: (1 - (node_filesystem_avail_bytes{mountpoint="/"} / node_filesystem_size_bytes{mountpoint="/"})) * 100 > 80
for: 5m
labels:
severity: warning
annotations:
summary: "Disk usage > 80% on {{ $labels.instance }}"
- alert: DiskWillFillIn24Hours
expr: predict_linear(node_filesystem_avail_bytes{mountpoint="/"}[6h], 24*3600) < 0
for: 30m
labels:
severity: critical
annotations:
summary: "Disk will be full within 24 hours on {{ $labels.instance }}"
# === Target Down ===
- alert: TargetDown
expr: up == 0
for: 2m
labels:
severity: critical
annotations:
summary: "{{ $labels.job }}/{{ $labels.instance }} is DOWN"
# === Application ===
- alert: HighErrorRate
expr: rate(http_requests_total{status=~"5.."}[5m]) / rate(http_requests_total[5m]) * 100 > 5
for: 5m
labels:
severity: critical
annotations:
summary: "Error rate > 5% on {{ $labels.instance }}"
- alert: HighLatency
expr: histogram_quantile(0.99, rate(http_request_duration_seconds_bucket[5m])) > 1
for: 5m
labels:
severity: warning
annotations:
summary: "P99 latency > 1s on {{ $labels.instance }}"
หลักการตั้ง Alert ที่ดี:
- ใช้
forclause เสมอ — ป้องกัน false positive จาก spike ชั่วคราว - แยก severity — warning ส่ง Slack, critical ส่ง PagerDuty
- ใช้
predict_linear— alert ก่อนที่ปัญหาจะเกิด ไม่ใช่หลังจากเกิดแล้ว - ใช้ inhibition rules — ถ้า critical alert ยิงแล้ว ไม่ต้องยิง warning ซ้ำ
- ตั้ง
repeat_intervalให้เหมาะสม — ไม่ spam ทุก 5 นาที
📌 บทความแนะนำจาก siamlancard: | |
(อ้างอิง: uptime kuma monitoring production setup guide)
Application Metrics — Instrument Your Code
Python (FastAPI + prometheus_client)
# pip install prometheus-client prometheus-fastapi-instrumentator
from fastapi import FastAPI
from prometheus_fastapi_instrumentator import Instrumentator
from prometheus_client import Counter, Histogram, Gauge
app = FastAPI()
# Auto-instrument all endpoints
Instrumentator().instrument(app).expose(app)
# Custom metrics
orders_total = Counter(
'orders_total',
'Total orders processed',
['status', 'payment_method']
)
order_amount = Histogram(
'order_amount_thb',
'Order amount in THB',
buckets=[100, 500, 1000, 5000, 10000, 50000]
)
active_users = Gauge(
'active_users',
'Currently active users'
)
@app.post("/api/orders")
async def create_order(order: OrderCreate):
result = await process_order(order)
orders_total.labels(status=result.status, payment_method=order.payment).inc()
order_amount.observe(order.amount)
return result
Node.js (Express + prom-client)
const express = require('express');
const client = require('prom-client');
const app = express();
// Default metrics (CPU, memory, event loop, etc.)
client.collectDefaultMetrics({ timeout: 5000 });
// Custom metrics
const httpRequestDuration = new client.Histogram({
name: 'http_request_duration_seconds',
help: 'Duration of HTTP requests in seconds',
labelNames: ['method', 'route', 'status_code'],
buckets: [0.01, 0.05, 0.1, 0.5, 1, 5]
});
// Middleware
app.use((req, res, next) => {
const end = httpRequestDuration.startTimer();
res.on('finish', () => {
end({ method: req.method, route: req.route?.path || req.path, status_code: res.statusCode });
});
next();
});
// Metrics endpoint
app.get('/metrics', async (req, res) => {
res.set('Content-Type', client.register.contentType);
res.end(await client.register.metrics());
});
(อ้างอิง: llm fine tuning lora remote work setup)
High Availability Setup
Prometheus HA
วิธีที่ง่ายที่สุดคือรัน 2 Prometheus instances ที่มี config เหมือนกัน scrape targets เดียวกัน ทั้งสองจะมีข้อมูลเหมือนกัน (เกือบ) ถ้าตัวหนึ่งล่ม อีกตัวยังทำงานได้
# Prometheus 1: --web.external-url=http://prom1:9090
# Prometheus 2: --web.external-url=http://prom2:9090
# ใช้ config เดียวกัน
# Grafana ชี้ไปทั้งสอง (ใช้ load balancer หน้า Prometheus)
upstream prometheus {
server prom1:9090;
server prom2:9090 backup;
}
Long-term Storage ด้วย Thanos
# Thanos Sidecar — แนบกับ Prometheus
thanos sidecar \
--tsdb.path=/var/lib/prometheus \
--prometheus.url=http://localhost:9090 \
--objstore.config-file=/etc/thanos/bucket.yml
# bucket.yml — เก็บข้อมูลใน S3
type: S3
config:
bucket: my-thanos-metrics
endpoint: s3.ap-southeast-1.amazonaws.com
region: ap-southeast-1
Thanos ช่วยให้เก็บ metrics ได้นานหลายปีใน object storage (S3, GCS) ด้วยค่าใช้จ่ายต่ำมาก และ query ข้าม Prometheus instances ได้
Troubleshooting
ปัญหา 1: Prometheus กิน RAM มาก
# ตรวจสอบจำนวน time series
curl http://localhost:9090/api/v1/status/tsdb | jq '.data.headStats'
# ถ้ามากเกินไป ลด cardinality
# - ลด labels ที่ไม่จำเป็น
# - ใช้ relabel_configs drop metrics ที่ไม่ใช้
# - ลด scrape_interval
ปัญหา 2: Scrape timeout
# เพิ่ม scrape_timeout
scrape_configs:
- job_name: 'slow-target'
scrape_timeout: 30s
static_configs:
- targets: ['slow-app:9090']
ปัญหา 3: Alert ไม่ส่ง
# ตรวจสอบ Alertmanager
curl http://localhost:9093/api/v2/alerts | jq .
# ตรวจสอบ Prometheus alert rules
curl http://localhost:9090/api/v1/rules | jq '.data.groups[].rules[] | select(.state=="firing")'
คำถามที่พบบ่อย (FAQ)
Q: Prometheus เก็บข้อมูลได้นานแค่ไหน?
A: Default 15 วัน ปรับได้ด้วย --storage.tsdb.retention.time แนะนำ 30-90 วัน สำหรับ long-term ใช้ Thanos หรือ Mimir
Q: Prometheus กับ Datadog เลือกอะไรดี?
A: Prometheus ฟรี ยืดหยุ่นสูง แต่ต้อง manage เอง Datadog ง่ายกว่าแต่แพง ($15-23/host/month) ถ้าทีมเล็กและ budget จำกัด ใช้ Prometheus
Q: ใช้กับ Kubernetes ยังไง?
A: ใช้ kube-prometheus-stack Helm chart ติดตั้งทุกอย่างในคำสั่งเดียว:
helm install kube-prom prometheus-community/kube-prometheus-stack -n monitoring --create-namespace
Q: Grafana กับ Kibana ต่างกันอย่างไร?
A: Grafana เหมาะกับ metrics visualization (time-series data) Kibana เหมาะกับ log analysis (text data) ใช้ด้วยกันได้ — Grafana สำหรับ metrics, Kibana สำหรับ logs
Q: ต้องใช้ server spec แค่ไหน?
A: สำหรับ 100 targets, 15s scrape interval: 2 CPU, 4GB RAM, 50GB SSD เพียงพอ สำหรับ 1000+ targets: 8 CPU, 32GB RAM, 500GB SSD
ผมเคยเขียนเรื่องที่เกี่ยวข้องไว้ใน grafana loki logql remote work setup
สรุป — เริ่ม Monitor วันนี้
Prometheus + Grafana เป็น monitoring stack ที่ดีที่สุดในปี 2026 ฟรี ทรงพลัง และมี community ใหญ่มาก เริ่มจากติดตั้ง Prometheus + Node Exporter + Grafana ตั้ง alert rules พื้นฐาน import dashboard จาก community ภายใน 1 ชั่วโมงคุณจะมี monitoring ที่ดีกว่า 90% ขององค์กรทั่วโลก
ถ้ามีคำถาม สอบถามได้ที่ SiamCafe Forum ครับ
💡 เรียนรู้เพิ่มเติม: | | — จาก ผู้เชี่ยวชาญประสบการณ์กว่า 13 ปี
🎬 ดูวิดีโอเพิ่มเติม
เรียนรู้ IT, Forex Trading และเทคนิค Server จากประสบการณ์จริง 30 ปี