etcd 3.2 в production Kubernetes: Watch API быстрее, алерты - раньше
Обновляем etcd до 3.2 на production-кластере Kubernetes: улучшения Watch API и Raft снижают latency. Настраиваем алерты на write latency - кластер сигналит раньше, чем K8s замечает проблему.
etcd 3.2 - значительные улучшения производительности Watch API, gRPC gateway и lease TTL, которые снижают нагрузку на Kubernetes control plane
etcd - это та часть Kubernetes-кластера, о которой думаешь меньше всего, пока она не ломается. И когда ломается - больно всем сразу: apiserver не отвечает, scheduler не планирует, controller-manager застывает. Мы обновили etcd до 3.2 на одном из managed-кластеров и заодно наконец нормально закрыли мониторинг latency.
Что изменилось в 3.2
Команда etcd описывает 3.2 как релиз с фокусом на стабильность и производительность под нагрузкой. Три вещи нас интересовали больше всего.
Watch API переработан серьёзно. В 3.1 Watch под нагрузкой мог создавать ситуацию, когда watchers накапливали backlog событий быстрее чем их обрабатывали. В 3.2 watcherGroup переписан, есть разделение на синхронные и асинхронные пути. Для Kubernetes это важно - apiserver активно использует Watch для отслеживания изменений объектов, и любой hickup здесь немедленно сказывается на реакции кластера на изменения в конфигурации.
gRPC gateway. etcd 3.2 включает встроенный gRPC-gateway, который транслирует HTTP/JSON запросы в gRPC. Для нас это значит, что можно убрать отдельный прокси-процесс, который раньше нужен был для мониторинговых запросов через curl к etcd API v3. Мелочь, но одна лишняя движущаяся часть - это всегда лишняя точка отказа.
Lease TTL. Исправлено поведение при одновременном истечении большого количества lease. В старом поведении etcd мог на короткое время упасть в спайк write latency при batch-истечении lease - как раз в этот момент K8s обновляет NodeStatus через node heartbeat. В 3.2 это сглажено.
Как обновляли
Кластер - три ноды etcd, развёрнутые рядом с Kubernetes master-нодами. Схема из недавнего поста про kubeadm: keepalived, haproxy перед apiserver, etcd кластеризован между мастерами.
Обновление делали по одной ноде с проверкой кворума между шагами. etcd умеет rolling upgrade: 3.1 и 3.2 ноды нормально работают в одном кластере, пока все не переедут на новую версию. Это важно - downtime нулевой при аккуратном подходе.
Порядок на каждой ноде:
- Убедиться что кластер healthy и leader не на этой ноде.
- Остановить etcd.
- Заменить бинарь.
- Запустить etcd.
- Проверить
etcdctl endpoint healthпо всем трём нодам. - Перейти к следующей.
Единственный момент который потребовал внимания - флаги. В 3.2 несколько deprecated флагов стали hard-ошибками при запуске. У нас в systemd unit-файле был --listen-peer-urls с устаревшим форматом - etcd просто отказался запускаться. Поправили до запуска обновления на всех нодах.
Мониторинг latency: что мы сделали правильно только сейчас
Вот здесь главное. У нас был мониторинг etcd - метрики через Prometheus, дашборд в Grafana. Но алертов на write latency не было. Была только грубая проверка доступности через TCP-порт.
Проблема такая: когда etcd начинает деградировать по write latency, TCP-порт ещё отвечает. apiserver тоже ещё отвечает - просто медленно. Kubernetes начинает вести себя странно: деплойменты зависают, события запаздывают, kubectl apply висит. Это состояние может длиться минуты - и всё это время нет ни одного алерта, потому что технически всё «работает».
В etcd 3.x есть метрика etcd_disk_wal_fsync_duration_seconds - время fsync на WAL-диске. Это хороший ранний индикатор: если диск начинает тормозить (нагрузка, проблемы с железом, конкуренция с другими процессами), это первое место где это видно. Мы добавили алерт:
- 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 }}"
Десять миллисекунд на p99 - это наш порог для warning. Ещё добавили аналогичный на etcd_disk_backend_commit_duration_seconds для backend (boltdb) с порогом 25ms.
Второй алерт - на etcd_server_proposals_failed_total: если proposals начинают фейлиться, это Raft-кворум под давлением. Это уже critical, реагировать нужно немедленно.
Проверили на практике - при искусственной нагрузке на диск ноды etcd алерт на WAL fsync срабатывал за несколько минут до того, как kubectl начинал заметно тупить. Разрыв небольшой, но достаточный чтобы успеть посмотреть что происходит до того как клиент позвонит.
Что видно после обновления
Субъективно - кластер стал чуть живее при применении конфигурации через kubectl apply. Объективно смотреть сложно: у нас нет baseline с точными цифрами до обновления на этом конкретном кластере. Мы не закладывали нагрузочный тест специально под апгрейд - это production, не стенд.
Что есть: p99 WAL fsync в нормальном режиме держится около 2-3ms, что хорошо. Leader election за последние двое суток - ноль незапланированных смен лидера. До обновления изредка видели одиночные смены лидера без очевидной причины - теперь нет, хотя может просто совпадение и нагрузка была другая.
gRPC gateway работает, убрали прокси-процесс, стало чуть проще.
Итог
Обновление прошло без происшествий. Ноль downtime, ноль проблем с кворумом в процессе. Rolling upgrade в etcd - это не маркетинг, реально работает корректно.
Главное что мы получили - не столько от самого 3.2, сколько от поводя разобраться наконец с мониторингом latency. etcd не то что можно мониторить только по TCP-пингу. Теперь видим состояние WAL и backend, и это сдвигает момент обнаружения проблемы в нашу пользу.