← Back to notes

etcd: хранилище состояния
кластера Kubernetes


22: Controller Manager | 24: SLA/SLO/RPO/RTO


Роль etcd в кластере

etcd — единственный источник истины для всего состояния кластера. Каждый объект Kubernetes (Pod, Service, Deployment, Secret, ConfigMap) хранится в etcd. API server — единственный компонент, который напрямую читает и пишет в etcd. Все остальные компоненты общаются только через API server.

При деградации etcd API server может отвечать на GET (из watch cache), но write path, reconciliation и scheduling ломаются.

source: kubernetes.io/docs/concepts/overview/components/#etcd


Raft consensus protocol

etcd использует Raft для обеспечения consistency между members кластера. Все members видят одну и ту же последовательность операций.

source: kubernetes.io/docs/tasks/administer-cluster/configure-upgrade-etcd/

Роли

РольЧто делает
Leaderпринимает все writes, реплицирует на followers, решает когда commit
Followerполучает entries от leader, пишет в WAL, отправляет ack
Candidatefollower, не получивший heartbeat от leader, начинает election

Все writes идут через leader. Followers не принимают writes напрямую. Если leader недоступен — writes невозможны до завершения election (~1-3 секунды).


Полный write path

kubectl apply (write)
  
  
API server
    authn  authz  APF  admission  validation
  
  
etcd client (gRPC)
  
  
etcd leader
  
  ├── 1. Append to WAL (Write-Ahead Log)
  
  ├── 2. fsync WAL to disk
  
  ├── 3. Replicate entry to followers (Raft)
        
        
        Follower-1: append WAL  fsync  ack ──┐
        Follower-2: append WAL  fsync  ack ──┤
                                                
        ◄──── quorum acks received ─────────────┘
  
  ├── 4. Commit (entry считается committed)
  
  ├── 5. Apply to state machine (boltdb)
  
  └── 6. Response  API server  200 OK to client

Каждый write проходит через Raft consensus — гарантия, что данные не потеряются при отказе одного member.


Quorum arithmetic

Quorum = majority = (N/2) + 1 members должны подтвердить write.

Размер кластераQuorumДопустимые потериКомментарий
110нет fault tolerance
321минимальный HA
431хуже 3: та же tolerance, больше overhead
532production recommendation
743редко нужен, больше latency

Почему 4 members хуже 3

Кластер из 3:  quorum = 2, допустимые потери = 1
Кластер из 4:  quorum = 3, допустимые потери = 1

Та же fault tolerance, но:
  - каждый write требует ack от 3 вместо 2
  - больше latency на consensus
  - больше шансов, что slow member замедлит commit

Правило: всегда нечётное число members. Чётное количество не даёт выигрыша в fault tolerance, но увеличивает overhead.


Потеря quorum: каскад последствий

СитуацияWrite pathRead pathReconciliation
Quorum intactработаетработаетработает
1 member down (из 3)работает (2/3)работаетработает
Leader down, quorum естьпауза ~1-3s (election)может работатьпауза на election
2 members down (из 3)сломанstale reads возможныостановлена
Полная потеря quorumсломансломан или staleостановлена

При потере quorum начинается каскад:

Quorum lost
  
  ├── Writes fail
       ├── Lease renewals fail
            ├── Leader election controller-manager ломается
            └── Leader election scheduler ломается
       ├── Node heartbeats не записываются
            └── Node controller считает nodes unreachable
       └── Новые Pod-ы не создаются
  
  └── Watch events останавливаются
        └── Controllers не видят изменений
              └── Reconciliation замирает

API server может продолжать отвечать на GET (из watch cache), создавая иллюзию работающего кластера.


Чувствительность к disk latency

Каждый Raft entry записывается в WAL и fsync-ается на диск до отправки ack. Это самый чувствительный участок.

fsync latency → состояние кластера

