Все должно быть под контролем. Резервное копирование для Kubernetes. Часть 2

от автора

В первой части мы рассмотрели подходы к созданию резервных копий контейнеров в кластере Kubernetes с использованием restic над каталогом данных и относительно новых возможностей CSI для создания и восстановления мгновенных снимков. Пришло время поговорить о возможностях автоматизации управления резервными копиями, о мониторинге процесса и иных важных DevOps-задачах.

Прежде всего для автоматизации нужно решить вопрос с доступом к данным и исключить необходимость установки дополнительных приложений (например. restic) на управляющий узел. В основном сценарии развертывания сервисы предполагается, что мы используем возможности Kubernetes по выделению емкости системы хранения через PersistentVoIumeClaim с режимом доступа ReadWriteOnce, который допускает монтирование раздела для записи только на один контейнер, что затрудняет восстановление данных из резервной копии. Также для создания резервной копии было бы желательно, чтобы доступ к данным был также у специального контейнера, содержащего все необходимые инструменты (например, Restic). Попробуем разобраться как это может быть реализовано и поговорим о концепции sidecar-контейнеров.

Прежде всего посмотрим на конфигурацию развертывания контейнера (может быть Deployment, DaemonSet или StatefulSet) и обнаружим, что в спецификации шаблона развертывания определение контейнеров перечисляется в списке containers. Такая схема определения позволяет запускать несколько контейнеров внутри одного Pod и все контейнеры будут разделять общий набор PersistentVolumeClaim и иметь доступ к опубликованным сетевым сервисам через localhost (все контейнеры в Pod разделяют общий ip-адрес). Таким образом можно добавить дополнительный контейнер с restic в развертывание и примонтировать к нему именованный раздел (в соответствии с названием в volumes или volumeClaimTemplates) и использовать его для получением доступа на чтение к данным из хранилища основного контейнера. Такой подход получил название «sideсаг-контейнер» и позволяет расширить функциональность основного контейнера без влияния на его доступность и конфигурацию. Так, мы можем добавить запуск образа контейнера, содержащего restic и подключить раздел с данными дополнительно к нему. Основная проблема состоит в том, что контейнер из образа restic/restic указывает точку входа в консольную утилиту restic и будет непрерывно перезапускаться. Чтобы этого избежать, можно поменять точку входа и отметить, что контейнер должен выполниться в режиме ожидания и мы в дальнейшем сможем подключиться к нему через kubectl ехес и выполнить команду restic backup когда наступит время для создания резервной копии. Также необходимо примонтировать раздел для хранения данных restic из системы хранения к соответствующему контейнеру, а также передать пароль для шифрования резервной копии через переменные окружения контейнера (может быть получен из Secret).

kubectl create secret -n postgres generic restic --from-literal=password=topsecret
postgres.yaml  apiVersion: v1 kind: Namespace metadata:   name: postgres --- apiVersion: apps/v1 kind: StatefulSet metadata:   namespace: postgres   name: postgres spec:   selector:     matchLabels:       app: postgres   serviceName: postgres   replicas: 1   template:     metadata:       labels:         app: postgres      spec:       containers:         - name: postgres           image: postgres:13           ports:             - name: postgres               containerPort: 5432               protocol: TCP           env:             - name: POSTGRES_USER               value: postgres             - name: PGUSER               value: postgres             - name: POSTGRES_DB               value: postgres             - name: PGDATA               value: /var/lib/postgresql/data/pgdata             - name: POSTGRES_PASSWORD               value: password             - name: POD_IP               valueFrom:                 fieldRef:                   apiVersion: v1                   fieldPath: status.podIP           livenessProbe:             exec:               command:                 - sh                 - -c                 - exec pg_isready --host $POD_IP             failureThreshold: 6             initialDelaySeconds: 60             periodSeconds: 10             successThreshold: 1             timeoutSeconds: 5           readinessProbe:             exec:               command:                 - sh                 - -c                 - exec pg_isready --host $POD_IP             failureThreshold: 3             initialDelaySeconds: 5             periodSeconds: 5             successThreshold: 1             timeoutSeconds: 3           volumeMounts:             - mountPath: /var/lib/postgresql/data/pgdata               name: postgres               subPath: postgres-db         - name: restic           image: restic/restic           command: ["/bin/sh"]           args: ["-c", "while true; do sleep 360; done"]           env:           - name: RESTIC_PASSWORD             valueFrom:               secretKeyRef:                 name: restic                 key: password           volumeMounts:             - mountPath: /data               name: postgres               subPath: postgres-db             - mountPath: /backup               name: backup   volumeClaimTemplates:   - metadata:       name: postgres     spec:       storageClassName: "standard"       accessModes: ["ReadWriteOnce"]       resources:         requests:           storage: 4Gi   - metadata:       name: backup     spec:       storageClassName: "standard"       accessModes: ["ReadWriteOnce"]       resources:         requests:           storage: 4Gi

