Bối cảnh & Tại sao cần
Chạy script hay backend service trên production, khó chịu nhất là hệ thống chậm dần đều nhưng log không báo lỗi nào. Log chỉ cho biết sự kiện gì vừa xảy ra tại một thời điểm nhất định. Nó hoàn toàn bất lực khi bạn cần biết throughput hiện tại, p99 latency hay số kết nối database đang nghẽn.
Thực tế tại dự án trước của mình, một cron job sync 50.000 bản ghi thỉnh thoảng treo cứng giữa đêm. Mở file log hơn 2 GB ra grep mỏi mắt cũng không rõ script kẹt ở bước đọc API đối tác hay ghi DB. Sau khi gắn prometheus-client vào code, bức tranh sáng tỏ ngay lập tức. Tốc độ xử lý rớt từ 120 record/s xuống 0, kèm theo gauge kết nối pool DB chạm trần. Không còn cảnh đoán mò.
Bốn loại metrics cốt lõi cần nhớ
- Counter: Số chỉ tăng, chỉ reset về 0 khi restart app. Phù hợp đếm tổng request, số giao dịch thanh toán thành công hay số job thất bại.
- Gauge: Giá trị dao động lên xuống linh hoạt. Thường dùng đo RAM/CPU hiện tại, số worker đang bận hoặc số connection trong pool.
- Histogram: Đo phân phối dữ liệu (thời gian phản hồi, kích thước payload) rồi gom vào các bucket dựng sẵn. Hỗ trợ tính toán quantile (p50, p95, p99) linh hoạt trên Prometheus server.
- Summary: Cũng đo phân phối nhưng tính sẵn quantile trực tiếp phía client. Nhược điểm: tốn tài nguyên app và không thể cộng gộp (aggregate) giữa nhiều instance. Đa số trường hợp backend nên ưu tiên Histogram.
Cài đặt môi trường
Cài đặt thư viện chính thức từ PyPI:
pip install prometheus-client
Để kiểm thử toàn bộ luồng gồm Prometheus và Grafana ngay trên máy local, Docker Compose là lựa chọn nhanh nhất. Tạo file docker-compose.yml:
version: '3.8'
services:
prometheus:
image: prom/prometheus:v2.51.0
ports:
- "9090:9090"
volumes:
- ./prometheus.yml:/etc/prometheus/prometheus.yml
extra_hosts:
- "host.docker.internal:host-gateway"
grafana:
image: grafana/grafana:10.4.0
ports:
- "3000:3000"
depends_on:
- prometheus
Lưu ý: Dòng extra_hosts ở trên giúp container Prometheus trên Linux kết nối được ngược lại script Python chạy ngoài host thông qua hostname host.docker.internal.
Triển khai trong mã nguồn Python
1. Khởi tạo và cập nhật metrics
Hàm start_http_server sẽ spawn một daemon thread riêng để mở cổng HTTP, phục vụ endpoint /metrics cho Prometheus cào dữ liệu theo chu kỳ.
import time
import random
from prometheus_client import start_http_server, Counter, Gauge, Histogram
# Khai bao metrics
REQUEST_COUNT = Counter(
'app_http_requests_total',
'Tong so request HTTP ma he thong da nhan',
['method', 'endpoint', 'status']
)
ACTIVE_WORKERS = Gauge(
'app_active_workers',
'So worker dang xu ly cong viec dong thoi'
)
REQUEST_LATENCY = Histogram(
'app_request_latency_seconds',
'Thoi gian xu ly request tinh bang giay',
['endpoint'],
buckets=[0.05, 0.1, 0.25, 0.5, 1.0, 2.5, 5.0]
)
def process_request():
endpoint = random.choice(['/api/v1/orders', '/api/v1/users', '/api/v1/checkout'])
method = 'POST' if endpoint == '/api/v1/checkout' else 'GET'
ACTIVE_WORKERS.inc()
start_time = time.time()
try:
# Gia lap do tre tu 20ms den 600ms
duration = random.uniform(0.02, 0.6)
time.sleep(duration)
status = '200' if random.random() > 0.1 else '500'
finally:
elapsed = time.time() - start_time
REQUEST_LATENCY.labels(endpoint=endpoint).observe(elapsed)
REQUEST_COUNT.labels(method=method, endpoint=endpoint, status=status).inc()
ACTIVE_WORKERS.dec()
if __name__ == '__main__':
start_http_server(8000)
print("Metrics endpoint san sang tai: http://localhost:8000/metrics")
while True:
process_request()
time.sleep(0.05)
2. Lưu ý thực chiến để tránh sập Prometheus (High Cardinality)
- Tránh bẫy bùng nổ nhãn (High Cardinality): Tuyệt đối không đưa User ID, Email, UUID hay Order ID vào label. Giả sử hệ thống có 100.000 user, mỗi metric sẽ sinh ra 100.000 time-series riêng biệt. Prometheus ngốn sạch RAM và bị OOMKilled chỉ sau vài giờ. Label chỉ nên chứa các giá trị hữu hạn như HTTP status code, API route, service name hoặc HTTP method.
- Quy ước đặt tên chuẩn: Tên metric nên kết thúc bằng đơn vị thời gian hoặc kích thước và dùng số nhiều cho Counter. Ví dụ:
http_request_duration_seconds(Histogram),http_requests_total(Counter),db_connection_pool_active(Gauge). - Thiết kế Bucket sát ngưỡng thực tế: Buckets mặc định của Histogram chạy từ 5ms đến 10s. Nếu bạn đo query Elasticsearch chạy từ 200ms đến 30s, các bucket nhỏ dưới 100ms sẽ hoàn toàn thừa thãi, gây lãng phí bộ nhớ.
- Tận dụng Decorator: Thay vì viết thủ công
try/finally, bạn có thể dùng cú pháp ngắn gọn@REQUEST_LATENCY.labels(endpoint='/api/pay').time()hoặc@ACTIVE_WORKERS.track_inprogress()đặt ngay trên hàm cần đo.
3. Cấu hình Scrape Job trên Prometheus
Tạo file prometheus.yml ngang cấp với file Docker Compose để Prometheus định kỳ pull dữ liệu mỗi 5 giây:
global:
scrape_interval: 5s
evaluation_interval: 5s
scrape_configs:
- job_name: 'python-app'
static_configs:
- targets: ['host.docker.internal:8000']
Kiểm tra & Trực quan hóa
1. Soi raw metrics bằng curl
Gửi request kiểm tra xem application đã render định dạng Prometheus text format chuẩn chưa:
curl -s http://localhost:8000/metrics | grep -E "app_http_requests_total|app_active_workers"
Dữ liệu trả về sẽ có cấu trúc nhãn tường minh:
# TYPE app_http_requests_total counter
app_http_requests_total{endpoint="/api/v1/orders",method="GET",status="200"} 482.0
app_http_requests_total{endpoint="/api/v1/checkout",method="POST",status="500"} 53.0
# TYPE app_active_workers gauge
app_active_workers 1.0
2. Viết câu truy vấn PromQL cơ bản
Mở giao diện Prometheus tại http://localhost:9090/graph và thử các truy vấn sau:
- Tần suất request trên giây (Throughput RPS):
sum(rate(app_http_requests_total[1m])) by (endpoint) - Tỷ lệ lỗi 5xx (% error rate):
(sum(rate(app_http_requests_total{status=~"5.."}[5m])) / sum(rate(app_http_requests_total[5m]))) * 100 - Độ trễ phân vị 95 (p95 latency) theo endpoint:
histogram_quantile(0.95, sum(rate(app_request_latency_seconds_bucket[5m])) by (le, endpoint))
3. Lên Dashboard trên Grafana
- Truy cập
http://localhost:3000, đăng nhập user/pass mặc định:admin/admin. - Vào mục Connections > Data Sources, chọn Prometheus và nhập Server URL:
http://prometheus:9090, nhấn Save & Test. - Tạo Dashboard mới, thêm 3 Panel chủ đạo:
- Stat: Query
app_active_workers, cấu hình ngưỡng màu vàng khi vượt 5 worker, đỏ khi vượt 10 worker. - Time Series: Vẽ đường đồ thị RPS và p95 latency nhằm phát hiện đột biến lưu lượng.
- Pie Chart hoặc Bar Gauge: Thống kê tỷ lệ phân bổ status 200 vs 500 để nắm nhanh độ ổn định của API.
- Stat: Query
Bổ sung custom metrics chỉ tốn chừng 15 phút code nhưng mang lại khả năng debug và phân tích sự cố vượt trội hơn hẳn so với việc đọc log truyền thống.