fsync P99Состояние
< 10msнормальная работа
10-20mswrites замедляются, leader начинает отставать
20-50msheartbeat-ы пропускаются, возможен leader churn
50ms
leader election каждые несколько секунд, кластер нестабилен

Почему HDD и network-attached storage опасны: нестабильный IOPS означает скачки fsync latency. Один spike до 100ms может вызвать пропущенный heartbeat и ненужный leader election.

Network RTT между members

RTTСостояние
< 2msидеально (same rack / same AZ)
2-10msнормально для cross-AZ в одном регионе
10-50msдопустимо, write latency заметно растёт
50ms
consensus замедляется критично

Цепочка деградации: от диска до пользователя

Slow disk
  
  
Delayed fsync (WAL write медленный)
  
  
Delayed commit (ack от followers приходит позже)
  
  
Delayed watch event (API server получает notify позже)
  
  
Controller видит изменение позже  reconcile lag
  
  
Scheduler видит Pod позже  placement delay
  
  
API write latency растёт  user-facing degradation

Один медленный диск на одном etcd member может замедлить весь control plane.


Сценарии отказов

Scenario 1: Slow disk на одном member

Симптомы: один follower не успевает ack-нуть. Если quorum есть — writes проходят, но latency растёт. Если slow member — leader — деградация немедленная.

Обнаружение:

  • etcd_disk_wal_fsync_duration_seconds P99 > 10ms на конкретном member
  • etcd_server_slow_apply_total растёт
  • etcd_disk_backend_commit_duration_seconds растёт

Scenario 2: Leader churn

Симптомы: leader меняется каждые несколько секунд. Каждый election — пауза writes на 1-3s.

Причины: disk fsync spikes, network packet loss, CPU/memory contention на node с leader.

Обнаружение: etcd_server_leader_changes_seen_total > 0 за короткий период.

Scenario 3: Network partition

         ┌──── network partition ────┐
                                    
    member-1 (leader)          member-2
    member-3                         
                                    
    quorum = 2/3              alone = 1/3 
    writes работают            writes fail

API servers, подключённые к minority partition, отвечают на GET, но все writes fail-ятся.

Scenario 4: Полная потеря quorum

Симптомы: все writes fail. API server отдаёт 500 или timeout на mutating requests.

Последствия:

  • Новые Pod-ы не создаются
  • Lease renewals fail → leader election ломается
  • Node heartbeats не записываются → node controller считает все nodes unreachable
  • Кластер начинает "забывать" свои nodes

Maintenance операции

ОперацияЗачемРиск
Compactionудаляет старые revisions, освобождает местобез неё db растёт до quota alarm
Defragmentationвозвращает свободное место на диск после compactionблокирует member на время выполнения; делать по одному
Snapshotbackup state для disaster recoveryоснова restore; без него RPO = infinity
Quota alarmetcd блокирует writes при достижении лимитакластер read-only до ручного вмешательства

Compaction и defragmentation

# Compaction: удалить revisions старше текущего
ETCDCTL_API=3 etcdctl compact \
  $(ETCDCTL_API=3 etcdctl endpoint status --write-out="json" \
    --endpoints=https://etcd-1:2379 \
    --cacert=/etc/etcd/ca.crt \
    --cert=/etc/etcd/client.crt \
    --key=/etc/etcd/client.key \
    | jq '.[0].Status.header.revision')

# Defragmentation: вернуть место на диск (по одному member!)
ETCDCTL_API=3 etcdctl defrag \
  --endpoints=https://etcd-1:2379 \
  --cacert=/etc/etcd/ca.crt \
  --cert=/etc/etcd/client.crt \
  --key=/etc/etcd/client.key

ETCDCTL_API=3 etcdctl defrag \
  --endpoints=https://etcd-2:2379 \
  --cacert=/etc/etcd/ca.crt \
  --cert=/etc/etcd/client.crt \
  --key=/etc/etcd/client.key

API server выполняет auto-compaction по умолчанию (каждые 5 минут, флаг --etcd-compaction-interval). Defragmentation — ручная операция, требует последовательного выполнения (не одновременно на всех members).

