あるひどい夜があった。本番Kubernetesクラスターが突然すべてのkubectlコマンドを受け付けなくなったのだ。API serverは動いているし、nodeもすべてgreenなのに、すべてのリクエストがタイムアウトする。犯人はetcdのdisk I/Oボトルネックで、fsync latencyが2秒以上に跳ね上がっていた。そして、そのためのアラートが一つもなかったのだ。
その障害をきっかけに、etcdのmonitoringスタック全体を作り直した。この記事は、6ヶ月間の実運用から学んだことをまとめたものだ。
クイックスタート:5分でダッシュボードを手に入れる
すでにPrometheus + Grafanaが導入済みなら(このブログのPrometheus Operatorの記事を参照)、etcd metricsはすぐに取得できる状態だ。あとはscrapeを正しく設定するだけでいい。
ステップ1:etcdのmetricsポートを開く
etcdはデフォルトで:2381/metricsにmetricsを公開する。有効になっているか確認しよう:
# control plane node上で
curl -s http://localhost:2381/metrics | grep etcd_disk_wal_fsync
出力があればetcdはmetricsを公開している。なければ、etcd configにこのflagを追加しよう(kubeadmの場合は/etc/kubernetes/manifests/etcd.yaml):
spec:
containers:
- command:
- etcd
- --listen-metrics-urls=http://0.0.0.0:2381
# ... 他のflagはそのまま
ステップ2:PrometheusにScrape Configを追加する
# 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
ステップ3:GrafanaダッシュボードをImportする
Grafana.comのDashboard ID 3070が最も標準的なetcdダッシュボードだ。すぐにImportできる:
# または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"}]}'
Grafana → Dashboards → Import → ID 3070を入力 → Prometheus datasourceを選択。これで完了だ。
etcdで最も重要な3つのMetrics
etcdは数十種類のmetricsを公開しているが、実運用を通じて、クラスターの死活を左右するのはこの3グループだと分かった:
1. Disk Fsync Latency — etcdダウンの最大の原因
etcdはdurabilityを保証するためにWAL(Write-Ahead Log)を使用している。すべてのwriteはcommit前にdiskへfsyncされなければならない。diskが遅ければ、クラスター全体が遅くなる。
# 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))
実際の閾値:
- WAL fsync < 10ms:正常、SSDは良好
- 10ms – 100ms:警告 — diskに問題が発生している
- > 100ms:クリティカル — etcdがタイムアウト寸前、Kubernetes APIが不安定になる
私が経験した障害では、IOPS burst limitの枯渇によりEBS volumeがthrottleされ、WAL fsyncが2.3秒まで達した。教訓:etcdには専用SSDが必要で、他のworkloadと共有してはいけない。
2. Raft Proposals — etcdクラスターの鼓動
Raftはetcdのconsensusアルゴリズムだ。Proposalsとは、Raftを通じて提案およびcommitされるトランザクションのことだ。
# Proposal rate(トランザクション/秒)
rate(etcd_server_proposals_committed_total[5m])
# Proposal failures — これは危険なmetric
rate(etcd_server_proposals_failed_total[5m])
# 待機中のProposals(キュー)
etcd_server_proposals_pending
最も注目するのはproposals_pendingだ。この数値が下がることなく増え続けているなら、etcdが処理に追いつけていないということだ。たいていはdiskかmember間のnetworkがボトルネックになっている。
# アラート:5分間継続してproposal failuresが発生
sum(rate(etcd_server_proposals_failed_total[5m])) > 0
3. Leader Changes — クラスター不安定のサイン
3ノードのetcdクラスターには常に1つのleaderがある。Leader changeは、旧leaderがタイムアウトまたはクラッシュしたときに発生する。1日に数回のleader changeは正常だ(rolling restart時など)。しかし、頻繁なleader changeは非常に悪いサインだ。
# leader changeの回数
changes(etcd_server_leader_changes_seen_total[1h])
# 現在のnodeがleaderかどうか(1 = leader、0 = follower)
etcd_server_is_leader
以前、1つのnodeが残り2つのnodeとの間でnetwork packet loss >5%になったために、クラスターが1時間に50回以上leader changeした事例を見たことがある。Kubernetes APIはダウンしなかったが、latencyが急上昇し、kubectlが断続的にエラーを返した。
実際のアラート設定
ここが最も時間をかけた部分だ。alert fatigueはmonitoring初期設定時の問題だった。最初は閾値を低く設定しすぎてAlertmanagerが頻繁に発火し、徐々に誰もがアラートを無視するようになってしまった。適切な閾値に行き着くまで何度も調整が必要だった。
# 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が高い
- 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: "{{ $labels.instance }}でetcd WAL fsync latencyが高い"
description: "P99 WAL fsync = {{ $value | humanizeDuration }}、diskが過負荷状態の可能性がある"
# Proposal failures
- alert: EtcdProposalsFailing
expr: sum(rate(etcd_server_proposals_failed_total[5m])) by (instance) > 0
for: 5m
labels:
severity: critical
annotations:
summary: "{{ $labels.instance }}でetcd proposalsが失敗している"
description: "{{ $value | humanize }} proposals/s が失敗 — クラスターがquorumを失う可能性がある"
# Leader changesが多すぎる
- alert: EtcdHighNumberOfLeaderChanges
expr: changes(etcd_server_leader_changes_seen_total[1h]) > 3
labels:
severity: warning
annotations:
summary: "etcd leaderが頻繁に変更されている"
description: "1時間で{{ $value }}回のleader change — node間のnetworkとdiskを確認すること"
# leaderが消失
- alert: EtcdNoLeader
expr: sum(etcd_server_is_leader) == 0
for: 1m
labels:
severity: critical
annotations:
summary: "etcdクラスターにleaderがいない!"
description: "クラスターがquorumを失い、Kubernetes APIが機能しなくなる"
kubectl apply -f etcd-alerts.yaml
本番環境から学んだ実践Tips
etcd専用のdiskを用意する
VM/cloudを使う場合は、etcdのdata directory(/var/lib/etcd)用に専用ドライブをmountする。AWSではio2で最低3000 IOPS、bare metalではNVMe SSDを使う。etcdとcontainerdで同じドライブを共有しないこと — container imageのpullがetcd fsyncとIOPSを奪い合うことになる。
etcd member間のnetwork latencyを監視する
# etcd peer間のround-trip time
histogram_quantile(0.99, sum(rate(etcd_network_peer_round_trip_time_seconds_bucket[5m])) by (le, To))
member間のRTTが150msを超えると、Raft heartbeat timeoutが発生し始める。etcd nodeは理想的には同じAZまたは近いdatacenterに配置すべきだ。
DB size — 見落とされがちだが重要なmetric
# etcd DBの実際のsize
etcd_mvcc_db_total_size_in_bytes
# compact後のDB size
etcd_mvcc_db_total_size_in_use_in_bytes
etcdはDBを8GB未満に保つことを推奨している。超えると、パフォーマンスが大幅に低下する。auto-compactを設定しておこう:
# etcd flagsに追加する
- --auto-compaction-mode=periodic
- --auto-compaction-retention=1h
何かを変更する前にバックアップを取る
# メンテナンス前にetcdをsnapshotする
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
6時間ごとにcronjobを設定して7日分のバックアップを保持している。小規模なKubernetesクラスター(<1000 resources)では、snapshotファイルは通常10〜50MBだ。
本当に心配すべき状況
実運用から生まれた、etcdアラートを受けたときのチェックリストだ:
- WAL fsync P99 > 500ms が10分間継続:I/O contentionを引き起こしているアプリを再起動するか、IOPSを即座にスケールする
- Proposal failures > 0:まず3つのetcd member間のnetwork connectivityを確認する
- Leader change >5回/時間:
etcdctl endpoint health --clusterを実行して不安定なnodeを特定する - DB size > 6GB:今すぐdefragmentする:
etcdctl defrag --cluster
etcdはKubernetesの中で、失うとクラスター全体のstateを失う唯一のコンポーネントだ。バックアップがなければ復元できない。良いmonitoringとは多くのアラートを出すことではなく、問題が障害になる前に検知することだ。