После применения конфигурации можно обнаружить, что теперь Pod запущен с двумя контейнерами:

$ kubectl apply -f postgres.yaml $ kubectl get pod -n postgres  NAME         READY   STATUS    RESTARTS   AGE postgres-0   2/2     Running   0          11s

И теперь мы можем выполнить инициализацию каталога для управления резервными копиями (однократно) и создать мгновенный снимок данных с использованием sidecar-контейнера restic:

kubectl exec -it -n postgres postgres-0 -c restic -- restic init --password-command "echo $RESTIC_PASSWORD" -r /backup kubectl exec -it -n postgres postgres-0 -c restic -- restic backup --password-command "echo $RESTIC_PASSWORD" -r /backup /data

Для восстановления можно указать в target непосредственно каталог, куда примонтирован раздел оригинального контейнера (для sidecar ограничений на запись нет, поскольку все контейнеры разделяют единый PVC-раздел):

kubectl exec -it -n postgres postgres-0 -c restic -- restic restore --password-command "echo $RESTIC_PASSWORD" -r /backup latest --target /data

Для создания снимка (как и в первой части статьи) лучше, разумеется, использовать собственные инструменты исходного сервиса (например, pg_dump) и это также может быть предусмотрено в sidecar-контейнере. Образ для выгрузки состояния сервиса и восстановления может быть создан самостоятельно, например для PostgreSQL Dockerfile может выглядеть следующим образом:

FROM restic/restic  RUN apk update && \     apk add postgresql-client && \     echo '#!/bin/sh' >/opt/init.sh && \     echo 'restic init --password-command "echo $RESTIC_PASSWORD" -r /backup' >>/opt/init.sh && \     echo '#!/bin/sh' >/opt/backup.sh && \     echo 'mkdir -p /data' >>/opt/backup.sh && \     echo 'pg_dumpall -Upostgres -h localhost >/data/postgres.dump' >>/opt/backup.sh && \     echo 'restic backup --password-command "echo $RESTIC_PASSWORD" -r /backup /data' >>/opt/backup.sh && \     echo '#!/bin/sh' >/opt/restore.sh && \     echo 'restic restore --echo "echo $RESTIC_PASSWORD" -r /backup --target /' >>/opt/restore.sh && \     echo 'psql -Upostgres -h localhost </data/postgres.dump' >>/opt/restore.sh && \     chmod +x /opt/backup.sh && \     chmod +x /opt/restore.sh && \     chmod +x /opt/init.sh  ENTRYPOINT /bin/sh

Созданный контейнер также может быть добавлен как sidecar к основному процессу PostgreSQL и предусматривает инициализацию репозитория (kubectl exec -it -n postgres postgres-0 -c restic /opt/init.sh), создание резервной копии из дампа базы (kubectl exec -it -n postgres postgres-0 -c restic /opt/backup.sh) и восстановление из последнего доступного дампа из репозитория (kubectl exec -it -n postgres postgres-0 -c restic /opt/restore.sh). Аналогично могут быть созданы контейнеры для других сервисов, либо можно воспользоваться существующими на Docker Hub:

Основной недостаток рассмотренного выше решения — необходимость ручного изменения конфигурации развертывания сервисов, запуска сценариев создания резервной копии и восстановления, подключения дополнительных контейнеров с монтированием PersistentVolumeClaim к соответствующим каталогам, где ожидается получить данные в сценарии резервного копирования. Можно ли как-то автоматизировать этот процесс? Да, и в этом нам поможет Dynamic Admission Control (а именно использование Mutating Webhooks).

Регистрация ресурсов ValidatingWebhooks (admissionregistration.k8s.io/v1/ValidatingWebhookConfiguration) и MutatingWebhooks (admissionregistration.k8s.io/v1/MutatingWebhookConfiguration) позволяют добавить дополнительную обработку при создании/изменении/удалению ресурсов (например, Deployment) и добавляют возможности для дополнительных проверок конфигурации (при валидации) или изменения создаваемого ресурса «на лету» (при мутации). Конфигурация мутации определяет адрес API для вызова при возникновении подходящих условий и набор правил (rules), описывающих на какие группы и версии API применяется правило (apiGroups, apiVersions), какие операции отслеживаются (например, CREATE, UPDATE), какие типы ресурсов будут наблюдаться (resources). Часто установка вебхуков выполняется при установке операторов — контейнеров, самостоятельно управляющих ресурсами для развертывания управляемого сервиса и регистрирующих точки подключения к службам, а также дополнительные версии API и типы ресурсов (используется механизмы CRD — Custom Resource Definition). Кроме возможностей встраивания дополнительных действий в процесс развертывания ресурсов такой подход позволяет создавать предметно-специфические типы и метаданные для ресурсов (например, для описания расписания резервного копирования).

