ADG Оставить заявку
Блог DevOps 5 мин чтения

etcd 3.4 в Kubernetes 1.19: настраиваем под продакшн

etcd 3.4 стал дефолтным для Kubernetes 1.19. Разбираем выделенные SSD с latency <1ms, I/O scheduler и мониторинг wal_fsync_duration в Prometheus.

Контекст момента

etcd 3.4 становится дефолтным для Kubernetes 1.19, что выводит тему настройки хранилища raft-лога под продакшн-нагрузку на первый план

Когда мы обновляли кластеры до Kubernetes 1.19, одним из негромких изменений был переход на etcd 3.4 как дефолтную версию. Само по себе это не событие - 3.4 вышел ещё в 2019-м, многие уже использовали. Но несколько инцидентов с нестабильным leader election на клиентских кластерах заставили нас заново пройтись по тому, как мы вообще готовим etcd к продакшн-нагрузке. Оказалось, что некоторые детали конфигурации мы принимали как данность без особых оснований.

Почему disk latency - это и есть leader election

etcd использует Raft - распределённый консенсус-протокол, где запись подтверждается только после того, как большинство участников записали лог. Критический путь выглядит так: лидер получает запись, пишет в WAL (write-ahead log) на диск, рассылает записи фолловерам, ждёт подтверждений, коммитит.

Важный момент: fsync WAL блокирует обработку следующих запросов. Если wal_fsync_duration начинает расти - лидер перестаёт отвечать на heartbeat в срок, и кластер запускает очередные выборы. Нам попадался кластер, где на разделённом SAN-дисковом пространстве с латентностью 3-5ms etcd переизбирал лидера несколько раз в час под нагрузкой - при внешне здоровом Kubernetes. Поды, которые в этот момент делали запросы через kubectl или через API-server, получали случайные таймауты. Диагностировали не сразу, потому что на обычных графиках в Grafana всё выглядело зелёным.

etcd разработчики сами пишут в документации: нужен диск с задержкой записи менее 10ms для нормальной работы, менее 1ms - для хорошей. Разница между «нормальной» и «хорошей» в продакшне ощущается вполне конкретно.

Что делаем с дисками

Выделенный SSD под etcd. Не делить диск с containerd, kubelet, системными логами, prometheus-agent и прочим, что периодически пишет крупными блоками. На shared-диске один dd if=/dev/zero способен создать вам внеплановый leader election. Отдельный NVMe под etcd данные и WAL - хороший минимум для серьёзного кластера.

WAL и данные на разных томах. Менее обязательно, чем выделенный диск вообще, но если диск один - хотя бы убедитесь что --data-dir и --wal-dir не конкурируют за I/O с чем-то ещё. etcd по умолчанию кладёт WAL в <data-dir>/member/wal. Если хотите разнести - указываете --wal-dir отдельно при запуске.

I/O scheduler. На modern NVMe-дисках scheduler none (он же noop в старых ядрах) предпочтительнее cfq или bfq. CFQ пытается быть справедливым между процессами, но для etcd это значит что он может придержать ваш fsync, пока не обслужит чью-то большую sequential read. Проверяем текущий scheduler:

cat /sys/block/nvme0n1/queue/scheduler

Меняем:

echo none > /sys/block/nvme0n1/queue/scheduler

Для persistence между перезагрузками - через udev rules или в /etc/rc.local, в зависимости от дистрибутива. На нескольких кластерах этот переход дал ощутимое снижение p99 latency по wal_fsync_duration.

--quota-backend-bytes. По умолчанию 2GB. При достижении квоты etcd переходит в maintenance mode и перестаёт принимать записи - кластер деградирует. Для кластеров с большим количеством CRD, секретов, configmap-ов смотрим реальный размер через etcdctl endpoint status и выставляем квоту с запасом. Максимум - 8GB, больше etcd официально не рекомендует.

Мониторинг: что смотреть в Prometheus

etcd 3.4 экспортирует метрики через /metrics endpoint. Для Kubernetes-кластеров он обычно доступен на порту 2381 (по умолчанию). Ключевые метрики:

etcd_disk_wal_fsync_duration_seconds - гистограмма времени fsync WAL. Смотрим p99. Если p99 > 10ms стабильно - проблема с диском или I/O contention. Если p99 кратковременно уходит за 50-100ms - ищем что в этот момент пишет на диск.

etcd_disk_backend_commit_duration_seconds - время коммита boltdb backend. p99 должен быть в разумных пределах, но WAL fsync критичнее для latency кластера.

etcd_server_leader_changes_seen_total - счётчик смены лидера. В нормальном кластере растёт только при рестартах нод. Если растёт в idle - что-то не так с latency или сетью между etcd-нодами.

etcd_network_peer_round_trip_time_seconds - RTT между участниками. Для хорошей работы Raft нужен RTT < 10ms между нодами. Если etcd-ноды размазаны по дата-центрам с RTT 30-50ms - лучший диск не спасёт.

Пример recording rule для алертинга:

- alert: EtcdHighWalFsyncDuration
  expr: histogram_quantile(0.99, rate(etcd_disk_wal_fsync_duration_seconds_bucket[5m])) > 0.01
  for: 5m
  labels:
    severity: warning
  annotations:
    summary: "etcd WAL fsync p99 > 10ms на {{ $labels.instance }}"

Параметры heartbeat и election timeout

etcd 3.4 по умолчанию: --heartbeat-interval=100ms, --election-timeout=1000ms. Для кластеров на быстрых локальных дисках и низкой сетевой latency это нормально. Если диски медленнее или RTT между нодами выше - можно увеличить --election-timeout до 2000-5000ms, чтобы дать лидеру больше времени на медленный fsync без запуска выборов. Но это двухстороннее решение: более высокий timeout означает более долгое восстановление при реальном падении лидера. Увеличиваем осторожно, наблюдая метрики.

Где сейчас

После того как мы прошлись по этому чеклисту на клиентских кластерах под сопровождением - хаотичные timeouts ушли. Основным виновником был shared-диск на одном из кластеров и CFQ-scheduler на другом. Не rocket science, но нужно было потратить время чтобы это системно проверить, а не гасить симптомы рестартами etcd.

etcd - не та компонента, которую хочется трогать в панике посреди инцидента. Лучше один раз настроить правильно и мониторить wal_fsync_duration в фоне.

Контакт

Нужна такая же инженерная работа?

Опишите задачу и контекст. Ответим в течение рабочего дня, при необходимости подпишем NDA.