Hướng dẫn giám sát cụm etcd với Prometheus và Grafana: Theo dõi Disk Fsync Latency, Raft Proposals và Leader Changes để bảo vệ Kubernetes Control Plane

Monitoring tutorial - IT technology blog
Monitoring tutorial - IT technology blog

Mình đã có một buổi tối rất tệ khi cụm Kubernetes production bỗng nhiên không nhận lệnh kubectl nào nữa. API server vẫn chạy, nodes vẫn green, nhưng mọi request đều timeout. Thủ phạm: etcd bị bottleneck disk I/O đến mức fsync latency vọt lên hơn 2 giây. Và mình không có alert nào cho điều đó.

Sau sự cố đó, mình rebuild toàn bộ monitoring stack cho etcd. Bài này là tóm tắt những gì mình học được sau 6 tháng chạy thực tế.

Quick Start: Có Dashboard trong 5 Phút

Nếu bạn đã có Prometheus + Grafana (xem bài Prometheus Operator trên blog này), thì etcd metrics đã sẵn sàng — chỉ cần cấu hình scrape đúng chỗ.

Bước 1: Mở port metrics trên etcd

etcd mặc định expose metrics ở :2381/metrics. Kiểm tra xem đã bật chưa:

# Trên control plane node
curl -s http://localhost:2381/metrics | grep etcd_disk_wal_fsync

Nếu có output thì etcd đang expose. Nếu không, thêm flag này vào etcd config (/etc/kubernetes/manifests/etcd.yaml với kubeadm):

spec:
  containers:
  - command:
    - etcd
    - --listen-metrics-urls=http://0.0.0.0:2381
    # ... các flag khác giữ nguyên

Bước 2: Thêm scrape config vào Prometheus

# prometheus-etcd-scrape.yaml
apiVersion: monitoring.coreos.com/v1
kind: ServiceMonitor
metadata:
  name: etcd
  namespace: monitoring
  labels:
    release: prometheus
spec:
  endpoints:
  - interval: 30s
    port: metrics
    scheme: http
    path: /metrics
  namespaceSelector:
    matchNames:
    - kube-system
  selector:
    matchLabels:
      component: etcd
kubectl apply -f prometheus-etcd-scrape.yaml

Bước 3: Import Grafana Dashboard

Dashboard ID 3070 trên Grafana.com là dashboard etcd chuẩn nhất. Import nhanh:

# Hoặc dùng API
curl -X POST http://admin:password@localhost:3000/api/dashboards/import \
  -H 'Content-Type: application/json' \
  -d '{"dashboard":{"id":3070},"overwrite":true,"inputs":[{"name":"DS_PROMETHEUS","type":"datasource","pluginId":"prometheus","value":"Prometheus"}]}'

Vào Grafana → Dashboards → Import → nhập ID 3070 → chọn Prometheus datasource. Xong.

Ba Metrics Quan Trọng Nhất Của etcd

Có hàng chục metrics etcd expose ra, nhưng qua thực tế mình thấy 3 nhóm này quyết định sống còn của cluster:

1. Disk Fsync Latency — Thủ phạm số 1 gây sập etcd

etcd dùng WAL (Write-Ahead Log) để đảm bảo durability. Mỗi write phải fsync xuống disk trước khi commit. Nếu disk chậm, toàn bộ cluster chậm theo.

# P99 WAL fsync latency
histogram_quantile(0.99, sum(rate(etcd_disk_wal_fsync_duration_seconds_bucket[5m])) by (le))

# P99 Backend commit latency (bbolt)
histogram_quantile(0.99, sum(rate(etcd_disk_backend_commit_duration_seconds_bucket[5m])) by (le))

Ngưỡng thực tế:

  • WAL fsync < 10ms: Bình thường, SSD tốt
  • 10ms – 100ms: Cảnh báo — disk đang có vấn đề
  • > 100ms: Critical — etcd sắp bị timeout, Kubernetes API sẽ bất ổn

Lần sự cố của mình, WAL fsync lên tới 2.3 giây vì EBS volume bị throttle do IOPS burst limit hết. Bài học: etcd cần dedicated SSD, không dùng chung với workload khác.

2. Raft Proposals — Nhịp tim của etcd cluster

Raft là thuật toán consensus của etcd. Proposals là các giao dịch được đề xuất và committed qua Raft.

# Proposal rate (giao dịch/giây)
rate(etcd_server_proposals_committed_total[5m])

# Proposal failures — đây là metric nguy hiểm
rate(etcd_server_proposals_failed_total[5m])

# Proposals đang chờ (queued)
etcd_server_proposals_pending

Điều mình hay nhìn nhất là proposals_pending — nếu con số này tăng liên tục mà không giảm, nghĩa là etcd đang không xử lý kịp. Thường do disk hoặc network giữa các members bị bottleneck.

# Alert: proposal failures liên tục trong 5 phút
sum(rate(etcd_server_proposals_failed_total[5m])) > 0

3. Leader Changes — Dấu hiệu cluster bất ổn

etcd cluster 3 nodes luôn có 1 leader. Leader change xảy ra khi leader cũ timeout hoặc crash. Một vài lần leader change mỗi ngày là bình thường (ví dụ rolling restart). Nhưng leader change liên tục là dấu hiệu rất xấu.

# Số lần leader change
changes(etcd_server_leader_changes_seen_total[1h])

# Node hiện tại có phải leader không (1 = leader, 0 = follower)
etcd_server_is_leader

