Правильные, но (не)простые бэкапы. Как настроить резервное копирование в Kubernetes?

от автора

Разработчикам и системным администраторам важно обеспечивать надежность и доступность данных в приложениях Kubernetes. Несмотря на высокую степень автоматизации и управления контейнерами, всегда остаются риски сбоев или человеческих ошибок — поэтому необходимо заранее задуматься о резервном копировании.

Меня зовут Филипп, я системный администратор в отделе Data- и ML-продуктов Selectel. В этой статье постараюсь раскрыть, какие есть решения для резервного копирования в Kubernetes, и на простом примере покажу, как с ними работать. Подробности под катом.

Оглавление

Резервное копирование в Kubernetes
Какие есть варианты
Подготовка рабочего окружения
Резервное копирование на базе K8up
Резервное копирование на базе Kasten K10 by Veeam

Резервное копирование в Kubernetes


Динамичная и распределенная архитектура Kubernetes обязывает использовать резервные копии. Необходимо учитывать состояние каждого контейнера, конфигурации, секрета и блока данных, которые находятся в постоянном хранилище.

Важно отметить, что в Kubernetes управление постоянным хранилищем осуществляется через основные компоненты:

  • PVC (Persistent Volume Claim) — это запрос на использование хранилища со стороны приложения или пользователя.
  • PV (Persistent Volume) — Физическое или логическое хранилище в кластере, которое может быть связано с внешними хранилищами.
  • Storage Class — определяет, как PV создается.
  • CSI (Container Storage Interface) — плагин, который позволяет взаимодействовать с различными внешними хранилищами.
  • Внешнее хранилище: Облачное или локальное хранилище, где фактически хранятся данные.

Также Kubernetes поддерживает парадигму Infrastructure as code, IaC. Это позволяет автоматизировать и стандартизировать развертывание и управление ресурсами с помощью GitOps. Однако даже в таком случае есть важная деталь, которую нельзя игнорировать: сохранность данных.

Пока ресурсы типа config-map, secret, deployment, service, ingress и другие могут быть воссозданы из кода по IaC, данные в PV восстановить в случае сбоя, удаления или взлома не получится.

Какие есть варианты

Можно придумать разные способы создания резервной копии постоянных данных. Это могут быть, например, кастомные скрипты, которые запускаются по расписанию (cron) и создают снапшоты (снимки) самих PV.

При создании снимков PV могут возникнуть проблемы, если во время процесса в базу данных активно записываются данные. Например, снапшот может получиться неполным или поврежденным. Тогда при попытке восстановиться есть риск, что база данных запустится некорректно.

Однако есть сложные системы, которые обеспечивают непрерывное резервное копирование и автоматическое восстановление в случае сбоя. Среди них — Velero.io, Kaste K10, Cohesity, Portworx и другие.

Выбор стратегии резервного копирования будет зависеть от текущей реализации. Например, если ваше высоконагруженное приложение запущено в Managed Kubernetes, а стремительно растущие базы данных — на сетевых дисках, необходимо подумать о том, чтобы резервное копирование не влияло на работоспособность IT-систем.

Например, если основная база данных — PostgreSQL, необходимо подумать о бэкапировании с помощью streaming replication и настроить резервное копирование на secondary-реплике через pg_dump, pg_basebackup, Barman или WAL-E. А также — определиться с периодичностью бэкапирования, его шифрованием, способом хранения, мониторингом и проверкой восстановления из резервных копий.

Далее мы рассмотрим два инструмента K8UP и Kasten K10 от Veeam, которые предоставляют различные функции для резервного копирования. Но прежде подготовим тестовый стенд в облаке Selectel.


Подготовка рабочего окружения


Подготовим тестовый стенд, в который установим простейшее приложение с постоянным хранилищем данных. За основу возьмем Managed Kubernetes, а в качестве хранилища резервных копий — объектное хранилище S3, к которому настроим доступ через сервисного пользователя и S3-ключ.

Подготовка кластера Managed Kubernetes