Рассмотрим несколько операторов, представляющих возможности для управления резервным копированием:

Velero

Продукт с открытым исходным кодом (ранее назывался Haptio Ark), позволяет выполнять резервное копирование ресурсов кластера Kubernetes и управлять резервными копиями PersistentVolume через утилиту командной строки velero. Для настройки процесса копирования (например, добавления pre- и post-сценариев для подготовки и очистки дампов) используются аннотации в метаданных развертывания. Поддерживаются также возможности CSI Snapshotting.

Для создания резервной копии используется restic и внешние хранилища, которые могут быть подключены через плагины. Создание резервной копии выполняется командой velero backup.

velero backup create postgres --ordered-resources 'statefulsets=postgres/postgres' --include-namespaces=postgres

Также можно запланировать резервное копирование по расписанию:

velero schedule create postgres --schedule="* * * * *"

Расписание задается в синтаксисе Cron (минуты часы день месяц день_недели).

K8Up

Оператор для управления ресурсами резервного копирования. Позволяет работать с PV-разделами с типом доступа ReadWriteMany.

Установка может быть выполнена через helm:

helm repo add appuio https://charts.appuio.ch helm repo update helm install k8up appuio/k8up --create-namespace --namespace k8up-operator

После установки регистрируется версия API k8up.io/v1 и дополнительные ресурсы:

  • Backup — задает конфигурацию репозитория для restic

  • PreBackupPod — описание sidecar-контейнера и команды для запуска для подготовки дампа системы (также может быть задано в аннотации k8up.io/backupcommand у любого развертывания)

  • Schedule — определить расписание резервного копирования (в синтаксисе Cron)

  • Archive — создание холодной копии restic-репозитория для долговременного хранения

Stash

Оператор использует механизмы Mutating Webhooks для установки sidecar-контейнеров с поддержкой restic в существующие/новые/изменяемые развертывания. Stash позволяет настраивать резервное копирование для PVC-разделов с режимом доступа ReadWriteOnce, поскольку управляющий контейнер добавляется к существующим подам и разделяет с ними общий доступ к хранилищам и ip-адрес.

$ helm repo add appscode https://charts.appscode.com/stable/ $ helm repo update $ helm install stash appscode/stash \   --version v2022.05.18 \   --namespace stash --create-namespace \   --set features.community=true               \   --set-file global.license=/path

Файл opensource лицензии запрашивается на сайте проекта. После установки регистрируется новая версия API stash.appscode.com/v1alpha1 и stash.appscode.com/v1beta1 и дополнительные ресурсы:

  • Repository — описание расположения репозитория для Restic (может быть один на все развертывания или отдельный под каждое из них), поддерживается S3, Google Cloud Storage, Microsoft Azure Storage, а также REST для отправки в произвольный веб-ресурс

  • Snapshot — обертка вокруг возможностей управления снимками Kubernetes

  • BackupConfiguration — конфигурация резервного копирования (также может включать в себя hooks для подготовки данных и их очистки после завершения процесса)

  • BackupBlueprint — создание шаблона для описания резервного копирования, на него можно ссылаться в развертываниях через аннотации:

    • stash.appscode.com/backup-blueprint — название шаблона

    • stash.appscode.com/schedule — расписание резервного копирования

  • Function — шаг подготовки резервной копии, может включать в себя создание дампа базы данных или иные операции, необходимые для создания согласованной выгрузки. Функция определяется со ссылкой на образ sidecar-контейнера (для PostgreSQL можно использовать stashed/postgres-stash) и командную строку для запуска действия. Также функция может использоваться для отправки метрик по резервному копированию в Prometheus (образ appscode/stash:0.10.0, команда update-status). При описании аргументов команды можно использовать переменные окружения, предоставленные Stash (в том числе, информация о расположении ресурса).

  • Task — объединение последовательности функций (перечисляются как step) с параметрами. Task может быть указан для исполнения в описании конфигурации BackupConfiguration или BackupBlueprint.

  • RestoreSession — выполнение восстановления из указанного репозитория в каталог (обычно связывается с PVC по имени, в соответствии с названием в volumes/persistentVolumeClaims развертывании).

