Kubernetes

Теория: Резервное копирование и восстановление

Данные в Kubernetes живут в PersistentVolume. Pod приходят и уходят, но данные остаются — пока не случится сбой диска, ошибка оператора или атака. Без резервных копий вы узнаете о проблеме, когда данные уже потеряны.

В этом уроке разберём три подхода к резервному копированию: VolumeSnapshot для моментальных снимков на уровне хранилища, CronJob для регулярных бэкапов на уровне приложения, и Velero для резервного копирования всего кластера.

Зачем нужны бэкапы в Kubernetes

Kubernetes обеспечивает высокую доступность приложений — если Pod упал, создаётся новый. Но это не защищает данные:

  • Ошибка в коде — приложение удалило или повредило данные
  • Человеческая ошибка — кто-то выполнил kubectl delete pvc не в том namespace
  • Сбой хранилища — диск вышел из строя, облачный провайдер потерял данные
  • Ransomware — злоумышленник зашифровал данные

StatefulSet и PVC не спасут от этих проблем. Нужны резервные копии, которые хранятся отдельно от основных данных.

VolumeSnapshot: снимки на уровне хранилища

VolumeSnapshot — это моментальный снимок PVC. Он создаётся на уровне хранилища, быстро и без остановки приложения.

Как это работает:

  1. Вы создаёте объект VolumeSnapshot
  2. CSI-драйвер хранилища создаёт снимок диска
  3. Снимок можно использовать для восстановления или создания нового PVC

Проверьте, поддерживает ли ваш кластер VolumeSnapshot:

kubectl get volumesnapshotclasses

Если команда вернула список — снапшоты поддерживаются. Если ошибка «the server doesn't have a resource type» — нужно установить CSI-драйвер с поддержкой снапшотов.

В облаках (GKE, EKS, AKS) VolumeSnapshot обычно работает из коробки. В minikube нужно включить CSI-драйвер:

minikube addons enable csi-hostpath-driver
minikube addons enable volumesnapshots

Создание VolumeSnapshotClass:

apiVersion: snapshot.storage.k8s.io/v1
kind: VolumeSnapshotClass
metadata:
  name: csi-snapclass
driver: hostpath.csi.k8s.io  # Зависит от вашего CSI-драйвера
deletionPolicy: Delete

Создание снимка PVC:

apiVersion: snapshot.storage.k8s.io/v1
kind: VolumeSnapshot
metadata:
  name: database-snapshot-2024-01-15
spec:
  volumeSnapshotClassName: csi-snapclass
  source:
    persistentVolumeClaimName: database-pvc

Снимок создаётся за секунды, независимо от размера данных — это copy-on-write на уровне хранилища.

Восстановление из снимка:

Создайте новый PVC из снимка:

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: database-pvc-restored
spec:
  dataSource:
    name: database-snapshot-2024-01-15
    kind: VolumeSnapshot
    apiGroup: snapshot.storage.k8s.io
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 10Gi

Теперь можно запустить Pod с восстановленным PVC и проверить данные.

Ограничения VolumeSnapshot:

  • Работает только с CSI-драйверами, которые поддерживают снапшоты
  • Снимок хранится в том же хранилище — если хранилище умрёт, снимки тоже
  • Не все типы хранилищ поддерживают (например, NFS обычно не поддерживает)
  • Снимок может быть неконсистентным, если приложение активно пишет данные

Для консистентного снимка базы данных лучше сначала выполнить CHECKPOINT или остановить запись.

CronJob: бэкапы на уровне приложения

VolumeSnapshot делает снимок диска «как есть». Но для базы данных лучше использовать её родные инструменты — pg_dump, mysqldump, mongodump. Они создают логический бэкап, который гарантированно консистентен.

CronJob запускает Pod по расписанию. Идеально для регулярных бэкапов.

Бэкап PostgreSQL:

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: backup-storage
spec:
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 10Gi
---

apiVersion: batch/v1
kind: CronJob
metadata:
  name: postgres-backup
