В первой части мы рассмотрели подходы к созданию резервных копий контейнеров в кластере 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:
-
PostgreSQL — https://hub.docker.com/r/interaction/restic-pg-dump
-
MariaDB — https://hub.docker.com/r/hochri/restic_maria_backup
Основной недостаток рассмотренного выше решения — необходимость ручного изменения конфигурации развертывания сервисов, запуска сценариев создания резервной копии и восстановления, подключения дополнительных контейнеров с монтированием 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/
Добавить комментарий