Snapshot и restore

# Создать snapshot
ETCDCTL_API=3 etcdctl snapshot save /backup/etcd-snapshot-$(date +%Y%m%d-%H%M%S).db \
  --endpoints=https://etcd-1:2379 \
  --cacert=/etc/etcd/ca.crt \
  --cert=/etc/etcd/client.crt \
  --key=/etc/etcd/client.key

# Проверить snapshot
ETCDCTL_API=3 etcdctl snapshot status \
  /backup/etcd-snapshot.db \
  --write-out=table

# Restore (создаёт новый data directory)
ETCDCTL_API=3 etcdctl snapshot restore /backup/etcd-snapshot.db \
  --data-dir=/var/lib/etcd-restored \
  --name=etcd-1 \
  --initial-cluster=etcd-1=https://etcd-1:2380,etcd-2=https://etcd-2:2380,etcd-3=https://etcd-3:2380 \
  --initial-cluster-token=etcd-cluster-new \
  --initial-advertise-peer-urls=https://etcd-1:2380

Restore создаёт новый кластер с данными из snapshot. Все данные, записанные после snapshot — потеряны. Выполнять restore нужно на всех members с одним snapshot файлом.

Quota alarm и разблокировка

# Проверить текущий размер и quota
ETCDCTL_API=3 etcdctl endpoint status --write-out=table \
  --endpoints=https://etcd-1:2379,https://etcd-2:2379,https://etcd-3:2379 \
  --cacert=/etc/etcd/ca.crt \
  --cert=/etc/etcd/client.crt \
  --key=/etc/etcd/client.key

# Снять alarm после compaction + defrag
ETCDCTL_API=3 etcdctl alarm disarm \
  --endpoints=https://etcd-1:2379 \
  --cacert=/etc/etcd/ca.crt \
  --cert=/etc/etcd/client.crt \
  --key=/etc/etcd/client.key

Quota alarm workflow:

etcd db size  приближается к quota (default 2GB)
  
  
Quota alarm triggered
  
  
etcd блокирует ВСЕ writes
  
  
API server: все mutating requests fail
  
  
Ручное вмешательство:
  1. etcdctl compact (удалить старые revisions)
  2. etcdctl defrag (вернуть место на диск)
  3. etcdctl alarm disarm (снять alarm)

Sizing recommendations

ПараметрРекомендация
Members3 (small-medium), 5 (large production)
DiskSSD, dedicated (не shared с другими workloads)
Network< 10ms RTT между members
Memory8-16GB (зависит от db size)
CPU2-4 cores dedicated
DB quotadefault 2GB, можно увеличить до 8GB

Критично: etcd на shared disk с другими workloads — главная причина нестабильности. Диск должен быть dedicated и SSD. NVMe предпочтительнее SATA SSD.

source: kubernetes.io/docs/tasks/administer-cluster/configure-upgrade-etcd/#hardware-recommendations


Влияние на три пути control plane

PathКак проявляется деградация etcd
Request path (API server)write latency растёт, mutating requests timeout-ятся; GET может работать из кэша
Placement path (scheduler)bind operation fail-ится (bind = write в etcd); pending pods растут
Reconciliation path (controller-manager)controller actions fail-ятся, workqueue retries растут, convergence останавливается

Сигналы мониторинга

Disk и storage

МетрикаЧто показываетПорог
etcd_disk_wal_fsync_duration_secondsскорость записи в WALP99 > 10ms warning, > 25ms critical
etcd_disk_backend_commit_duration_secondsскорость commit в boltdbP99 > 25ms warning
etcd_mvcc_db_total_size_in_bytesразмер базы
80% quota → compaction needed
etcd_server_quota_backend_bytesлимит базыприближение → alarm скоро

Consensus и leadership

