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ố.