В панели управления нужно перейти в раздел Kubernetes:

Далее дождитесь статуса ACTIVE и скачайте kubeconfig для дальнейшей работы с кластером:

Для работы с кластером можно использовать Lens или Kubectl. Для kubectl необходимо указать переменную KUBECONFIG:

 #: export KUBECONFIG=<ПОЛНЫЙ ПУТЬ ДО KUBECONFIG ФАЙЛА> 

В качестве проверки можно получить пространство имен кластера:

#: kubectl get namespace 

Подготовка объектного хранилища

Далее нужно создать и настроить контейнер: ввести его название, выбрать Приватный тип, класс хранения Стандартное хранение и нажать кнопку.

Создание сервисного пользователя и ключа S3

Важно: S3 access и secret keys необходимо сохранить в надежном месте.

Установка тестового приложения в кластере

Теперь осталось установить в кластер простейшее тестовое приложение. Для этого подготовим манифест создания Storage Class, Deployment и Persistent Volume Claim и применим его:

# nginx-pvc.yaml apiVersion: storage.k8s.io/v1 kind: StorageClass metadata:   name: default   annotations:     storageclass.kubernetes.io/is-default-class: 'true' provisioner: cinder.csi.openstack.org parameters:   availability: ru-9a   fsType: ext4   type: fast.ru-9a reclaimPolicy: Delete allowVolumeExpansion: true volumeBindingMode: Immediate --- apiVersion: v1 kind: PersistentVolumeClaim metadata:   name: nginx-pvc   namespace: default spec:   accessModes:     - ReadWriteOnce   resources:     requests:       storage: 1Gi --- apiVersion: apps/v1 kind: Deployment metadata:   name: nginx-deployment   namespace: default spec:   replicas: 1   selector:     matchLabels:       app: nginx   template:     metadata:       labels:         app: nginx     spec:       containers:         - name: nginx-container           image: nginx:latest           ports:             - containerPort: 80           volumeMounts:             - name: nginx-volume               mountPath: /usr/share/nginx/html       volumes:         - name: nginx-volume           persistentVolumeClaim:             claimName: nginx-pvc 

#: kubectl apply -f nginx-pvc.yaml 

Готово — теперь можем начать работу с инструментами резервного копирования в Kubernetes.

Резервное копирование на базе K8up

K8up (/keɪtæpp/ или просто «кетчуп») — это оператор для автоматизации резервного копирования данных в Kubernetes, в основе которого лежит restic.

Возможности инструмента

  • Автоматическое копирование данных из PVC и БД.
  • Запуск резервного копирования по требованию и расписанию.
  • Сохранение копий в разных местах, включая Amazon S3 и Minio.
  • Восстановление данных через командную строку.
  • Возможность создавать резервные копии, которые учитывают особенности приложений и включают в себя результаты работы любого инструмента, способного выдавать данные в стандартный поток вывода.

Установка

Рассмотрим процесс установки и настройки K8up в подготовленном рабочем окружении. Для наглядности представим схему работы инструмента.

Устанавливаем Custom Resource Definition:

#: kubectl apply -f https://github.com/k8up-io/k8up/releases/download/k8up-4.4.1/k8up-crd.yaml 

Для удобства устанавливаем K8up в отдельное пространство имен:

#: kubectl create namespace k8up #: helm repo add k8up-io https://k8up-io.github.io/k8up #: helm install -n k8up k8up k8up-io/k8up 

Настройка хранилища для резервных копий

Перед созданием резервных копий необходимо указать хранилище, в котором они будут храниться. Подробнее о вариантах хранения можно почитать в документации restic.

Секреты для доступа по S3

Ранее мы подготовили объектное хранилище. Теперь создадим секреты, чтобы у K8up был к нему доступ. Укажем токен доступа к S3 и пароль для restic-репозитория:

# secrets.yaml apiVersion: v1 kind: Secret metadata:   name: "s3-secrets"   namespace: k8up stringData:   bucket: "<BUCKET NAME>"   password: ”<S3 SECRET KEY>”   username: ”<S3 ACCESS KEY>” --- apiVersion: v1 kind: Secret metadata:   name: "restic-repository-password"   namespace: k8up stringData:   password: "secret_pass" # Это пароль для restic repository - создается при инициализации, его можно поменять на любой, желательно надежный и состоящий из более чем 16 символов 

Применим описанный манифест в кластер:

#: kubectl apply -f secrets.yaml 

Подготовительные работы выполнены, теперь можем приступить к созданию резервных копий.

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

Теперь попробуем сделать бэкап данных приложения, которое мы задеплоили на этапе подготовки рабочего окружения. Для этого необходимо создать Custom Resource для K8up, тем самым объяснить, что и как мы хотим копировать.

Создадим CR-Backup, который сделает резервное копирование всех PVC в пространстве имен, где будет создан данный ресурс, а после — применим манифест:

# backup.yaml apiVersion: k8up.io/v1 kind: Backup metadata:   name: k8up-test-swift   namespace: k8up spec:   failedJobsHistoryLimit: 4   successfulJobsHistoryLimit: 0   backend:     repoPasswordSecretRef:       name: "restic-repository-password"       key: "password"     s3:       accessKeyIDSecretRef:         key: username         name: s3-secrets       bucket: test       endpoint: s3.ru-1.storage.selcloud.ru       secretAccessKeySecretRef:         key: password         name: s3-secrets 

#: kubectl apply -f backup.yaml 

После создания ресурса K8up автоматически начнет поиск и создание снимков всех PVC с режимами доступа к хранилищу типа RMX (ReadMany, Execute) и RWO (ReadWriteOnce). Если все было выполнено правильно, мы должны увидеть успешно завершенный под с именем “backup-k8up-….”, который подмонтировал к себе нужный PVC (в нашем случае — nginx-pvc) и создал снимок файловой системы с помощью restic, пример успешно завершенного пода представлен на скриншоте ниже:

Под K8up.

То же самое можно проверить через CLI с помощью команд:

#: kubectl get -A backups.k8up.io #: kubectl get -A snapshots.k8up.io 

В K8up также можно настроить резервное копирование по расписанию, добавить проверку целостности с помощью custom-resource — Schedule.

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

Локальное восстановление

Можно установить утилиту restic и подмонтировать снимок локально, указав чувствительные данные для доступа к объектному хранилищу. Но прежде необходимо узнать SNAPSHOT ID:

RESTIC_REPOSITORY="s3://s3.ru-1.storage.selcloud.ru/<S3-BUCKET NAME>/<S3 K8UP FOLDER PATH>" \ AWS_ACCESS_KEY_ID="" \ AWS_SECRET_ACCESS_KEY="" \ RESTIC_PASSWORD="" \ restic snapshots 

Теперь мы можем скопировать к себе резервную копию командой:

RESTIC_REPOSITORY="s3://s3.ru-1.storage.selcloud.ru/<S3-BUCKET NAME>/<S3 K8UP FOLDER PATH>" \ AWS_ACCESS_KEY_ID="" \ AWS_SECRET_ACCESS_KEY="" \ RESTIC_PASSWORD="" \ restic restore <SNAPSHOT ID> --target ~/Desktop/mnt/ 

В папке ~/Desktop/mnt/ можно увидеть все файлы, находящиеся в снимке.

Восстановление в кластере Custom Resource

Для демонстрации изменим index.html — вернем предыдущее состояние файла до внесения правок. Просто добавим какой-то текст в файл index.html, например Hacker destroyed this site!

Теперь восстановим предыдущее состояние файла в кластере, для этого необходимо создать customResource Restore и новый PVC, в который будем восстанавливать данные:

# restore.yaml kind: PersistentVolumeClaim apiVersion: v1 metadata:   name: restore-test-mfw   namespace: k8up   annotations:     # set to "true" to include in future backups     k8up.io/backup: "false" spec:   accessModes:     - ReadWriteOnce   resources:     requests:       storage: 1Gi --- apiVersion: k8up.io/v1 kind: Restore metadata:   name: restore-test-mfw   namespace: k8up spec:   podSecurityContext:     fsGroup: 65532     fsGroupChangePolicy: OnRootMismatch   restoreMethod:     folder:       claimName: restore-test-mfw   backend:     envFrom:     - secretRef:         name: "open-stack-secret"     repoPasswordSecretRef:       name: "restic-repository-password"       key: "password"     swift:       path: "/container-path"       container: "cmlp-testing-rpd-5921-ff-research-k8up" 

В качестве восстановленной PVC можем указать новую, в которую восстановили бэкап при помощи следующей CLI команды:

kubectl patch -n k8up deployment nginx-deployment -p '{"spec":{"template":{"spec":{"volumes":[{"name":"nginx-volume","persistentVolumeClaim":{"claimName":"restore-test-mfw"}}]}}}}' 

И убедиться, что файл index.html будет без внесенных изменений, т.е. файл будет восстановлен из резервной копии.

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

Проблема при создании бэкапов баз данных в том, что необходимо остановить запись в базу, чтобы на момент создания снимка не было никаких активных и открытых ячеек. Иначе в резервной копии будут незавершенные операции.

В результате мы пришли к выводу, что делать бэкап базы данных лучше встроенными утилитами — например, pg_dump, mongodump и другими. K8up позволяет нам использовать механизм запуска команд в поде, к которому будут добавлены аннотации.

Добавление аннотаций

Процесс создания аннотаций и их описание есть в документации K8up. Ниже — основные конфигурации для них.

annotations:         k8up.io/backupcommand: sh -c 'mongodump --username=$MONGODB_ROOT_USER --password=$MONGODB_ROOT_PASSWORD --archive'             k8up.io/file-extension: .archive 

Конфигурация аннотации MongoDB.

annotations:       k8up.io/backupcommand: sh -c 'PGDATABASE="$POSTGRES_DB" PGUSER="$POSTGRES_USER" PGPASSWORD="$POSTGRES_PASSWORD" pg_dump --clean'       k8up.io/file-extension: .sql 

Конфигурация аннотации PostgreSQL.

<SNIP> template:   metadata:     labels:       app: my-db     annotations:       k8up.io/backupcommand: sh -c 'PGDATABASE="$POSTGRES_DB" PGUSER="$POSTGRES_USER" PGPASSWORD="$POSTGRES_PASSWORD" pg_dump --clean'       k8up.io/file-extension: .sql       k8up.io/backupcommand-container: postgres   spec:     containers:       - name: pgbouncer       - name: postgres       - name: prometheus-exporter         ... <SNIP> 

Сборка аннотации на определенный контейнер.

Резервное копирование на базе Kasten K10 by Veeam

Kasten K10 создан специально для Kubernetes и представляет собой платформу управления данными Cloud Native для операций «второго дня».

Возможности инструмента

Инструмент предоставляет командам DevOps простую, масштабируемую и безопасную систему для резервного копирования и восстановления приложений Kubernetes. Kasten K10 можно интегрировать с реляционными и NoSQL-базами данных и основными дистрибутивами Kubernetes.

Важно: бесплатная версия Kasten K10 ограничена пятью нодами.

Установка

PRE-FLIGHT проверки

Проверим наличие всех необходимых компонентов для Kasten K10:

#: curl <a href="https://docs.kasten.io/tools/k10_primer.sh">https://docs.kasten.io/tools/k10_primer.sh</a> | bash 

В выводе команды мы увидим, чего не хватает для корректной работы K10. В нашем случае отсутствует CRD для создания снапшотов. Решим эту проблему.

Выведем имеющиеся для Volume CRD:

#: kubectl api-resources | grep volume 

Теперь мы можем установить недостающие CRD и проверить, что они появились в списке api-resources:

#: kubectl apply -f https://raw.githubusercontent.com/selectel/mks-csi-snapshotter/master/deploy/setup-snapshot-controller.yaml 

#: kubectl api-resources | grep volume persistentvolumeclaims                pvc                     v1                                          true         PersistentVolumeClaim persistentvolumes                     pv                      v1                                          false        PersistentVolume volumesnapshotclasses                 vsclass,vsclasses   snapshot.storage.k8s.io/v1                  false        VolumeSnapshotClass volumesnapshotcontents                vsc,vscs                snapshot.storage.k8s.io/v1                  false        VolumeSnapshotContent volumesnapshots                       vs                      snapshot.storage.k8s.io/v1                  true         VolumeSnapshot volumeattachments                                         storage.k8s.io/v1                           false        VolumeAttachment 

Повторно прогоняем проверки PRE-FLIGHT:

#: curl <a href="https://docs.kasten.io/tools/k10_primer.sh">https://docs.kasten.io/tools/k10_primer.sh</a> | bash 

Ошибка по CRD-Base пропала, но появилась новая: CSI Provisioner doesn’t have VolumeSnapshotClass — Error. Все верно: CRD есть, а VolumeSnapshotClass отсутствует. Попробуем решить эту проблему.

Рабочий вариант — вручную подготовить манифест. Приведу пример для Cinder OpenStack. Подробнее о драйверах можно почитать на сайте.

apiVersion: snapshot.storage.k8s.io/v1 kind: VolumeSnapshotClass driver: cinder.csi.openstack.org metadata:   annotations:     k10.kasten.io/is-snapshot-class: "true"   name: csi-hostpath-snapclass-v1 deletionPolicy: Delete 

Применим манифест и проверим наличие ресурса VolumeSnapshotClass:

#: kubectl apply -f VolumeSnapshotClass.yaml volumesnapshotclass.snapshot.storage.k8s.io/cinder-csi-openstack-org created #: kubectl get -f VolumeSnapshotClass.yaml NAME                           DRIVER                         DELETIONPOLICY   AGE csi-hostpath-snapclass-v1   cinder.csi.openstack.org   Delete               20s 

Теперь можем снова запустить проверку PRE-FLIGHT:

#: curl https://docs.kasten.io/tools/k10_primer.sh | bash 

Все пункты должны быть в статусе ОК.

Дополнительно необходимо добавить аннотацию k10.kasten.io/volume-snapshot-class в default storage-class:

#: kubectl annotate storageclass default \     k10.kasten.io/volume-snapshot-class=csi-hostpath-snapclass-v1 

На этом предварительные настройки для работы Kasten K10 закончены. Теперь можем перейти к установке самого инструмента.

Установка Kasten K10

Добавляем репозиторий Kasten:

#: helm repo add kasten https://charts.kasten.io/ 

Создаем отдельное пространство имен:

#: kubectl create namespace kasten-io 

Устанавливаем Helm-чарт с включением sidecar kanister, который необходим для создания снапшотов. С версии Kasten K10 6.5.0 он по умолчанию выключен.

#: helm install k10 kasten/k10 --namespace=kasten-io --set injectKanisterSidecar.enabled=true --set-string injectKanisterSidecar.namespaceSelector.matchLabels.k10/injectKanisterSidecar=true 

Дожидаемся полной установки компонентов и пробрасываем порт на локальный хост:

#: kubectl get pods --namespace kasten-io --watch 

#: kubectl --namespace kasten-io port-forward service/gateway 8080:8000 

Теперь можем открыть веб-интерфейс по следующему адресу:

http://127.0.0.1:8080/k10/#/dashboard 

Настройка хранилища для резервных копий

Для создания резервных копий из снимков, перемещения приложений и их данных между кластерами и разными облаками, а также — последующего импорта/экспорта этих резервных копий в другой кластер используются профили хранилища.

Инициализация профиля S3

Как и в случае с K8up, настроим объектное хранилище в качестве основного для всех резервных копий. Это можно сделать как через веб-интерфейс, так и с помощью YAML-манифестов.