МетрикаЧто показываетПорог
etcd_server_leader_changes_seen_totalчастота смены leader
1 за 10 мин → investigate
etcd_server_is_leaderкто leader0 на всех → no leader, critical
etcd_server_proposals_failed_totalнеудачные Raft proposalsrate > 0 sustained → quorum problem
etcd_server_proposals_pendingproposals, ждущие commit
5 sustained → consensus bottleneck

Network

МетрикаЧто показываетПорог
etcd_network_peer_round_trip_time_secondsRTT между membersP99 > 50ms warning
etcd_network_peer_sent_failures_totalошибки отправки между membersrate > 0 → network problem

Что мониторить в первую очередь:

# etcd жив и имеет leader?
etcd_server_is_leader == 1 (ровно на одном member)

# Диск справляется?
etcd_disk_wal_fsync_duration_seconds P99 < 10ms

# Leader стабилен?
rate(etcd_server_leader_changes_seen_total) == 0 за последние 10 минут

# База не переполнена?
etcd_mvcc_db_total_size_in_bytes / etcd_server_quota_backend_bytes < 0.8

Когда использовать

3-member etcd:

  • Dev/staging кластеры, production с небольшой нагрузкой.
  • Выживает потерю 1 member. Достаточно для большинства prod кластеров.

5-member etcd:

  • Критичные production кластеры, где важна устойчивость к потере 2 members.
  • Multi-AZ deployments: по одному member на AZ + 2 в основной AZ.
  • Больше overhead на каждый write, но higher fault tolerance.

Dedicated etcd nodes:

  • Всегда для production. etcd не должен делить диск с kubelet, containerd или другими workloads.
  • Taints + tolerations или dedicated node group.

Snapshot frequency:

  • Минимум — ежедневный automated snapshot. Лучше — каждый час.
  • Хранить off-cluster (S3, GCS). Snapshot на том же диске что и etcd бесполезен при disk failure.
  • Регулярно тестировать restore.

Типичные ошибки

1. etcd на shared SSD с другими workloads

Если kubelet или containerd создают интенсивный disk I/O, fsync latency etcd вырастет. Leader churn, нестабильный control plane. Решение: dedicated disk для etcd или dedicated nodes.

2. Чётное количество members (2 или 4)

2 members = quorum=2, допустимые потери=0. Хуже, чем 1 member (нет fault tolerance + write overhead). 4 members = quorum=3, та же fault tolerance что у 3-member кластера. Всегда нечётное количество.

3. Не тестировать restore

Snapshot создаётся, но restore никогда не тестировался. При реальной аварии оказывается, что snapshot corrupted или процедура restore незнакома команде. Регулярный drill restore — обязателен.

4. Игнорировать etcd_server_leader_changes_seen_total

Один leader change за сутки — нормально (плановые операции). Несколько за час — признак нестабильности (disk latency spikes, network issues). Без alert-а на этот counter проблема остаётся незамеченной.

5. Defrag на всех members одновременно

Defragmentation блокирует member на время выполнения. Одновременный defrag на 2 из 3 members уничтожает quorum. Всегда по одному, с паузой между операциями.

6. Не следить за размером базы

etcd растёт постепенно. Quota alarm срабатывает неожиданно, кластер уходит в read-only. Настройте alert на etcd_mvcc_db_total_size_in_bytes > 80% quota.


Альтернативы

etcd в production Kubernetes не заменяется — это требование. Контекст:

Managed etcd (EKS/GKE/AKS): провайдер управляет backup, upgrade, failover. Снимает operational overhead, ограничивает visibility.

k3s с SQLite/PostgreSQL: lightweight Kubernetes для edge/IoT с одним control plane node. Не для production HA.

kine: shim для работы API server с PostgreSQL/MySQL. Используется в k3s. Теряет часть etcd-гарантий (watch semantics), снижает operational complexity для небольших кластеров.


22: Controller Manager | 24: SLA/SLO/RPO/RTO

etcd: хранилище состояния кластера Kubernetes | Aleksandr Suprun