spec:
  schedule: "0 2 * * *"  # Каждый день в 2:00
  concurrencyPolicy: Forbid  # Не запускать новый, пока предыдущий не завершился
  successfulJobsHistoryLimit: 3
  failedJobsHistoryLimit: 3
  jobTemplate:
    spec:
      template:
        spec:
          containers:
            - name: backup
              image: postgres:15
              command:
                - /bin/sh
                - -c
                - |
                  set -e
                  BACKUP_FILE="/backup/db-$(date +%Y%m%d-%H%M%S).sql.gz"
                  echo "Starting backup to $BACKUP_FILE"
                  pg_dump -h postgres-service -U appuser -d appdb | gzip > "$BACKUP_FILE"
                  echo "Backup completed: $(ls -lh $BACKUP_FILE)"
                  # Удаляем бэкапы старше 7 дней
                  find /backup -name "*.sql.gz" -mtime +7 -delete
                  echo "Old backups cleaned up"
              env:
                - name: PGPASSWORD
                  valueFrom:
                    secretKeyRef:
                      name: db-secret
                      key: password
              volumeMounts:
                - name: backup
                  mountPath: /backup
          restartPolicy: OnFailure
          volumes:
            - name: backup
              persistentVolumeClaim:
                claimName: backup-storage

Разберём ключевые моменты:

  • schedule: "0 2 * * *" — cron-формат: минута, час, день месяца, месяц, день недели. Это «каждый день в 02
    ».
  • concurrencyPolicy: Forbid — если предыдущий бэкап ещё выполняется, новый не запустится. Защита от накопления параллельных задач.
  • set -e — скрипт остановится при первой ошибке. Без этого ошибка pg_dump останется незамеченной.
  • gzip — сжатие экономит место. SQL-дампы сжимаются в 5-10 раз.
  • find ... -mtime +7 -delete — ротация: удаляем файлы старше 7 дней, чтобы не переполнить диск.

Проверка работы CronJob:

# Список CronJob
kubectl get cronjob
# Запустить бэкап вручную (не дожидаясь расписания)
kubectl create job --from=cronjob/postgres-backup manual-backup
# Посмотреть логи
kubectl logs job/manual-backup
# Проверить, что файл создался
kubectl exec -it <любой-pod-с-backup-pvc> -- ls -la /backup

Отправка бэкапов во внешнее хранилище:

Хранить бэкапы в том же кластере — плохая идея. Если кластер умрёт, бэкапы умрут вместе с ним. Отправляйте бэкапы в S3, GCS или другое внешнее хранилище:

apiVersion: batch/v1
kind: CronJob
metadata:
  name: postgres-backup-s3
spec:
  schedule: "0 2 * * *"
  jobTemplate:
    spec:
      template:
        spec:
          containers:
            - name: backup
              image: postgres:15
              command:
                - /bin/sh
                - -c
                - |
                  set -e
                  apt-get update && apt-get install -y awscli
                  BACKUP_FILE="db-$(date +%Y%m%d-%H%M%S).sql.gz"
                  pg_dump -h postgres-service -U appuser -d appdb | gzip > "/tmp/$BACKUP_FILE"
                  aws s3 cp "/tmp/$BACKUP_FILE" "s3://my-backups/postgres/$BACKUP_FILE"
                  echo "Backup uploaded to S3"
              env:
                - name: PGPASSWORD
                  valueFrom:
                    secretKeyRef:
                      name: db-secret
                      key: password
                - name: AWS_ACCESS_KEY_ID
                  valueFrom:
                    secretKeyRef:
                      name: aws-credentials
                      key: access-key
                - name: AWS_SECRET_ACCESS_KEY
                  valueFrom:
                    secretKeyRef:
                      name: aws-credentials
                      key: secret-key
                - name: AWS_DEFAULT_REGION
                  value: "us-east-1"
          restartPolicy: OnFailure

В production лучше использовать готовый образ с awscli, а не устанавливать его каждый раз.

Velero: бэкап всего кластера

VolumeSnapshot и CronJob работают с отдельными PVC. Но что если нужно забэкапить весь namespace или весь кластер? Deployment, Service, ConfigMap, Secret — всё это тоже нужно восстанавливать.

