Перейти к содержимому
HoginHogin
Назад

Velero: бэкап кластера в S3 и репетиция восстановления — DR, который вы хотя бы раз прогнали

9 мин чтения

Понедельник, 07:00. Кластер не поднимается после отказа ноды, вы открываете список бэкапов — и понимаете, что ни один из них ни разу не восстанавливали. Зелёный список бэкапов — это не план восстановления, это перечень того, что вы не пробовали поднять. Velero закрывает обе половины уравнения: бэкап кластера в S3 по расписанию и дешёвый способ прогнать восстановление в чистом namespace — хоть каждый месяц.

Почему сейчас: Velero 1.18 — актуальная линейка 2026 года, AWS обновила официальные гайды для EKS (май 2026), появились свежие практические разборы настройки на S3. Ниже — минимальный контур, который стоит собрать: ежедневные бэкапы с TTL, CSI-снапшоты и репетиция restore с алертом.

Содержание

Открыть содержание

Что значит «забэкапить кластер»

В кластере три слоя, и бэкапить их нужно по-разному:

Классические подходы закрывают слои по отдельности — и не закрывают главный вопрос: поднимется ли всё это вместе на новом месте.

Три слоя кластера и то, что закрывают привычные инструменты

Пройдёмся по привычным инструментам:

Каждый инструмент по-своему прав. Проблема не в них, а в том, что «бэкап кластера» — это три задачи, решённые согласованно, плюс одна, которую никто не любит: проверка, что из этого поднимается. Согласованно — ключевое слово: слои, снятые в разное время и разными инструментами, при восстановлении собираются в кластер, которого никогда не существовало. Часть подов ссылается на секреты из вчерашнего снапшота, часть — на томы из позавчерашнего, и поиск «какой кусок от какого времени» съедает ночь.

Что меняет Velero

Velero — контроллер, который превращает бэкап и восстановление в обычные Custom Resources кластера. Конкретно это даёт:

Путь бэкапа от Schedule через Backup CR до Restore в новый namespace

Ключевой сдвиг не «ещё один бэкапщик», а то, что restore стал операцией уровня кластера, которую можно запускать скриптом — а значит, и по расписанию.

etcd-snapshot + скриптыVelero
Объекты кластерада, но всем кластером сразуда, выборочно по namespace
Данные PVCнетда: CSI-снапшот или Kopia в S3
Расписание и TTLсвой cron и свои скрипты очисткиSchedule CR, TTL из коробки
Restore в другой namespaceнет--namespace-mappings
Независимость от storage-кластеранеприменимоfile-level копия лежит в S3
Проверка restoreручная, «когда-нибудь»restore — CR, гоняется Job’ой по расписанию
Что крутится в кластереcron-скрипты на нодахDeployment + DaemonSet

Что нужно, чтобы поднять бэкапы на Velero

Минимальный путь из четырёх шагов — от пустого кластера до первого восстановленного namespace.

1. Бакет и credentials. Отдельный бакет в S3 или MinIO и отдельный ключ с правами только на него:

[default]
aws_access_key_id=AKIA...
aws_secret_access_key=...

2. Установка. Один вызов CLI разворачивает всё: контроллер, location’ы и — при --use-node-agent — DaemonSet для file-level бэкапов:

velero install \
  --provider aws \
  --plugins velero/velero-plugin-for-aws:v1.14.0 \
  --bucket velero-backups \
  --backup-location-config region=ru-central1,s3Url=https://s3.example.internal,s3ForcePathStyle=true \
  --snapshot-location-config region=ru-central1 \
  --use-node-agent \
  --features=EnableCSI \
  --secret-file ./credentials-velero

Тонкости: для MinIO обязательны s3Url и s3ForcePathStyle=true; версию плагина сверьте с вашей версией Velero — совместимость ломается между мажорами; флаг --features=EnableCSI требует VolumeSnapshot CRDs и запущенного snapshot-controller в кластере.

3. Schedule. Расписание живёт в кластере как CR — ежедневный бэкап в 03:00 с TTL неделя:

apiVersion: velero.io/v1
kind: Schedule
metadata:
  name: daily
  namespace: velero
spec:
  schedule: "0 3 * * *"
  ttl: 168h0m0s
  template:
    includedNamespaces: ["app", "infra"]
    defaultVolumesToFsBackup: true
    snapshotVolumes: false

defaultVolumesToFsBackup: true — главный флаг: каждый PVC без явных исключений уезжает в S3 через node-agent. snapshotVolumes: false здесь осознанно: для малого кластера дешевле одна консистентная схема, чем две. TTL 168h0m0s — ровно 7 суток, дальше контроллер удаляет сам.

