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

etcd 3.3 и backup-restore: проверили disaster recovery на реальном кластере Kubernetes

Настроили ежечасный CronJob для snapshot etcd в S3-совместимое хранилище и прогнали полный disaster-recovery сценарий - восстановление с нуля заняло около четырёх минут.

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

etcd 3.3 - улучшения производительности и механизм snapshot для Kubernetes

etcd - это та часть Kubernetes, о которой стараются не думать лишний раз. Работает в фоне, хранит всё состояние кластера, и при спокойной работе о нём никто не вспоминает. Мы решили, что такое отношение к компоненту, без которого кластер превращается в набор бесхозных контейнеров, - не очень хорошая идея. И взялись за нормальный backup-процесс.

Поводом стал не инцидент, что уже неплохо. В etcd 3.3 подтянули работу с snapshot-ами через etcdctl - команда etcdctl snapshot save стала более предсказуемой, появился snapshot status для проверки целостности. Мы как раз обновляли кластера под управлением managed-инфраструктуры и решили совместить обновление с нормально выстроенным DR-процессом.

Что именно настраивали

Задача звучала просто: делать snapshot etcd каждый час, складывать в S3-совместимое хранилище, и уметь из него восстановиться за разумное время.

Реализация - CronJob в Kubernetes, который запускается раз в час и делает примерно следующее:

  • Запрашивает snapshot. etcdctl snapshot save с параметрами подключения к etcd - эндпойнт, TLS-сертификаты через projected volume из Secret. Snapshot пишется во временный файл внутри контейнера.
  • Верифицирует snapshot. etcdctl snapshot status с выводом в таблицу - смотрим что ревизия, количество ключей и размер файла вменяемые. Если статус не возвращает нормального ответа - Job завершается с ошибкой, Alertmanager видит failed CronJob.
  • Загружает в хранилище. aws s3 cp в S3-совместимое хранилище. Имя файла включает временную метку в UTC. Lifecycle rule на бакете - удалять файлы старше семи дней.

CronJob запускается в отдельном namespace с минимальными правами - ему нужен доступ только к Secret с etcd TLS-сертификатами и endpoint-у etcd. Serviceaccount без лишних RBAC-прав.

Один момент, который сразу выяснился: etcd-эндпойнт нужно указывать конкретно - не через kube-apiserver, а прямо на etcd member. Иначе snapshot берётся через API, который делает это иначе и без гарантий консистентности. Казалось бы очевидно, но документация на это намекает не особо громко.

Disaster recovery: проверили по-настоящему

Настроить backup - это одно. Мы решили проверить, что восстановление вообще работает, пока мы это выбрали сами, а не когда придётся. Взяли отдельный тестовый кластер, намеренно уронили etcd - удалили data directory на всех членах кластера, после чего кластер ожидаемо перестал функционировать, kube-apiserver сыпал ошибками.

Восстановление по шагам:

Первое - восстановление каждого member-а из snapshot. etcdctl snapshot restore создаёт новый data directory. Для каждого member-а запускаем с его индивидуальными параметрами - --name, --initial-advertise-peer-urls, --initial-cluster. Это важный момент: восстановление делается на каждой ноде отдельно из одного и того же snapshot-файла, с разными параметрами идентификации. Не копируем data directory с одной ноды на другие.

Второе - запуск etcd. После восстановления запускаем etcd на каждой ноде, кластер формирует кворум, начинает выбирать лидера.

Третье - перезапуск kube-apiserver. После того как etcd доступен и отвечает, apiserver поднимается сам. Следом поднимаются controller-manager и scheduler. Kubelet на воркер-нодах пинает apiserver, тот отвечает - ноды начинают возвращаться в Ready.

Итоговое время от «etcd data directory удалён» до «кластер полностью функционирует, все поды запущены» - около четырёх минут. Это для трёх-нодового кластера с небольшим количеством объектов в etcd. Большая часть времени - ожидание пока поднимутся system-поды вроде CoreDNS и kube-proxy, а не само восстановление etcd.

Хочется оговориться: мы не восстанавливали с нуля виртуальные машины, они уже были живые. Реальный disaster требует сначала поднять инфраструктуру, потом восстанавливать etcd. Это отдельный сценарий, который мы ещё не прогоняли от и до - он включает Terraform и provisioning, что удлиняет процесс на неизвестное время.

Пара вещей, которые выяснились

Snapshot не включает секреты в открытом виде. Если в кластере включено encryption at rest для etcd (EncryptionConfiguration в apiserver), данные в etcd зашифрованы, и snapshot тоже зашифрован. При восстановлении нужно, чтобы ключи шифрования были доступны apiserver-у. Это значит, что ключи шифрования должны жить в отдельном, более защищённом месте, а не только в etcd. У нас они хранятся в Vault - это дополнительная зависимость при восстановлении, которую нужно учитывать.

etcd 3.3 чуть менее говорлив при restore. В предыдущих версиях snapshot restore выдавал вывод, который требовал интерпретации - не всегда было понятно, ошибка это или просто информационное сообщение. В 3.3 это немного причесали, команда явно завершается с нулевым кодом при успехе. Мелочь, но в стрессовой ситуации восстановления это приятно.

Мониторинг самого etcd. В процессе настройки посмотрели на метрики etcd - там есть etcd_mvcc_db_total_size_in_bytes, etcd_server_leader_changes_seen_total, etcd_disk_wal_fsync_duration_seconds. Добавили их в Prometheus и сделали алерты на аномальные значения. Частая смена лидера или растущий размер базы без compaction - это симптомы, которые лучше замечать заранее.

Backup-ротация и автоматическая верификация snapshot-а - это следующее что хочется добавить. Сейчас Job просто проверяет что snapshot сохранился, но не проверяет что из него реально можно восстановиться. Полная проверка потребует тестового окружения, в котором периодически прогоняется restore - это уже отдельная история.

Контакт

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

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