Sự hỗn loạn khi giám sát thủ công trên môi trường Production
Sáu tháng trước, đội ngũ của chúng tôi đã gặp bế tắc. Chúng tôi quản lý khoảng 40 microservices trên ba cụm Kubernetes. Mỗi khi một lập trình viên triển khai dịch vụ mới, tôi lại phải cập nhật thủ công file ConfigMap prometheus.yml dài 1.200 dòng. Tôi thường phải khởi động lại pod Prometheus và cầu nguyện rằng không có một ký tự tab hay khoảng trắng nào làm hỏng toàn bộ quy trình. Đó là một quá trình bị động và mệt mỏi, khiến đội ngũ DevOps của chúng tôi luôn trong tình trạng “chữa cháy” liên tục.
Điểm giới hạn đã đến trong một đợt triển khai vào chiều thứ Sáu. Một thay đổi nhỏ trong tên dịch vụ đã khiến Prometheus mất dấu đối tượng thu thập dữ liệu (scrape target). Chúng tôi đã mất bốn tiếng đồng hồ trong tình trạng “mù thông tin” vì hệ thống giám sát không tự động thích ứng với thay đổi. Đó là thời điểm tôi nhận ra rằng việc coi Prometheus như một thực thể tĩnh trong một môi trường động là một công thức dẫn đến thảm họa.
Tại sao Prometheus truyền thống lại thất bại trong Kubernetes
Nguyên nhân gốc rễ không nằm ở bản thân Prometheus, mà ở chi phí cấu hình quá lớn. Trong một thiết lập tiêu chuẩn, Prometheus phụ thuộc vào một file cấu hình khổng lồ. Đây là một điểm yếu trong thế giới Kubernetes, nơi các pod chỉ tồn tại tạm thời và các dịch vụ có thể mở rộng quy mô chỉ trong vài giây.
Việc tự động phát hiện (discovery) tiêu chuẩn qua annotations (prometheus.io/scrape: "true") có thể hoạt động tốt trong các môi trường thử nghiệm nhỏ, nhưng nó thiếu sự kiểm soát. Bạn không thể dễ dàng xác định các khoảng thời gian thu thập dữ liệu (scrape interval) khác nhau cho các dịch vụ cụ thể. Việc quản lý các quy tắc cảnh báo (alerting rules) cũng trở thành một cơn ác mộng về mặt vận hành khi bạn có hơn 150 quy tắc dùng chung cho nhiều đội ngũ. Chúng tôi cần việc giám sát phải có tính khai báo (declarative) giống như các ứng dụng mà chúng tôi đang triển khai.
So sánh các giải pháp
Trước khi chuyển đổi, tôi đã đánh giá ba hướng đi chính:
- Helm Charts thủ công: Chúng tôi đã thử chart Prometheus của cộng đồng. Dù tốt hơn YAML thuần túy, nó vẫn yêu cầu quản lý các file values khổng lồ cho mỗi cảnh báo mới.
- Giải pháp SaaS: Các nền tảng như Datadog rất tuyệt vời, nhưng chi phí báo giá cho quy mô của chúng tôi đã vượt quá 2.500 USD mỗi tháng. Chúng tôi cũng cần giữ dữ liệu metric nhạy cảm trong VPC riêng của mình.
- Prometheus Operator: Giải pháp này sử dụng Custom Resource Definitions (CRDs) để quản lý các thành phần. Nó coi việc giám sát như một “công dân hạng nhất” (first-class citizen) của Kubernetes API.
Mô hình Operator đã chiến thắng vì nó phân quyền cấu hình cho các đội ngũ sở hữu dịch vụ. Nếu một lập trình viên cần giám sát một ứng dụng mới, họ chỉ cần thêm một đối tượng ServiceMonitor vào Helm chart của chính họ. Không còn cần phải gửi yêu cầu (ticket) cho đội ngũ hạ tầng nữa.
Triển khai Prometheus Operator
Sau một tháng thử nghiệm, chúng tôi đã quyết định chọn kube-prometheus-stack. Nó đóng gói Operator, Prometheus, Grafana và Alertmanager vào một lần triển khai duy nhất. Làm chủ bộ công cụ này là con đường tắt để thăng tiến từ một quản trị viên cấp thấp lên kỹ sư nền tảng (platform engineer) cao cấp.
1. Triển khai qua Helm
Việc thiết lập bắt đầu với kho lưu trữ Helm. Tôi luôn sử dụng một namespace monitoring riêng biệt để giữ cho cụm được ngăn nắp và áp dụng các hạn mức tài nguyên (resource quotas) nghiêm ngặt.
helm repo add prometheus-community https://prometheus-community.github.io/helm-charts
helm repo update
helm install monitoring prometheus-community/kube-prometheus-stack \
--namespace monitoring \
--create-namespace
2. Tự động phát hiện với ServiceMonitor
Phép màu thực sự nằm ở ServiceMonitor. Thay vì chỉnh sửa cấu hình toàn cục, bạn tạo một file YAML nhỏ. File này chỉ dẫn cho Prometheus biết dịch vụ nào cần thu thập dữ liệu dựa trên các nhãn (labels). Đây là mẫu chúng tôi sử dụng cho các dịch vụ API nội bộ:
apiVersion: monitoring.coreos.com/v1
kind: ServiceMonitor
metadata:
name: backend-api-monitor
namespace: monitoring
labels:
release: monitoring # Phải khớp với nhãn mà instance Prometheus của bạn yêu cầu
spec:
selector:
matchLabels:
app: backend-api
namespaceSelector:
matchNames:
- production
endpoints:
- port: http-metrics
interval: 15s
path: /metrics
Sau khi bạn áp dụng cấu hình này, Operator sẽ cập nhật cấu hình Prometheus ngay lập tức. Không cần khởi động lại và không cần can thiệp thủ công. Mọi thứ hoạt động trơn tru.
3. Cảnh báo khai báo với PrometheusRule
Việc quản lý cảnh báo từng là một đống hỗn độn các ConfigMap lồng nhau. Với PrometheusRule, chúng tôi định nghĩa các cảnh báo ngay bên cạnh mã nguồn. Nếu một dịch vụ có độ trễ cao, định nghĩa cảnh báo sẽ nằm trong cùng một kho lưu trữ Git với mã triển khai dịch vụ đó.
apiVersion: monitoring.coreos.com/v1
kind: PrometheusRule
metadata:
name: api-latency-alerts
labels:
release: monitoring
spec:
groups:
- name: backend.rules
rules:
- alert: HighLatency
expr: job:request_latency_seconds:mean5m{job="backend-api"} > 0.5
for: 10m
labels:
severity: critical
annotations:
summary: "Độ trễ cao trên {{ $labels.instance }}"
description: "Độ trễ vượt quá 0,5 giây trong hơn 10 phút."
4. Định tuyến với Alertmanager
Operator cũng quản lý cả Alertmanager. Chúng tôi định tuyến các cảnh báo nghiêm trọng đến PagerDuty và các cảnh báo không nghiêm trọng đến Slack. Cấu hình được quản lý thông qua một Kubernetes Secret. Operator theo dõi Secret này, đảm bảo rằng các thay đổi về định tuyến được áp dụng mà không làm mất bất kỳ thông báo nào.
Những bài học thực tế
Chuyển sang Operator không chỉ là một sự nâng cấp về mặt kỹ thuật. Đó là một sự thay đổi về văn hóa hướng tới Giám sát dưới dạng mã (Monitoring as Code). Dưới đây là những bài học rút ra sau sáu tháng vận hành thực tế:
- Khớp nhãn (Label Matching) là lỗi phổ biến nhất: Hầu hết các vấn đề của
ServiceMonitorđều bắt nguồn từ việc thiếu nhãnrelease. Nếu nó không khớp với những gì Prometheus CRD mong đợi, các metric của bạn sẽ không xuất hiện. - Chú ý đến bộ nhớ: Prometheus rất “ngốn” tài nguyên. Sau 90 ngày, instance của chúng tôi đã bị lỗi OOM (Out of Memory) ở mức 8GB RAM. Chúng tôi đã phải tăng giới hạn lên 12GB và điều chỉnh chính sách lưu giữ dữ liệu (retention policy) xuống còn 15 ngày.
- Chuẩn hóa các Metric: Khuyến khích các lập trình viên tự viết monitor cho riêng họ. Tuy nhiên, hãy duy trì một kho lưu trữ trung tâm cho các quy tắc toàn cục như Node Exporter hoặc sức khỏe của cụm để tránh các cảnh báo trùng lặp.
Sự chuyển đổi này đã giúp đội ngũ của chúng tôi tiết kiệm khoảng 10 giờ làm việc thủ công mỗi tháng. Quan trọng hơn, giờ đây chúng tôi tin tưởng rằng hệ thống giám sát của mình cũng linh hoạt như chính hạ tầng của mình vậy. Nếu bạn vẫn đang chỉnh sửa cấu hình Prometheus một cách thủ công, đã đến lúc để Operator xử lý việc tự động hóa đó.