Сколько хранить — считайте от худшего сценария: вы замечаете тихую поломку (протухший токен, уехавший конфиг) не сразу. Семи дневных копий хватает, когда проблему видно за день-два; если нет — добавьте второй Schedule weekly с TTL 720h (месяц) и schedule: "0 4 * * 0". Это защита от «неделю бэкапим уже сломанное» ценой четырёх дополнительных копий в бакете. И учтите: по умолчанию Velero тянет объекты только из указанных namespace — cluster-scoped ресурсы (CRD, ClusterRole) попадают в бэкап с флагом --include-cluster-resources, при restore они воссоздаются вместе с namespace’ами.

4. Первый restore — руками, немедленно. Пока бэкапов один-два, прогоните восстановление лично:

velero restore create restore-test \
  --from-backup daily-2026-11-03 \
  --namespace-mappings app:app-test
velero restore describe restore-test --details

Restore создаст объекты из бэкапа в namespace app-test, а данные PVC вернёт в новые тома — убедитесь, что в кластере есть storage-class, который умеет их динамически биндить. --namespace-mappings — тот самый мост «прод → стенд»: ни прод, ни его namespace не трогаются.

Репетиция восстановления по расписанию

Один ручной restore доказывает, что бэкап рабочий — сегодня. Через полгода конфиги, версии и storage-class уедут, и доказательство устареет. Значит, прогон должен повторяться сам, а результат — куда-то репортиться.

Репетиция восстановления: CronJob, restore в dr-rehearsal, smoke-чеки и ping в Healthchecks

Раз в месяц CronJob берёт свежий бэкап, восстанавливает его в scratch-namespace и делает smoke-чеки:

set -eu
B=$(velero backup get -o name | tail -1)
NS=dr-rehearsal
velero restore create "rehearsal-$(date +%F)" \
  --from-backup "${B##*/}" \
  --namespace-mappings "app:${NS}"

until [ "$(velero restore get "rehearsal-$(date +%F)" -o jsonpath='{.status.phase}')" = "Completed" ]; do
  sleep 20
done

kubectl -n "$NS" wait --for=condition=Ready pod --all --timeout=300s
curl -fsS "https://hc-ping.com/<uuid>"   # сигнал: репетиция прошла
kubectl delete ns "$NS"                  # уборка за собой

Если что-то упало — скрипт не доходит до curl, пинга нет, и Healthchecks.io шлёт алерт по тишине. Это dead man’s switch — тот же приём, что для файловых бэкапов в статье про restic и Healthchecks: мониторить надо не наличие бэкапов, а факт успешного восстановления.

Job’е нужен свой ServiceAccount с узкими правами: get/list на backups, create/get на restores, create/delete на namespaces, list на pods. Ничего больше.

Три практических замечания. Первая репетиция — только руками: автопрогон имеет смысл, когда вы хотя бы раз видели, как выглядит успешный restore, — иначе дебажите сразу и Job’у, и бэкапу. Smoke-чеки — по глубине инцидента: kubectl wait на Ready достаточно для MVP; следующим шагом добавьте HTTP-пробу на реальный endpoint приложения — под может быть Ready, а приложение внутри — нет. Уборка обязательна: namespace dr-rehearsal удаляется в конце даже при падении скрипта (trap), иначе PVC и поды копятся, а следующая репетиция восстанавливается поверх мусора.

Границы: Postgres бэкапится сам

Снапшот тома с живой Postgres — это «почти»: часть WAL в кэше страниц, снапшот может снять диск между чекпоинтами. Для файлов и однодисковых приложений file-level бэкап достаточен; для базы — нет.

Поэтому граница проходит так: Postgres бэкапится своими инструментами — pgbackrest, Barman или нативный бэкап оператора CloudNativePG с WAL-архивом в S3. Velero остаётся вокруг базы: манифесты кластера, секреты, ConfigMaps — всё, что нужно, чтобы поднять саму базу на новом месте и накатить на неё бэкап. Crash-consistent restore базы из тома — аварийный запасной выход, а не план.

Если база попроще — single-instance MySQL или SQLite в PVC — конвейер всё равно можно собрать на Velero: у бэкапов есть pre/post-hooks — команды в контейнере до и после копирования тома (FLUSH TABLES WITH READ LOCK перед, unlock после; для файловых хранилищ — fsfreeze). Медленнее и капризнее нативного mysqldump, но целостность снимка перестаёт быть лотереей.

Как проверить, что всё работает

Итог

План восстановления, который ни разу не прогонялся, не существует — существует документ с оптимистичными допущениями. Velero делает прогон дешёвым: раз в месяц Job восстанавливает свежий бэкап в чистый namespace, гоняет smoke-чеки и репортит пинг — и вы узнаёте о сломанном DR утром репетиции, а не утром инцидента. Плата: контроллер с DaemonSet в кластере, бакет в S3 и честное отношение к базам через их собственные механизмы бэкапа. Для кластера, который стоит меньше недели работы, это самая дешёвая страховка на рынке Day-2.


Поделиться:

Следующая статья
Keycloak в Kubernetes: один OIDC для kubectl, Argo CD и Grafana — SSO на своём железе