Velero — инструмент для резервного копирования ресурсов Kubernetes и данных PVC. Он может:

  • Бэкапить все ресурсы в namespace или кластере
  • Бэкапить данные PVC через Restic или CSI-снапшоты
  • Восстанавливать в тот же или другой кластер
  • Мигрировать приложения между кластерами

Установка Velero:

Установим Velero по инструкции. После установки проверим доступность:

velero version
velero backup-location get

Опции --use-node-agent и --default-volumes-to-fs-backup включают бэкап данных PVC через файловую систему (Restic/Kopia).

Создание бэкапа:

# Бэкап всего namespace
velero backup create my-app-backup --include-namespaces default
# Бэкап с выбором ресурсов
velero backup create db-backup \
  --include-namespaces default \
  --selector app=postgres

# Посмотреть статус
velero backup describe my-app-backup
# Список бэкапов
velero backup get

Восстановление:

# Восстановить в тот же namespace
velero restore create --from-backup my-app-backup
# Восстановить в другой namespace
velero restore create --from-backup my-app-backup \
  --namespace-mappings default:restored-namespace
# Посмотреть статус
velero restore describe <restore-name>

Расписание бэкапов:

# Бэкап каждый день в 2:00
velero schedule create daily-backup \
  --schedule="0 2 * * *" \
  --include-namespaces default \
  --ttl 168h  # Хранить 7 дней

Velero в production

Velero — это стандарт для бэкапов Kubernetes в production. Он интегрируется с облачными провайдерами (AWS, GCP, Azure), поддерживает разные хранилища (S3, GCS, Azure Blob, MinIO), и может использовать CSI-снапшоты для быстрых бэкапов.

Основные сценарии использования:

  • Disaster recovery — восстановление после потери кластера
  • Миграция — перенос приложений между кластерами
  • Клонирование окружений — создание staging из production

Какой подход выбрать

ПодходКогда использовать
VolumeSnapshotБыстрые снимки для отката, поддержка CSI в кластере
CronJob + dumpЛогические бэкапы баз данных, гарантированная консистентность
VeleroБэкап всего namespace/кластера, disaster recovery, миграции

В production обычно комбинируют:

  • VolumeSnapshot для быстрого отката (минуты)
  • CronJob для ежедневных бэкапов БД в S3 (консистентность)
  • Velero для полного бэкапа кластера (disaster recovery)

Частые ошибки

  • Бэкапы в том же кластере — если кластер умрёт, бэкапы умрут вместе с ним. Отправляйте бэкапы во внешнее хранилище (S3, GCS).
  • Не тестируют восстановление — бэкап бесполезен, если из него нельзя восстановиться. Регулярно проверяйте, что восстановление работает.
  • Нет ротации — диск переполняется старыми бэкапами. Настройте удаление старых файлов или TTL в Velero.
  • Бэкап работающей БД без CHECKPOINT — VolumeSnapshot может поймать неконсистентное состояние. Для критичных данных используйте pg_dump или останавливайте запись.
  • Секреты в логах CronJob — не выводите пароли в echo. Используйте переменные окружения, а не аргументы командной строки.

Что запомнить

  • VolumeSnapshot — быстрые снимки на уровне хранилища. Требует CSI-драйвер с поддержкой снапшотов.
  • CronJob — регулярные задачи по расписанию. Идеально для pg_dump/mysqldump.
  • Velero — бэкап всего кластера. Стандарт для disaster recovery в production.
  • Бэкапы во внешнее хранилище — обязательно. Бэкап в том же кластере — не бэкап.
  • Тестируйте восстановление — бэкап без проверки восстановления — это иллюзия безопасности.

Рекомендуемые программы

+7 800 100 22 47

бесплатно по РФ

+7 495 085 21 62

бесплатно по Москве

108813 г. Москва, вн.тер.г. поселение Московский,
г. Московский, ул. Солнечная, д. 3А, стр. 1, помещ. 20Б/3
ОГРН 1217300010476
ИНН 7325174845