Mục lục
- Mục tiêu bài học
- Kiến trúc tổng thể
- App expose /metrics — FastAPI
- Cài Prometheus với Docker
- Cài Grafana với Docker
- Docker Compose — full stack
- PromQL — query language
- Grafana dashboard — pattern AI app
- Import dashboard có sẵn
- Alert rules
- Alertmanager — route alert ra Slack
- GPU monitoring — DCGM Exporter
- Loki — log aggregation
- Retention và long-term storage
- Cloud managed
- Common pitfalls
- Tóm tắt
- Bài tiếp theo
Mục tiêu bài học
Sau bài này bạn sẽ:
- ✅ Hiểu kiến trúc Prometheus scrape + Grafana visualize và vai trò từng component
- ✅ Expose
/metricstừ FastAPI bằngprometheus-fastapi-instrumentator - ✅ Viết PromQL cho rate, percentile, error rate
- ✅ Xây Grafana dashboard với panel cho LLM metrics (token cost, TTFT, cache hit rate)
- ✅ Thiết lập alert rule với Prometheus Alertmanager và route đến Slack
- ✅ Monitor GPU workload với DCGM Exporter
- ✅ Biết các pitfall về cardinality và retention
Kiến trúc tổng thể
Prometheus và Grafana là hai project riêng biệt nhưng thường dùng cùng nhau:
Prometheus
Time-series database kết hợp với scrape engine. Hoạt động theo mô hình pull: Prometheus chủ động gọi HTTP GET đến endpoint /metrics của app theo interval cấu hình (mặc định 15 giây). Dữ liệu lưu trong TSDB (time-series database) trên disk local. Có thêm Alertmanager để gửi alert khi rule thỏa mãn.
Grafana
Visualization layer. Không lưu data; chỉ query từ data source (Prometheus, Loki, Elasticsearch, InfluxDB, ...) và render thành dashboard. Hỗ trợ cả alert từ Grafana 9+, nhưng alert rule thường đặt ở Prometheus để không phụ thuộc Grafana UI.
Luồng dữ liệu
AI app ──expose──► /metrics endpoint (text format)
│
Prometheus scrape (mỗi 15s)
│
TSDB on disk
│
┌───────┴────────┐
Grafana query Alertmanager
(PromQL) (rule eval)
│ │
Dashboard Slack / PagerDuty / email
Node Exporter
Agent cài trên host để expose OS metrics: CPU, RAM, disk I/O, network. Là exporter riêng, không cần sửa code app. Chạy trên port 9100.
Prometheus text format
Mỗi metric là một dòng key-value có thể kèm labels:
# HELP http_requests_total Total HTTP requests
# TYPE http_requests_total counter
http_requests_total{method="POST",endpoint="/inference",status="200"} 1423
http_requests_total{method="POST",endpoint="/inference",status="500"} 12
# HELP http_request_duration_seconds HTTP request duration
# TYPE http_request_duration_seconds histogram
http_request_duration_seconds_bucket{le="0.1"} 234
http_request_duration_seconds_bucket{le="0.5"} 891
http_request_duration_seconds_bucket{le="1.0"} 1201
http_request_duration_seconds_bucket{le="+Inf"} 1423
http_request_duration_seconds_sum 2847.3
http_request_duration_seconds_count 1423
Có 4 loại metric type: Counter (chỉ tăng), Gauge (tăng giảm tự do), Histogram (phân phối vào bucket), Summary (quantile tính sẵn). Với latency nên dùng Histogram để Prometheus tự tính quantile qua PromQL.
App expose /metrics — FastAPI
Cài thư viện
pip install prometheus-fastapi-instrumentator==7.0.0
pip install prometheus-client==0.21.0
Expose /metrics tự động
prometheus-fastapi-instrumentator tự đo latency, request count, in-progress request cho mọi endpoint FastAPI:
from fastapi import FastAPI
from prometheus_fastapi_instrumentator import Instrumentator
app = FastAPI()
# Instrument tất cả route và tự tạo /metrics endpoint
Instrumentator().instrument(app).expose(app)
@app.post("/inference")
async def inference(payload: dict):
# logic xử lý
return {"result": "..."}
Truy cập http://localhost:8000/metrics sẽ thấy output text format Prometheus. Không cần viết thêm code.
Thêm custom metric cho LLM
Các metric HTTP có sẵn không đủ cho AI app. Cần thêm metric riêng cho token, cost, TTFT:
from prometheus_client import Counter, Histogram, Gauge
# Tổng token đã dùng (counter — chỉ tăng)
llm_tokens_total = Counter(
"llm_tokens_total",
"Total tokens consumed",
labelnames=["model", "direction"], # direction: "input" | "output"
)
# Chi phí LLM tính bằng USD
llm_cost_usd_total = Counter(
"llm_cost_usd_total",
"Total LLM cost in USD",
labelnames=["model"],
)
# Time To First Token (histogram để tính percentile)
llm_ttft_seconds = Histogram(
"llm_ttft_seconds",
"Time to first token in seconds",
labelnames=["model"],
buckets=[0.1, 0.25, 0.5, 1.0, 2.0, 5.0, 10.0],
)
# Cache hit / miss
cache_requests_total = Counter(
"cache_requests_total",
"Total cache lookup requests",
labelnames=["result"], # result: "hit" | "miss"
)
Ghi metric trong code xử lý LLM
import time
async def call_llm(model: str, messages: list) -> dict:
start = time.perf_counter()
# Gọi LLM API (ví dụ OpenAI)
response = await openai_client.chat.completions.create(
model=model,
messages=messages,
stream=False,
)
elapsed = time.perf_counter() - start
usage = response.usage
# Ghi metric
llm_tokens_total.labels(model=model, direction="input").inc(usage.prompt_tokens)
llm_tokens_total.labels(model=model, direction="output").inc(usage.completion_tokens)
llm_ttft_seconds.labels(model=model).observe(elapsed)
# Tính cost ví dụ cho gpt-4o-mini ($0.15/1M input, $0.60/1M output)
cost = (usage.prompt_tokens * 0.15 + usage.completion_tokens * 0.60) / 1_000_000
llm_cost_usd_total.labels(model=model).inc(cost)
return response
Ghi cache metric
def lookup_cache(key: str) -> str | None:
value = redis_client.get(key)
if value:
cache_requests_total.labels(result="hit").inc()
return value.decode()
else:
cache_requests_total.labels(result="miss").inc()
return None
Cài Prometheus với Docker
Chạy Prometheus container
docker run -d --name prom \
-p 9090:9090 \
-v $(pwd)/prometheus.yml:/etc/prometheus/prometheus.yml \
prom/prometheus:v2.54.0
prometheus.yml
File config khai báo scrape target và interval:
global:
scrape_interval: 15s # Gọi /metrics mỗi 15 giây
evaluation_interval: 15s # Evaluate alert rule mỗi 15 giây
scrape_configs:
- job_name: 'ai-api'
static_configs:
- targets: ['host.docker.internal:8000']
metrics_path: '/metrics'
- job_name: 'node-exporter'
static_configs:
- targets: ['host.docker.internal:9100']
host.docker.internal là hostname trỏ đến host machine từ trong container (macOS/Windows). Trên Linux dùng IP của host hoặc dùng network_mode: host. Khi dùng Docker Compose, thay bằng service name (xem mục 6).
Verify Prometheus hoạt động
Truy cập http://localhost:9090 → tab Status → Targets. Mỗi target phải có state UP. Nếu DOWN, kiểm tra port và hostname.
Chạy Node Exporter
docker run -d --name node-exporter \
-p 9100:9100 \
prom/node-exporter:v1.8.2
Cài Grafana với Docker
docker run -d --name grafana \
-p 3000:3000 \
-e GF_SECURITY_ADMIN_PASSWORD=admin \
grafana/grafana:11.2.0
Truy cập http://localhost:3000, đăng nhập admin/admin. Grafana sẽ yêu cầu đổi mật khẩu lần đầu.
Add Prometheus data source
- Vào Connections → Data sources → Add new data source
- Chọn Prometheus
- URL:
http://prom:9090(nếu dùng Docker Compose, service name làprom) - Click Save & test — phải thấy "Data source is working"
Nếu chạy Grafana và Prometheus standalone (không Compose), URL là http://host.docker.internal:9090.
Docker Compose — full stack
Dùng Docker Compose để khởi động toàn bộ stack với 1 lệnh docker compose up -d:
docker-compose.yml
services:
api:
build: .
ports:
- "8000:8000"
networks:
- monitoring
prometheus:
image: prom/prometheus:v2.54.0
ports:
- "9090:9090"
volumes:
- ./prometheus.yml:/etc/prometheus/prometheus.yml
- ./alerts.yml:/etc/prometheus/alerts.yml
- prom_data:/prometheus
command:
- '--config.file=/etc/prometheus/prometheus.yml'
- '--storage.tsdb.retention.time=30d'
networks:
- monitoring
grafana:
image: grafana/grafana:11.2.0
ports:
- "3000:3000"
environment:
GF_SECURITY_ADMIN_PASSWORD: admin
volumes:
- grafana_data:/var/lib/grafana
networks:
- monitoring
node-exporter:
image: prom/node-exporter:v1.8.2
ports:
- "9100:9100"
networks:
- monitoring
volumes:
prom_data:
grafana_data:
networks:
monitoring:
driver: bridge
prometheus.yml khi dùng Compose
Trong Compose, các service liên lạc qua tên service (thay host.docker.internal):
global:
scrape_interval: 15s
evaluation_interval: 15s
rule_files:
- "alerts.yml"
scrape_configs:
- job_name: 'ai-api'
static_configs:
- targets: ['api:8000']
metrics_path: '/metrics'
- job_name: 'node-exporter'
static_configs:
- targets: ['node-exporter:9100']
Volumes prom_data và grafana_data quan trọng: không khai báo thì mất toàn bộ metric và dashboard khi restart container.
PromQL — query language
PromQL là ngôn ngữ query của Prometheus. Mọi Grafana panel dùng PromQL để lấy data.
Rate — request/sec từ counter
Counter chỉ tăng, không thể dùng trực tiếp để biết RPS. Dùng rate() với time range:
rate(http_requests_total[5m])
Nghĩa: tốc độ tăng trung bình trong 5 phút gần nhất (đơn vị: req/sec). Window [5m] cần ít nhất 2 data point, nên scrape_interval phải nhỏ hơn window này.
Sum by label
sum by (endpoint) (rate(http_requests_total[5m]))
Gộp tất cả label khác, chỉ giữ nhóm theo endpoint. Kết quả: RPS từng endpoint.
Latency percentile từ histogram
histogram_quantile(0.95, rate(http_request_duration_seconds_bucket[5m]))
P95 latency toàn app. Muốn chia theo endpoint:
histogram_quantile(
0.95,
sum by (endpoint, le) (rate(http_request_duration_seconds_bucket[5m]))
)
Lưu ý: phải sum by (..., le) — giữ label le (less than equal) vì đó là bucket boundary của histogram.
Error rate %
100 * sum(rate(http_requests_total{status=~"5.."}[5m]))
/ sum(rate(http_requests_total[5m]))
status=~"5.." là regex match mọi 5xx status code.
LLM cost trong 1 giờ
sum(increase(llm_cost_usd_total[1h]))
increase() là lượng tăng tuyệt đối trong window. Khác rate() — rate là tốc độ (per second), increase là tổng.
Token per minute
sum(rate(llm_tokens_total[1m])) * 60
Cache hit rate %
100 * sum(rate(cache_requests_total{result="hit"}[5m]))
/ sum(rate(cache_requests_total[5m]))
TTFT P95 theo model
histogram_quantile(
0.95,
sum by (model, le) (rate(llm_ttft_seconds_bucket[5m]))
)
Grafana dashboard — pattern AI app
Tổ chức dashboard theo row. Grafana 11 hỗ trợ collapsible row để nhóm panel liên quan.
Row 1: Top-level health
4 Stat panel (single number) đặt cạnh nhau để nhìn tổng quan ngay lập tức:
| Panel | PromQL | Unit |
|---|---|---|
| RPS | sum(rate(http_requests_total[1m])) |
req/s |
| P95 Latency | histogram_quantile(0.95, rate(http_request_duration_seconds_bucket[5m])) * 1000 |
ms |
| Error Rate | 100 * sum(rate(http_requests_total{status=~"5.."}[5m])) / sum(rate(http_requests_total[5m])) |
% |
| Cache Hit Rate | 100 * sum(rate(cache_requests_total{result="hit"}[5m])) / sum(rate(cache_requests_total[5m])) |
% |
Dùng threshold để tô màu: Error Rate > 5% → đỏ, 1-5% → vàng, <1% → xanh.
Row 2: Latency detail
- Time series: P50, P95, P99 theo endpoint (dùng 3 query với quantile 0.5/0.95/0.99).
- Heatmap: dùng
http_request_duration_seconds_buckettrực tiếp để xem phân phối latency theo thời gian. Panel type: Heatmap.
Row 3: LLM metrics
- Stat — LLM Cost Today:
sum(increase(llm_cost_usd_total[24h]))— unit USD. - Time series — Tokens/min:
sum by (model) (rate(llm_tokens_total[1m])) * 60— phân tách theo model. - Time series — TTFT P95:
histogram_quantile(0.95, sum by (model, le) (rate(llm_ttft_seconds_bucket[5m]))). - Stat — Cache Hit Rate: query cache hit rate.
Nếu có custom metric hallucination rate từ evaluator:
100 * sum(rate(llm_eval_hallucination_total[1h]))
/ sum(rate(llm_eval_requests_total[1h]))
Row 4: System resources
- CPU%:
100 - (avg(rate(node_cpu_seconds_total{mode="idle"}[5m])) * 100) - RAM%:
100 * (1 - node_memory_MemAvailable_bytes / node_memory_MemTotal_bytes) - Disk I/O:
rate(node_disk_read_bytes_total[5m])+ write - Network:
rate(node_network_receive_bytes_total[5m])
Import dashboard có sẵn
grafana.com/grafana/dashboards có thư viện dashboard công cộng. Mỗi dashboard có ID dạng số, import nhanh qua UI:
- Vào Dashboards → Import
- Nhập ID, ví dụ
1860cho "Node Exporter Full" - Chọn Prometheus data source
- Click Import
Một số dashboard hữu ích:
| ID | Tên | Dùng cho |
|---|---|---|
| 1860 | Node Exporter Full | OS metrics: CPU, RAM, disk, network |
| 7587 | FastAPI Observability | Tương thích prometheus-fastapi-instrumentator |
| 15661 | NVIDIA DCGM Exporter | GPU metrics |
Export JSON để version control
Grafana cho phép export dashboard thành JSON: Dashboard settings → JSON Model → Copy to clipboard. Lưu file dashboards/ai-app.json vào git repo để track thay đổi dashboard như code.
Có thể auto-provision dashboard khi khởi động Grafana qua volume mount:
# docker-compose.yml — thêm vào service grafana
volumes:
- grafana_data:/var/lib/grafana
- ./grafana/provisioning:/etc/grafana/provisioning
- ./grafana/dashboards:/var/lib/grafana/dashboards
Alert rules
Alert rule viết trong file YAML, Prometheus evaluate theo evaluation_interval. Rule chỉ fire nếu thỏa mãn liên tục trong khoảng for: — tránh alert mỗi spike ngắn.
alerts.yml
groups:
- name: ai-api
rules:
- alert: HighErrorRate
expr: |
100 * sum(rate(http_requests_total{status=~"5.."}[5m]))
/ sum(rate(http_requests_total[5m])) > 1
for: 5m
labels:
severity: critical
annotations:
summary: "Error rate > 1% for 5 minutes"
description: "Current error rate: {{ $value | printf \"%.2f\" }}%"
- alert: HighLatency
expr: |
histogram_quantile(
0.95,
rate(http_request_duration_seconds_bucket[5m])
) > 2
for: 10m
labels:
severity: warning
annotations:
summary: "P95 latency > 2s for 10 minutes"
- alert: HighLLMCostRate
expr: |
sum(rate(llm_cost_usd_total[1h])) * 3600 > 5
for: 15m
labels:
severity: warning
annotations:
summary: "LLM cost > $5/hour"
- alert: ServiceDown
expr: up{job="ai-api"} == 0
for: 1m
labels:
severity: critical
annotations:
summary: "AI API service is down"
Khai báo file rule trong prometheus.yml:
rule_files:
- "alerts.yml"
Kiểm tra rule đã load: http://localhost:9090/rules. Kiểm tra alert đang active: http://localhost:9090/alerts.
Alertmanager — route alert ra Slack
Alertmanager là component riêng của Prometheus ecosystem. Nhận alert từ Prometheus, dedup, group, route đến receiver.
alertmanager.yml
global:
resolve_timeout: 5m
route:
group_by: ['alertname', 'severity']
group_wait: 30s # Chờ 30s để gom alert cùng nhóm
group_interval: 5m # Gửi lại mỗi 5 phút nếu vẫn firing
repeat_interval: 4h # Gửi nhắc lại sau 4h
receiver: 'slack-warning'
routes:
- match:
severity: critical
receiver: 'slack-critical'
receivers:
- name: 'slack-warning'
slack_configs:
- api_url: 'https://hooks.slack.com/services/T0/B0/XXXX'
channel: '#alerts-warning'
text: '{{ .CommonAnnotations.summary }}'
- name: 'slack-critical'
slack_configs:
- api_url: 'https://hooks.slack.com/services/T0/B0/XXXX'
channel: '#alerts-critical'
text: '🔴 {{ .CommonAnnotations.summary }}'
Thêm Alertmanager vào Compose
alertmanager:
image: prom/alertmanager:v0.27.0
ports:
- "9093:9093"
volumes:
- ./alertmanager.yml:/etc/alertmanager/alertmanager.yml
networks:
- monitoring
Cập nhật prometheus.yml để biết địa chỉ Alertmanager:
alerting:
alertmanagers:
- static_configs:
- targets: ['alertmanager:9093']
Ngoài Slack, Alertmanager hỗ trợ: PagerDuty, email, OpsGenie, webhook. Cấu hình tương tự, chỉ thay block trong receivers.
GPU monitoring — DCGM Exporter
NVIDIA DCGM Exporter (Data Center GPU Manager) expose GPU metrics cho Prometheus. Quan trọng với inference server hoặc training job.
Thêm vào docker-compose.yml
dcgm-exporter:
image: nvcr.io/nvidia/k8s/dcgm-exporter:3.3.5-3.4.1-ubuntu22.04
runtime: nvidia
environment:
- NVIDIA_VISIBLE_DEVICES=all
ports:
- "9400:9400"
networks:
- monitoring
Yêu cầu: NVIDIA Container Toolkit cài sẵn (nvidia-container-toolkit). Không có GPU thì skip service này.
Scrape config cho GPU
- job_name: 'dcgm-exporter'
static_configs:
- targets: ['dcgm-exporter:9400']
Metrics DCGM Exporter expose
| Metric | Ý nghĩa |
|---|---|
DCGM_FI_DEV_GPU_UTIL |
GPU utilization (%) |
DCGM_FI_DEV_MEM_COPY_UTIL |
Memory bandwidth utilization (%) |
DCGM_FI_DEV_FB_USED |
GPU memory used (MB) |
DCGM_FI_DEV_FB_FREE |
GPU memory free (MB) |
DCGM_FI_DEV_GPU_TEMP |
GPU temperature (°C) |
DCGM_FI_DEV_POWER_USAGE |
Power consumption (W) |
Ví dụ PromQL cho GPU memory usage %:
100 * DCGM_FI_DEV_FB_USED
/ (DCGM_FI_DEV_FB_USED + DCGM_FI_DEV_FB_FREE)
Dùng dashboard Grafana ID 15661 để có panel GPU đầy đủ ngay mà không cần tự build.
Loki — log aggregation
Loki là log aggregation system của Grafana Labs, cùng codebase với Prometheus về thiết kế label. Khác Elasticsearch: Loki không index full-text, chỉ index labels. Điều này giúp storage nhỏ hơn nhiều nhưng query nội dung log chậm hơn.
LGTM stack
Grafana Labs đặt tên stack của họ là LGTM:
- Loki — log aggregation, query bằng LogQL
- Grafana — visualization
- Tempo — distributed tracing
- Mimir (hoặc Prometheus) — metrics
Trong Grafana, bạn có thể correlate: click vào log line của Loki → jump sang trace tương ứng trong Tempo → thấy span nào chậm. Đây là observability đầy đủ (metrics + logs + traces).
Thêm Loki vào stack (tùy chọn)
loki:
image: grafana/loki:3.2.0
ports:
- "3100:3100"
networks:
- monitoring
promtail:
image: grafana/promtail:3.2.0
volumes:
- /var/log:/var/log
- ./promtail.yml:/etc/promtail/config.yml
networks:
- monitoring
Promtail là agent đọc log file và gửi lên Loki. Sau đó add Loki làm data source trong Grafana, query bằng LogQL.
Bài này tập trung vào Prometheus + Grafana cho metrics. Loki và Tempo là chủ đề riêng nếu cần đi sâu.
Retention và long-term storage
Retention mặc định
Prometheus mặc định giữ dữ liệu 15 ngày. Sau đó block cũ bị xóa tự động. Cấu hình trong docker-compose.yml:
command:
- '--config.file=/etc/prometheus/prometheus.yml'
- '--storage.tsdb.retention.time=30d'
- '--storage.tsdb.retention.size=10GB'
Cả hai flag có thể dùng cùng nhau — block bị xóa khi vi phạm bất kỳ điều kiện nào.
Long-term storage
Prometheus local TSDB không phù hợp cho retention nhiều tháng hay multi-instance. Các giải pháp phổ biến:
| Tool | Đặc điểm |
|---|---|
| Thanos | Open source, object storage (S3/GCS), HA Prometheus, downsampling |
| Mimir | Grafana Labs, fully managed hay self-hosted, horizontal scale |
| VictoriaMetrics | Open source, drop-in replacement TSDB, nhanh hơn Prometheus về write |
| Cortex | CNCF project, tiền thân của Mimir |
Với hầu hết AI app startup, 30 ngày retention local là đủ. Chỉ cần long-term storage khi cần trend analysis qua nhiều tháng hoặc compliance.
Cloud managed
Nếu không muốn tự maintain stack, có các lựa chọn managed:
Grafana Cloud
Grafana Labs cung cấp managed Prometheus + Grafana + Loki + Tempo trên cloud. Free tier: 10,000 metrics, 50GB logs, 50GB traces. App gửi metric về Grafana Cloud qua remote_write:
# prometheus.yml — thêm remote_write
remote_write:
- url: https://prometheus-prod-01-eu-west-0.grafana.net/api/prom/push
basic_auth:
username: '12345'
password: '${GRAFANA_CLOUD_TOKEN}'
AWS Managed Service for Prometheus (AMP) + Grafana (AMG)
Tích hợp với IAM, phù hợp nếu app chạy trên EKS/ECS. App gửi metric qua remote_write đến AMP endpoint. AMG kết nối AMP như data source. Không cần tự quản lý storage.
Datadog và New Relic
Proprietary APM với khả năng cao hơn (tracing, profiling, RUM, SLO tracking) nhưng chi phí cao hơn đáng kể so với stack open source. Phù hợp khi team không có capacity tự vận hành Prometheus.
Common pitfalls
Cardinality bomb
Đây là vấn đề phổ biến nhất. Cardinality = số lượng time series duy nhất. Số này bằng tích của tất cả giá trị có thể của label combinations.
Ví dụ nguy hiểm:
# ĐỪNG làm — user_id có thể là hàng triệu giá trị
requests_by_user = Counter(
"http_requests_total",
labelnames=["endpoint", "user_id"], # ← BAD
)
100 endpoint × 1 triệu user_id = 100 triệu time series. Prometheus sẽ OOM. Labels an toàn là những giá trị có bounded cardinality: endpoint name, HTTP method, status code, model name.
Scrape interval quá ngắn
Interval 1 giây tạo load không cần thiết trên app và Prometheus. 15s là default phù hợp cho production. Chỉ giảm xuống 5s khi cần độ chính xác cao cho alert.
Quên for: trong alert rule
Nếu không có for:, alert fire ngay khi expression thỏa mãn lần đầu, kể cả spike ngắn. Kết quả: alert spam. Luôn đặt for: ít nhất bằng 2-3 lần scrape_interval.
Dashboard query nặng
Range 30 ngày kết hợp với sum() nhiều label → query có thể mất vài chục giây. Giải pháp: dùng recording rule để pre-compute query nặng, lưu kết quả như metric mới:
# prometheus.yml — recording rule
groups:
- name: recordings
interval: 1m
rules:
- record: job:http_request_duration_seconds:p95
expr: |
histogram_quantile(
0.95,
sum by (endpoint, le) (rate(http_request_duration_seconds_bucket[5m]))
)
Dashboard dùng job:http_request_duration_seconds:p95 thay vì query full.
Không mount volume Prometheus
Chạy Prometheus container không có volume → restart container là mất toàn bộ metric. Luôn mount prom_data:/prometheus trong Compose.
Alert không có receiver
Prometheus có thể evaluate rule và gửi alert đến Alertmanager, nhưng nếu không cấu hình Alertmanager hoặc receiver không hợp lệ, alert chỉ hiển thị trong UI mà không notify. Test receiver bằng amtool hoặc Alertmanager web UI.
Histogram bucket không cover range thực tế
Nếu LLM latency thường là 2-10 giây nhưng bucket chỉ đến 1.0, hầu hết observation rơi vào bucket +Inf. Percentile tính ra sẽ sai. Định nghĩa bucket phù hợp với SLO thực tế của app:
llm_ttft_seconds = Histogram(
"llm_ttft_seconds",
"Time to first token",
buckets=[0.1, 0.25, 0.5, 1.0, 2.0, 5.0, 10.0, 30.0], # Cover đến 30s
)
Tóm tắt
✅ Prometheus scrape /metrics mỗi 15s, lưu time-series trong TSDB local
✅ prometheus-fastapi-instrumentator expose HTTP metrics tự động; thêm Counter/Histogram riêng cho LLM token, cost, TTFT
✅ PromQL: rate() cho counter, histogram_quantile() cho percentile, increase() cho total trong window
✅ Grafana dashboard tổ chức theo row: top-level health → latency → LLM metrics → system
✅ Alert rule cần for: để tránh noise; Alertmanager route đến Slack/PagerDuty
✅ DCGM Exporter expose GPU utilization, memory, temperature cho inference workload GPU
✅ Tránh cardinality bomb — không dùng user_id hoặc trace_id làm label Prometheus
✅ Mount volume prom_data và grafana_data để dữ liệu tồn tại qua restart
