PodMonitoring CRD: Cấu Hình Nâng Cao & Kiểm Soát Scrape
Vì sao đây là điểm kiểm soát quan trọng nhất trong toàn bộ pipeline
File 01 đã thiết lập một sự thật vận hành cốt lõi: một khi dữ liệu đã ghi vào Monarch, bạn không còn cách nào "xóa bớt" cardinality một cách rẻ tiền — bạn phải xóa cả metric descriptor (một thao tác nặng, ảnh hưởng lịch sử dữ liệu) thay vì chỉ đơn giản dừng ghi thêm. Điều này biến PodMonitoring/ClusterPodMonitoring — nơi cấu hình ScrapeEndpoint và metricRelabeling — thành tuyến phòng thủ duy nhất và cuối cùng trước khi một sample rời khỏi cluster.
Nói cách khác: mọi sai lầm về cardinality, label thiết kế sai, hay volume quá lớn đều có thể được be lại tại đây với chi phí gần như bằng không (chỉnh một CRD), trong khi nếu để lọt qua, chi phí sửa chữa (xóa metric descriptor, chờ quota per-project reset) cao hơn nhiều bậc. Vì vậy phần này không phải là "tài liệu tham khảo field YAML" thông thường — nó là phần kiến trúc quyết định độ an toàn cost và hiệu năng của toàn hệ thống downstream.
Cấu trúc PodMonitoringSpec: nhìn theo luồng xử lý, không chỉ theo field
Theo tài liệu API reference của prometheus-engine (GoogleCloudPlatform/prometheus-engine doc/api.md), PodMonitoringSpec (namespace-scoped) và ClusterPodMonitoringSpec (cluster-scoped) có cấu trúc gần như tương đương, khác nhau chủ yếu ở phạm vi selector (một cái chỉ chọn Pod trong namespace của chính CRD, một cái chọn Pod ở mọi namespace):
apiVersion: monitoring.googleapis.com/v1
kind: PodMonitoring
metadata:
name: checkout-service-metrics
namespace: production
spec:
selector:
matchLabels:
app: checkout-service
filterRunning: true # mặc định true: bỏ qua Pod ở phase Failed/Succeeded
endpoints:
- port: metrics
interval: 30s
timeout: 10s
path: /metrics
scheme: http
params:
format: ["prometheus"]
metricRelabeling:
- action: drop
sourceLabels: [__name__]
regex: "checkout_debug_.+"
limits:
samples: 3000
labels: 30
labelNameLength: 100
labelValueLength: 200
targetLabels:
fromPod:
- from: version
to: app_versionHãy đọc cấu trúc này theo đúng thứ tự dữ liệu đi qua nó, vì mỗi field chặn một loại rủi ro khác nhau:
Bước 1 — selector và filterRunning: xác định "ai được scrape"
selector dùng label matching chuẩn Kubernetes để chọn Pod. Điểm dễ bị bỏ qua: filterRunning mặc định là true, nghĩa là Pod ở phase Failed hoặc Succeeded (ví dụ Job đã chạy xong) sẽ không bị scrape. Đây là quyết định thiết kế hợp lý — scrape một Pod đã chết không tạo ra dữ liệu có ý nghĩa và chỉ tốn thêm request. Nhưng nó cũng là nguồn gốc của một class lỗi phổ biến: metric "biến mất" từ short-lived Job vì Job hoàn thành trước khi scrape interval kịp lấy mẫu — tài liệu troubleshooting chính thức khuyến nghị đảm bảo target chạy tối thiểu 5 phút để có đủ cơ hội được scrape ổn định.
Bước 2 — ScrapeEndpoint: interval, timeout, path, params
Ba field interval/timeout/path tưởng đơn giản nhưng có hai ràng buộc dễ vi phạm:
Ràng buộc 1 — timeout phải nhỏ hơn interval. Nếu vi phạm, GMP trả lỗi rõ ràng "scrape timeout greater than scrape interval" ngay tại admission webhook, không để lỗi lọt xuống runtime.
Ràng buộc 2 — minimum interval là 5 giây. Đây không phải giới hạn tùy tiện của CRD mà là giới hạn ghi của chính Monarch: write rate tối đa cho một time series là một điểm mỗi 5 giây (Cloud Monitoring quotas and limits). Nếu bạn cấu hình interval: 2s, dữ liệu vẫn được thu thập nhưng khi export lên Monarch sẽ phát sinh lỗi "Points were written more frequently than maximum sampling period" — một trong những lỗi ingestion phổ biến nhất theo tài liệu troubleshooting chính thức.
path và query parameter — bẫy URL-encoding. Nếu bạn viết path: /metrics/detailed?family=queue_metrics, dấu ? bị URL-encode thành %3F và toàn bộ path trở nên vô hiệu. Cách đúng là tách query parameter ra field riêng:
path: /metrics/detailed
params:
family: ["queue_metrics", "queue_consumer_count"]Đây là lỗi cấu hình âm thầm nguy hiểm nhất trong nhóm này — không có error rõ ràng, chỉ đơn giản là target trả về 404 hoặc metric rỗng, và người vận hành thường debug sai hướng (nghi ngờ RBAC, network policy) trước khi phát hiện ra nguyên nhân là encoding.
Bước 3 — metricRelabeling: tuyến lọc chủ động, không phải tùy chọn
metricRelabeling áp dụng sau khi scrape, trước khi ghi vào Monarch — đây chính xác là điểm bạn có quyền quyết định metric nào đáng để trả tiền lưu trữ. Theo blog kỹ thuật chính thức (Controlling metric flow in Managed Service for Prometheus), có hai chiến lược đối lập:
Denylist (loại bỏ các metric biết trước là không cần):
metricRelabeling:
- action: drop
sourceLabels: [__name__]
regex: "foo_(bar|baz|qux)_.+"Allowlist (chỉ giữ lại danh sách đã biết là cần):
metricRelabeling:
- action: keep
sourceLabels: [__name__]
regex: "kube_(daemonset|deployment|pod|namespace|node|statefulset|persistentvolume|horizontalpodautoscaler)_.+"Allowlist an toàn hơn về mặt cost control dài hạn — khi một exporter được upgrade và thêm 50 metric mới, allowlist tự động chặn tất cả trừ khi được thêm tường minh, còn denylist để lọt mọi thứ không nằm trong danh sách cấm. Nhưng allowlist đòi hỏi bạn hiểu rõ metric nào thực sự cần dùng cho dashboard/alert — một chi phí vận hành ban đầu cao hơn.
Lọc theo label, không chỉ theo tên metric cũng là một pattern quan trọng khi một metric có giá trị nhưng một số label value của nó thì không:
metricRelabeling:
- action: drop
sourceLabels: [direction]
regex: "destination.*"Bước 4 — limits (ScrapeLimits): lưới an toàn cuối cùng, không phải công cụ thiết kế chính
Đây là field dễ bị hiểu lầm nhất. limits gồm bốn field: samples (số sample tối đa được chấp nhận mỗi lần scrape), labels (số label tối đa mỗi sample), labelNameLength, labelValueLength. Về hành vi: khi một target vượt sampleLimit trong một lần scrape, toàn bộ kết quả scrape đó bị coi là lỗi và bị loại bỏ, không phải "chỉ giữ lại phần dưới giới hạn". Đây là lý do tài liệu chính thức về self-deployed collection nhấn mạnh nên đặt sample_limit ở "giá trị rất cao, chỉ để bắt các trường hợp cấu hình sai rõ ràng" (Cost controls and attribution) — chứ không dùng nó như một cách "giới hạn cost" chủ động, vì nó không cắt bớt một cách graceful mà từ chối toàn bộ.
Tư duy đúng: metricRelabeling là công cụ thiết kế chủ động (bạn quyết định trước metric nào cần), còn limits là dây an toàn thụ động (bắt lỗi khi một exporter bất ngờ "nổ" cardinality do bug hoặc misconfiguration mà không ai lường trước). Đảo ngược vai trò hai cơ chế này — dùng sampleLimit thấp để "ép" giảm cost thay vì dùng metricRelabeling để chọn lọc — sẽ khiến toàn bộ target bị bỏ scrape một cách khó hiểu, tạo ra gap dữ liệu (metric hoàn toàn biến mất) thay vì giảm cardinality có kiểm soát.
targetLabels và protected label: ranh giới bạn không được vượt qua
targetLabels.fromPod cho phép copy một Pod label thành metric label — hữu ích để đưa thông tin như version, team, tier vào time series mà exporter gốc không tự expose. Nhưng có một ràng buộc cứng được ghi rõ trong API reference: relabeling rule không được phép override các protected target label — project_id, location, cluster, namespace, job, instance. Đây là các label mà GMP tự động gắn vào mọi time series để đảm bảo khả năng phân biệt nguồn gốc dữ liệu ở tầng Monarch (đây chính là cơ chế cho phép global query scope hoạt động đúng như mô tả ở file 01).
Nếu exporter của bạn tự expose một label trùng tên với một trong các protected label này (ví dụ chính exporter có label namespace riêng của nó, không phải Kubernetes namespace), GMP tự động đổi tên label đó thành exported_namespace để tránh xung đột. Đây là nguồn gốc phổ biến của một lớp bug khi migrate từ self-hosted Prometheus: dashboard cũ dùng namespace cho một mục đích khác, sau khi chuyển sang GMP, giá trị bị đổi tên và query cũ trả về rỗng một cách âm thầm — không có lỗi, chỉ có kết quả sai.
ClusterPodMonitoring và ranh giới RBAC
PodMonitoring là namespace-scoped: một application team có thể tự tạo resource này trong namespace của mình mà không cần quyền cluster-level, phù hợp với mô hình "mỗi team tự chịu trách nhiệm observability của service mình". ClusterPodMonitoring là cluster-scoped, chọn Pod theo label selector trên mọi namespace — dùng bởi platform team để giám sát các thành phần hệ thống dùng chung như kube-state-metrics, CoreDNS, DCGM GPU exporter.
Sự phân tách RBAC này không phải chi tiết phụ — nó là ranh giới trust boundary: nếu bạn cấp quyền tạo ClusterPodMonitoring cho một application team, họ có thể vô tình (hoặc cố ý) tạo selector chọn trúng Pod của team khác, kéo theo chi phí ingest họ không kiểm soát. Nguyên tắc vận hành đúng: chỉ platform team giữ quyền RBAC tạo ClusterPodMonitoring; application team chỉ được cấp quyền tạo PodMonitoring trong namespace của chính họ.
Bật targetStatus để chẩn đoán, nhưng phải tắt sau khi dùng xong
Một tính năng thường bị bỏ qua là OperatorConfig với targetStatus.enabled: true:
apiVersion: monitoring.googleapis.com/v1
kind: OperatorConfig
metadata:
namespace: gmp-public
name: config
features:
targetStatus:
enabled: trueKhi bật, bạn có thể chạy kubectl -n NAMESPACE describe podmonitorings/NAME để xem chính xác endpoint nào healthy, endpoint nào lỗi, và lý do lỗi cuối cùng (Last Error). Đây là công cụ chẩn đoán mạnh nhất ở tầng scrape. Nhưng tài liệu chính thức cảnh báo rõ: tính năng này verbose và tốn chi phí vận hành nếu để bật liên tục trên cluster lớn — nó tạo thêm một luồng cập nhật status object cho mọi PodMonitoring, mọi lần reconcile. Nguyên tắc đúng: bật tạm thời khi debug một sự cố cụ thể, tắt lại ngay sau khi xác định nguyên nhân.
Mẫu thiết kế: chuẩn hóa PodMonitoring theo team thay vì để mỗi team tự chế
Một anti-pattern phổ biến ở tổ chức lớn là để mỗi team tự viết PodMonitoring mà không có chuẩn chung — dẫn đến tình trạng một số team dùng interval: 5s cho mọi metric (kể cả metric không cần độ chi tiết đó), một số team không có metricRelabeling nào (export toàn bộ exporter mặc định, thường là hàng trăm metric không dùng đến). Nguyên nhân sâu xa của anti-pattern này là hiểu lầm: coi PodMonitoring như một chi tiết triển khai cấp service, thay vì một quyết định kiến trúc cấp platform ảnh hưởng trực tiếp đến hóa đơn chung.
Cách khắc phục đúng đắn là cung cấp một PodMonitoring template chuẩn (qua Helm chart nội bộ hoặc Kustomize base) với interval: 30s mặc định, một metricRelabeling allowlist cơ bản theo loại exporter phổ biến (kube-state-metrics, các client library HTTP framework chuẩn), và chỉ cho phép team override khi có lý do rõ ràng (ví dụ service cần alerting real-time với interval: 15s). Điều này biến cardinality control từ "trách nhiệm phân tán, không ai chịu trách nhiệm chung" thành một chính sách platform có thể audit.