Пример конфигурации для настройки резервного копирования базы данных:

apiVersion: stash.appscode.com/v1beta1 kind: BackupConfiguration metadata:   name: postgres   namespace: postgres spec:   driver: Restic   repository:     name: postgres   schedule: "0 * * * *"#каждый час   target:     alias: app-data     ref:       apiVersion: apps/v1#определение ресурса для резервного копирования       kind: StatefulSet       name: postgres     paths:     - /source/data#каталоги для копирования (обычно сюда монтируется PVC)     exclude:     - /source/data/tmp/*#исключения из копирования     volumeMounts:     - name: data       mountPath: /source/data   hooks:     preBackup:       exec:         command:#сценарий для запуска перед выполнением restic           - /bin/sh           - -c           - echo "Sample PreBackup hook demo"       containerName: my-app-container     postBackup:#сценарий для очистки после выполнения restic       exec:         command:           - /bin/sh           - -c           - echo "Sample PostBackup hook demo"       containerName: my-app-container   retentionPolicy:#политика удаления старых копий (здесь - оставить 5 последних)     name: 'keep-last-5'     keepLast: 5     prune: true

При выполнении резервной копии создается ресурс BackupSession, из которого можно получить информацию о состоянии. При успешном применении можно увидеть, что к развертыванию, которое указано в .spec.target.ref присоединяется sidecar-контейнер stashed/stash с запуском команды run-backup.

По умолчанию доступны 3 функции: update-status (отправка метрик в Prometheus), pvc-backup (сохранение данных из автономного Read-Write-Many PVC), pvc-restore (восстановление в автономный PVC). Для создания своей функции можно реализовать образ контейнера с точкой входа для создания дампа базы данных и отправки в restic-репозиторий, для настройки можно использовать переменные окружения:

  • REPOSITORY_PROVIDER — провайдер для репозитория (например, S3)

  • REPOSITORY_BUCKET — название S3 Bucket

  • REPOSITORY_ENDPOINT — адрес для подключения к S3 (или другому хранилищу)

  • REPOSITORY_PREFIX — префикс пути в репозитории

  • REPOSITORY_SECRET_NAME — название секрета, содержащего пароль к репозиторию

  • REPOSITORY_SECRET_NAMESPACE — пространство имен, где хранится секрет репозитория

  • RETENTION_KEEP_LAST — количество резервных копий для сохранения и ротации

Пример определения функции можно посмотреть здесь. Далее полученная функция интегрируется в задачу:

apiVersion: stash.appscode.com/v1beta1 kind: Task metadata:   name: postgres-backup spec:   steps:   - name: postgres-backup     params:     - name: secretVolume       value: secret-volume   - name: update-status     params:     - name: outputDir       value: /tmp/output     - name: secretVolume       value: secret-volume   volumes:   - name: secret-volume     secret:       secretName: ${REPOSITORY_SECRET_NAME}

И далее задача может быть добавлена к конфигурации резервного копирования:

apiVersion: stash.appscode.com/v1beta1 kind: BackupConfiguration metadata:   name: postgres   namespace: postgres spec:   driver: Restic   repository:     name: postgres   task:     name: postgres-backup     ...

Оператор Stash поддерживает присоединение sidecar-контейнеров к DaemonSet (добавляется к каждому запущенному Pod на всех допустимых узлах), StatefulSet (развертывание пересоздается с подключением дополнительного контейнера для каждой реплики сервиса), Deployment (дополнительный контейнер добавляется к существующему развертыванию без перезапуска).

Итак мы рассмотрели различные подходы к управлению резервным копированием, включая автоматическое управление через CRD-ресурсы в операторах Stash и k8up. Сейчас нет общепринятого решения для управления резервными копиями и появляется большое количество инструментов, основанных на CSI Snapshotting (например, Gemini или TrilioVault), но мы рассмотрели наиболее популярные и функциональные продукты, которые могут помочь вам настроить предсказуемое и надежное резервное копирование и избавить от переживаний о возможной потере данных.

Так;е хочу напомнить о том, что уже 2 июня мои коллеги из OTUS проведут бесплатный урок по теме: «Контроллеры репликации, сеты репликации и балансировка нагрузки». Регистрация на урок доступна по ссылке ниже.

Зарегистрироваться на бесплатный урок.


ссылка на оригинал статьи https://habr.com/ru/company/otus/blog/668308/


Комментарии

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

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