Mình từng thấy cluster change leader 50+ lần trong 1 tiếng vì 1 node bị network packet loss >5% với 2 node còn lại. Kubernetes API không sập nhưng latency tăng vọt và kubectl thỉnh thoảng trả về lỗi.

Cấu Hình Alert Thực Tế

Đây là phần mình tốn nhiều thời gian nhất. Alert fatigue là vấn đề mình gặp khi mới setup monitoring — ban đầu set threshold quá thấp, Alertmanager bắn liên tục, dần dần mọi người bắt đầu ignore alert. Phải tune đi tune lại mới ra ngưỡng hợp lý.

# etcd-alerts.yaml
apiVersion: monitoring.coreos.com/v1
kind: PrometheusRule
metadata:
  name: etcd-alerts
  namespace: monitoring
spec:
  groups:
  - name: etcd.rules
    rules:
    # WAL fsync latency cao
    - alert: EtcdHighFsyncLatency
      expr: |
        histogram_quantile(0.99,
          sum(rate(etcd_disk_wal_fsync_duration_seconds_bucket[5m])) by (le, instance)
        ) > 0.1
      for: 10m
      labels:
        severity: warning
      annotations:
        summary: "etcd WAL fsync latency cao trên {{ $labels.instance }}"
        description: "P99 WAL fsync = {{ $value | humanizeDuration }}, disk có thể đang quá tải"

    # Proposal failures
    - alert: EtcdProposalsFailing
      expr: sum(rate(etcd_server_proposals_failed_total[5m])) by (instance) > 0
      for: 5m
      labels:
        severity: critical
      annotations:
        summary: "etcd proposals đang thất bại trên {{ $labels.instance }}"
        description: "{{ $value | humanize }} proposals/s thất bại — cluster có thể mất quorum"

    # Leader changes quá nhiều
    - alert: EtcdHighNumberOfLeaderChanges
      expr: changes(etcd_server_leader_changes_seen_total[1h]) > 3
      labels:
        severity: warning
      annotations:
        summary: "etcd leader thay đổi quá nhiều lần"
        description: "{{ $value }} lần leader change trong 1 giờ — kiểm tra network và disk giữa các nodes"

    # Mất leader
    - alert: EtcdNoLeader
      expr: sum(etcd_server_is_leader) == 0
      for: 1m
      labels:
        severity: critical
      annotations:
        summary: "etcd cluster không có leader!"
        description: "Cluster mất quorum, Kubernetes API sẽ không hoạt động"
kubectl apply -f etcd-alerts.yaml

Tips Thực Tế Từ Production

Tách riêng disk cho etcd

Nếu dùng VM/cloud, mount một ổ riêng cho etcd data directory (/var/lib/etcd). Trên AWS dùng io2 với ít nhất 3000 IOPS. Trên bare metal, NVMe SSD. Đừng để etcd và containerd dùng chung một ổ — container image pull sẽ tranh IOPS với etcd fsync.

Giám sát network latency giữa etcd members

# Round-trip time giữa etcd peers
histogram_quantile(0.99, sum(rate(etcd_network_peer_round_trip_time_seconds_bucket[5m])) by (le, To))

Nếu RTT giữa các member vượt 150ms, Raft heartbeat timeout sẽ bắt đầu xảy ra. Các etcd nodes lý tưởng nên ở cùng AZ hoặc datacenter gần nhau.

DB size — metric ít ai theo dõi nhưng quan trọng

# etcd DB size thực tế
etcd_mvcc_db_total_size_in_bytes

# DB size sau khi compact
etcd_mvcc_db_total_size_in_use_in_bytes

etcd recommend giữ DB dưới 8GB. Nếu vượt quá, hiệu năng giảm đáng kể. Nên cấu hình auto-compact:

# Thêm vào etcd flags
- --auto-compaction-mode=periodic
- --auto-compaction-retention=1h

Backup trước khi thay đổi bất cứ gì

# Snapshot etcd trước mọi maintenance
ETCDCTL_API=3 etcdctl snapshot save /backup/etcd-$(date +%Y%m%d-%H%M%S).db \
  --endpoints=https://127.0.0.1:2379 \
  --cacert=/etc/kubernetes/pki/etcd/ca.crt \
  --cert=/etc/kubernetes/pki/etcd/server.crt \
  --key=/etc/kubernetes/pki/etcd/server.key

Mình set cronjob chạy mỗi 6 tiếng và giữ 7 ngày backup. Với cluster Kubernetes nhỏ (<1000 resources), file snapshot thường chỉ 10-50MB.

Khi Nào Cần Lo Lắng Thật Sự

Qua thực tế, đây là checklist mình dùng khi nhận alert etcd:

  • WAL fsync P99 > 500ms liên tục 10 phút: Restart ứng dụng đang gây I/O contention, hoặc scale IOPS ngay
  • Proposal failures > 0: Kiểm tra network connectivity giữa 3 etcd members trước
  • Leader change >5 lần/giờ: Chạy etcdctl endpoint health --cluster để xem node nào bất ổn
  • DB size > 6GB: Defragment ngay: etcdctl defrag --cluster

etcd là thành phần duy nhất trong Kubernetes mà nếu mất là mất toàn bộ cluster state. Không phục hồi được nếu không có backup. Monitoring tốt không phải để alert nhiều, mà để bạn phát hiện vấn đề trước khi nó trở thành sự cố.

Share: