Hãy tưởng tượng bạn vừa deploy model AI lên production. 10 phút sau, người dùng phàn nàn: "Hệ thống chậm quá!". Bạn kiểm tra server - CPU bình thường, RAM OK. Nhưng... tại sao lại chậm?
Không có observability = bạn đang lái xe trong đêm tối, không có đèn pha, không có đồng hồ tốc độ, không có GPS. Có gì đó sai nhưng bạn không biết cái gì, ở đâu, và tại sao.
Observability không chỉ là "theo dõi" - nó là khả năng hiểu trạng thái nội bộ của hệ thống dựa trên outputs bên ngoài. Theo Google SRE: "Monitoring cho bạn biết khi nào có vấn đề. Observability cho bạn biết tại sao có vấn đề đó."
Một nghiên cứu của Datadog cho thấy: Các tổ chức có observability tốt giải quyết sự cố nhanh hơn 60% và giảm thời gian ngừng hoạt động 73% so với những nơi chỉ có monitoring cơ bản.
Logs là gì? Records có timestamp ghi lại các sự kiện rời rạc trong hệ thống.
Ví dụ:
2024-02-02T10:30:15.123Z [INFO] Người dùng 12345 đăng nhập
2024-02-02T10:30:16.456Z [INFO] Yêu cầu dự đoán: user_id=12345, model=fraud_v2
2024-02-02T10:30:16.789Z [ERROR] Database timeout sau 5000ms
2024-02-02T10:30:17.012Z [WARN] Thử lại lần 2/3
2024-02-02T10:30:18.234Z [INFO] Dự đoán thành công: fraud_score=0.23
Các loại Logs:
Log ứng dụng:
import logging
logger = logging.getLogger(__name__)
def predict(features):
logger.info(f"Bắt đầu dự đoán: user_id={features['user_id']}")
try:
result = model.predict(features)
logger.info(f"Dự đoán thành công: score={result}")
return result
except Exception as e:
logger.error(f"Dự đoán thất bại: {e}", exc_info=True)
raise
Log web server:
192.168.1.1 - - [02/Feb/2024:10:30:15] "POST /api/predict HTTP/1.1" 200 1234 0.456
Log hệ thống:
Feb 2 10:30:15 server1 kernel: Hết bộ nhớ: Kill process 1234 (python) score 856
Mức độ Log:
DEBUG: Thông tin chi tiết để debug
INFO: Thông tin chung
WARNING: Có gì đó bất thường nhưng chưa phải lỗi
ERROR: Lỗi xảy ra, thao tác thất bại
CRITICAL: Lỗi nghiêm trọng cấp hệ thống
Best Practices:
✅ Structured Logging (JSON):
# ❌ Khó query
logger.info("User 12345 dự đoán xong, score: 0.8, mất 123ms")
# ✅ Dễ query
logger.info("Dự đoán hoàn thành", extra={
"user_id": 12345,
"model_version": "v2.1",
"prediction_score": 0.8,
"latency_ms": 123,
"timestamp": "2024-02-02T10:30:15Z"
})
Output dạng JSON:
{
"level": "INFO",
"message": "Dự đoán hoàn thành",
"user_id": 12345,
"model_version": "v2.1",
"prediction_score": 0.8,
"latency_ms": 123,
"timestamp": "2024-02-02T10:30:15Z"
}
✅ Correlation IDs:
import uuid
def handle_request(request):
correlation_id = str(uuid.uuid4())
logger.info("Nhận request", extra={
"correlation_id": correlation_id,
"endpoint": "/predict"
})
result = service.process(request, correlation_id)
logger.info("Request hoàn thành", extra={
"correlation_id": correlation_id,
"duration_ms": 234
})
→ Theo dõi request qua nhiều services:
Service A: correlation_id=abc-123
→ Service B: correlation_id=abc-123
→ Service C: correlation_id=abc-123
✅ Log Sampling (với traffic cao):
import random
def log_with_sampling(message, sample_rate=0.1):
if random.random() < sample_rate:
logger.info(message) # Chỉ log 10% requests
❌ Những sai lầm thường gặp:
# ❌ Log dữ liệu nhạy cảm
logger.info(f"Mật khẩu user: {password}") # TUYỆT ĐỐI KHÔNG!
# ❌ Quá chi tiết trong production
logger.debug(f"Biến x={x}, y={y}, z={z}...") # Dùng DEBUG ít thôi
# ❌ Không có context
logger.error("Dự đoán thất bại") # TẠI SAO? User nào? Model nào?
Metrics là gì? Các phép đo số học được tổng hợp theo thời gian.
Các loại:
Counter: Chỉ tăng (không giảm)
from prometheus_client import Counter
prediction_requests = Counter(
'prediction_requests_total',
'Tổng số requests dự đoán',
['model_version', 'status']
)
# Tăng counter
prediction_requests.labels(model_version='v2', status='success').inc()
prediction_requests.labels(model_version='v2', status='error').inc()
Dùng cho: Số requests, số lỗi, số jobs hoàn thành
Gauge: Giá trị có thể tăng giảm
from prometheus_client import Gauge
active_connections = Gauge(
'active_connections',
'Số kết nối đang active'
)
# Set giá trị
active_connections.set(42)
active_connections.inc() # 43
active_connections.dec() # 42
Dùng cho: Memory usage, CPU usage, queue size, số requests đồng thời
Histogram: Phân phối giá trị
from prometheus_client import Histogram
request_duration = Histogram(
'request_duration_seconds',
'Thời gian xử lý request',
buckets=[0.1, 0.5, 1.0, 2.0, 5.0] # Ngưỡng phân nhóm
)
# Ghi nhận giá trị
with request_duration.time():
process_request()
Kết quả:
request_duration_seconds_bucket{le="0.1"} 324 # 324 requests < 0.1s
request_duration_seconds_bucket{le="0.5"} 892 # 892 requests < 0.5s
request_duration_seconds_bucket{le="1.0"} 1234 # 1234 requests < 1.0s
Dùng cho: Latency, response time, request size
Summary: Tương tự Histogram nhưng tính percentiles
from prometheus_client import Summary
latency = Summary(
'prediction_latency_seconds',
'Thời gian dự đoán'
)
latency.observe(0.234) # Ghi 234ms
Output:
prediction_latency_seconds_sum 123.45 # Tổng thời gian
prediction_latency_seconds_count 500 # Tổng requests
prediction_latency_seconds{quantile="0.5"} 0.2 # p50 (trung vị)
prediction_latency_seconds{quantile="0.95"} 0.8 # p95
prediction_latency_seconds{quantile="0.99"} 1.2 # p99
Metrics quan trọng cho AI Systems:
Model Metrics:
# Phân phối điểm dự đoán
prediction_score = Histogram('prediction_score', 'Điểm output của model')
# Thời gian inference
inference_duration = Histogram('inference_duration_ms', 'Thời gian inference')
# Theo dõi version model
model_version = Gauge('model_version', 'Version model hiện tại')
System Metrics:
# Request rate
requests_per_second = Counter('http_requests_total')
# Error rate
error_rate = Counter('http_errors_total')
# Resource usage
memory_usage = Gauge('memory_usage_bytes')
cpu_usage = Gauge('cpu_usage_percent')
Business Metrics:
# Tác động doanh thu
revenue_per_request = Summary('revenue_per_request')
# User engagement
active_users = Gauge('active_users')
# Conversion rate
conversions = Counter('conversions_total')
Ngưỡng Cảnh báo:
# Ví dụ: Prometheus alerting rules
groups:
- name: api_alerts
rules:
# Error rate cao
- alert: HighErrorRate
expr: |
rate(http_errors_total[5m]) / rate(http_requests_total[5m]) > 0.05
for: 5m
annotations:
summary: "Tỷ lệ lỗi vượt 5%"
# Latency cao
- alert: HighLatency
expr: |
histogram_quantile(0.95, rate(request_duration_seconds_bucket[5m])) > 2
for: 10m
annotations:
summary: "p95 latency vượt 2 giây"
# Model giảm chất lượng
- alert: ModelAccuracyDrop
expr: |
model_accuracy < 0.85
for: 30m
annotations:
summary: "Độ chính xác model giảm xuống dưới 85%"
Traces là gì? Ghi lại hành trình của một request đi qua hệ thống phân tán.
Ví dụ: User gửi request dự đoán
User Request
├─ API Gateway (50ms)
│ ├─ Auth Service (20ms)
│ └─ Rate Limiter (5ms)
├─ Prediction Service (150ms)
│ ├─ Feature Store (30ms)
│ ├─ Model Inference (100ms) ← Nút thắt cổ chai!
│ └─ Cache Check (20ms)
└─ Logging Service (10ms)
Tổng: 235ms
Distributed Tracing:
from opentelemetry import trace
from opentelemetry.sdk.trace import TracerProvider
# Setup tracer
trace.set_tracer_provider(TracerProvider())
tracer = trace.get_tracer(__name__)
def predict(user_id, features):
# Bắt đầu span
with tracer.start_as_current_span("predict") as span:
span.set_attribute("user_id", user_id)
# Child span: lấy features
with tracer.start_as_current_span("fetch_features"):
features = feature_store.get(user_id)
# Child span: inference
with tracer.start_as_current_span("model_inference"):
prediction = model.predict(features)
span.set_attribute("prediction_score", prediction)
return prediction
Trace Visualization:
┌─────────────────────────────────────────────┐
│ predict (235ms) │
├─────────────────────────────────────────────┤
│ ├─ fetch_features (30ms) │
│ ├─ model_inference (100ms) ← Chậm! │
│ └─ cache_write (20ms) │
└─────────────────────────────────────────────┘
Thông tin quan trọng trong Traces:
Use Cases:
✅ Tìm nút thắt:
Trace cho thấy:
- API Gateway: 50ms
- Auth: 20ms
- Model Inference: 2000ms ← Vấn đề ở đây!
- Database: 30ms
→ Tập trung optimize model inference
✅ Debug lỗi:
Request thất bại tại:
Service A → Service B → Service C [ERROR: timeout]
Trace cho thấy Service C đợi database 30 giây
→ Database là nguyên nhân gốc
✅ Hiểu dependencies:
Prediction service phụ thuộc vào:
- Feature Store (quan trọng)
- Cache (optional, có thể fallback)
- Logging (async, không chặn)
Monolith:
Request → Application → Database → Response
↑ Dễ trace
Microservices:
Request → API Gateway
├→ Auth Service
│ └→ User DB
├→ Prediction Service
│ ├→ Feature Store
│ │ └→ Redis Cache
│ ├→ Model Server
│ └─→ Result Cache
└→ Logging Service
└→ ElasticSearch
Làm sao trace flow này? 🤯
Khái niệm chính: Truyền trace ID và span ID qua tất cả services.
# Service A
def call_service_b(request):
trace_id = request.headers.get('X-Trace-ID') or generate_trace_id()
span_id = generate_span_id()
# Truyền sang Service B
headers = {
'X-Trace-ID': trace_id,
'X-Parent-Span-ID': span_id
}
response = requests.post('http://service-b/api', headers=headers)
return response
# Service B
def handle_request(request):
trace_id = request.headers.get('X-Trace-ID')
parent_span_id = request.headers.get('X-Parent-Span-ID')
# Tạo child span
span = create_span(trace_id, parent_span_id)
# Xử lý request
result = process()
span.finish()
return result
Kết quả: Tất cả spans được liên kết bởi trace_id → nhìn thấy toàn bộ flow request.
Vấn đề: Trace mọi request = quá nhiều data (tốn kém, choáng ngợp).
Giải pháp:
Head-based Sampling:
import random
def should_trace():
return random.random() < 0.1 # Trace 10% requests
Tail-based Sampling:
Thu thập TẤT CẢ traces ban đầu → Chỉ giữ:
- Errors (100%)
- Requests chậm (>1s) (100%)
- Requests bình thường (5%)
Adaptive Sampling:
def get_sample_rate():
if error_rate > 0.05:
return 1.0 # Trace 100% khi có sự cố
elif cpu_usage > 0.8:
return 0.01 # Trace 1% khi hệ thống stress
else:
return 0.1 # Bình thường: 10%
Theo dõi 4 chỉ số này cho mọi service:
Latency: Requests mất bao lâu
p50, p95, p99 latency
Mục tiêu: p95 < 200ms
Traffic: Số lượng requests
Requests per second
Mục tiêu: Xử lý được 1000 req/s
Errors: Số requests thất bại
Error rate = lỗi / tổng requests
Mục tiêu: < 0.1% (99.9% thành công)
Saturation: Hệ thống "đầy" đến mức nào
CPU usage, Memory usage, Queue depth
Mục tiêu: < 80% capacity
Định nghĩa mục tiêu rõ ràng:
SLO:
availability: 99.9% # Tối đa ngừng: 43 phút/tháng
latency_p95: 200ms
error_rate: 0.1%
throughput: 1000 req/s
Error Budget:
SLO = 99.9% uptime
→ Error budget = 0.1% = 43 phút downtime/tháng
Nếu hết error budget:
- Dừng release features mới
- Tập trung vào reliability
- Họp post-mortem
Tạo dashboard theo vai trò:
Operations Dashboard:
- Request rate (1 giờ qua)
- Error rate (1 giờ qua)
- p95 latency (1 giờ qua)
- Cảnh báo đang active
- Tình trạng services
Business Dashboard:
- Số user active hàng ngày
- Doanh thu theo giờ
- Tỷ lệ chuyển đổi
- Phân phối điểm dự đoán model
Engineering Dashboard:
- Tần suất deploy
- Thời gian phục hồi trung bình (MTTR)
- Tỷ lệ thất bại khi thay đổi
- Lead time cho changes
Alerts phải:
✅ Actionable: Tôi có thể fix ngay
✅ Urgent: Cần xử lý bây giờ
✅ Specific: Tôi biết chính xác vấn đề gì
❌ Tránh alert fatigue:
# ❌ ALERT TỆ
"CPU usage > 50%" → Không khẩn, xảy ra suốt
# ✅ ALERT TỐT
"p95 latency > 2s trong 10 phút VÀ error rate > 1%"
→ Khẩn cấp, có thể hành động, cụ thể
Mức độ Cảnh báo:
P0 (Critical): Service down, tất cả users bị ảnh hưởng
→ Gọi on-call engineer ngay
→ Auto-rollback nếu do deploy gần đây
P1 (High): Hiệu năng giảm, một số users bị ảnh hưởng
→ Alert on-call, phản hồi trong 15 phút
P2 (Medium): Vấn đề nhỏ, có workaround
→ Tạo ticket, fix trong giờ hành chính
P3 (Low): Vấn đề cosmetic, không ảnh hưởng user
→ Backlog item
Khi cảnh báo kêu:
Bước 1: Xác nhận
Nhận alert → Xác nhận → Ngăn duplicate pages
Bước 2: Đánh giá
- Cái gì bị hỏng? (kiểm tra dashboards, logs, traces)
- Bao nhiêu users bị ảnh hưởng?
- Có đang tệ hơn không?
Bước 3: Giảm thiểu
Fix tạm thời để khôi phục service:
- Rollback deployment
- Scale up resources
- Bật circuit breaker
- Chuyển hướng traffic
Bước 4: Phân tích Nguyên nhân Gốc
- Điều gì trigger sự cố?
- Tại sao monitoring không phát hiện sớm hơn?
- Làm sao ngăn tái diễn?
Bước 5: Post-mortem
Post-mortem không đổ lỗi:
- Timeline các sự kiện
- Nguyên nhân gốc
- Action items
- Tác động đến SLO
Quản lý Log:
Metrics:
Tracing:
All-in-one:
Nhớ rằng:
Monitoring: "Nó có bị hỏng không?"
Observability: "Tại sao nó bị hỏng?"
Trong bài tiếp theo, chúng ta sẽ khám phá Agile & Project Management - cách quản lý ML projects hiệu quả trong môi trường không chắc chắn.
Bài viết thuộc series "From Zero to AI Engineer" - Module 10: Scalability & Observability