Создадим и настроим S3-профиль хранения резервных копий в объектном хранилище Selectel. Для этого перейдем на страницу профилей (http://127.0.0.1:8080/k10/#/profiles/location) — нас встретит такая страница:

Важно: в качестве endpoint нужно указать s3.ru-1.storage.selcloud.ru

Далее необходимо сохранить профиль. Если все сделали правильно, в списке увидите профиль со статусом VALID:

Настройка резервного копирования

Приступим к настройке периодических резервных копий нашего приложения.

Укажем label для пространства имен, куда установлено приложение. В нашем случае namespace=default:

#: kubectl label namespace default k10/injectKanisterSidecar=true 

Создадим Policy. Это можно сделать из веб-панели Kasten K10, но мы воспользуемся манифестом:

#sample-backup-action.yam apiVersion: config.kio.kasten.io/v1alpha1 kind: Policy metadata:   name: test   namespace: kasten-io spec:   frequency: "@hourly"   paused: false   actions:     - action: backup       backupParameters:         profile:           name: selectel-s3           namespace: kasten-io     - action: export       exportParameters:         frequency: "@hourly"         migrationToken:           name: test-migration-token           namespace: kasten-io         exportData:           enabled: true       retention: {}   retention:     hourly: 24     daily: 7     weekly: 4     monthly: 12     yearly: 7   selector:     matchExpressions:       - key: k10.kasten.io/appNamespace         operator: In         values:           - default 

Применим манифест в кластере:

#: kubectl create -f sample-backup-action.yaml 

Если все сделано правильно, должен создаться backupaction. Его можно вывести с помощью специальной команды:

#: kubectl get backupactions.actions.kio.kasten.io NAME              CREATED AT             STATE      PCT scheduled-pwbvk   2023-11-02T09:08:00Z   Complete   100 

Во вкладке Action веб-интерфейса можно проверить статус резервного копирования:

Копируем файл в приложение

Для проверки восстановления из резервной копии скопируем файл, например, Snapshot-class.yaml и вручную запустим бэкапирование. После удалим файл и восстановимся из резервной копии.

Копируем файл Snapshot-class.yaml:

#: kubectl cp Snapshot-class.yaml default/nginx-deployment-5dcc6d978b-lc96j:/usr/share/nginx/html/ 

Вручную запускаем резервное копирование. Это можно сделать на странице Policies:

Дожидаемся завершения резервного копирования (статус можно отслеживать на странице Dashboard), а после — удаляем файл из примонтированной папки в контейнере:

#: kubectl --namespace default exec -it nginx-deployment-5dcc6d978b-ps6rs -- /bin/sh Defaulted container "nginx-container" out of: nginx-container, kanister-sidecar # rm /usr/share/nginx/html/Snapshot-class.yaml 

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

Теперь можем проверить, восстанавливается ли система из резервной копии.

Перейдем во вкладку Application → Item-Menu → Restore:

Выберем нужную точку восстановления:

В появившемся окне Restore Point выберем приложение, поставим галочку рядом с Data-Only Restore и нажмем кнопку Restore:

Необходимо дождаться, пока задача на восстановление будет выполнена, и проверить, что ранее удаленный файл снова доступен в примонтированной папке:

Готово — резервное копирование на базе Kasten K10 работает!

Подводим итоги

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

В своей ML-платформе мы используем инструмент K8up для резервного копирования важной информации — например, служебных данных ClearML. В качестве хранилища используем объектное хранилище. K8up создает файловые копии один раз в день, а также проверяет их на целостность.

Если вам интересно посмотреть, как это работает на практике, протестируйте нашу ML-платформу. Мы разворачиваем ее индивидуально для каждого клиента и можем включить в сборку такие open source-инструменты, как ClearML или Kubeflow. В общем, все для того, чтобы вы смогли организовать полный цикл обучения и тестирования ML-моделей.


ссылка на оригинал статьи https://habr.com/ru/articles/777414/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *