С появлением snapshot-controller в Kubernetes появилась возможность создавать снапшоты для совместимых с ними CSI-драйверов и облачных провайдеров.
Как и всё в Kubernetes, имплементация API является универсальной и не зависит от какого-либо вендора, что позволяет нам рассмотреть данный функционал в общем порядке. Как же устроены снапшоты и какую пользу они могут принести пользователям Kubernetes?
Введение
Для начала давайте разберёмся, что такое снапшоты. Снапшот — это возможность сделать моментальный снимок состояния файловой системы для последующего сохранения или восстановления из него. Создание снапшота занимает крайне малое время. После создания снапшота все изменения в оригинальной файловой системе начинают записываться в отдельные блоки.
Так как данные снапшота хранятся в том же хранилище, что и оригинальные данные, снапшоты не заменяют полноценный бэкап, но снятие бэкапа из снапшота, а не из живых данных, позволяет сделать его более консистентным. Обусловлено это тем, что при создании снапшота мы можем гарантировать актуальность всех данных на тот момент, когда этот снапшот был сделан.
Для того, чтобы возможность создания снапшотов заработала, в Kubernetes-кластере должен быть установлен snapshot-controller (общий компонент для всех CSI-драйверов), а также соответствующие CRD:
VolumeSnapshotClass
– аналогStorageClass
для снапшотов;VolumeSnapshotContent
– аналог PV для снапшотов;VolumeSnapshot
– аналог PVC для снапшотов.
Помимо этого, наш CSI-драйвер должен иметь соответствующий контроллер csi-snapshotter
и поддерживать создание снапшотов.
Как работают снапшоты в Kuberbetes?
Логика простая. У нас есть несколько сущностей, в VolumeSnapshotClass
описываются параметры для создания снапшотов. В первую очередь это используемый CSI-драйвер, а также там можно указать дополнительные настройки, например, должны ли снапшоты быть инкрементальными и где они должны храниться.
При создании снапшота VolumeSnapshot
необходимо указать PersistentVolumeClaim
, для которого этот снапшот будет создаваться.
Когда CSI-драйвер успешно выполняет процедуру создания снапшота, он создаёт в кластере ресурс, который называется VolumeSnapshotContent
и указывает данные созданного снапшота в нём (как правило, его ID).
После этого, как в случае с PV
и PVC
, происходит процедура Bound, когда snapshot-controller связывает наш VolumeSnapshot
с VolumeSnapshotContent
.
При создании нового PersistentVolume
можно указать созданный VolumeSnapshot
в dataSource
, и тогда в нём появятся данные из нашего снапшота.
Конфигурация
Параметры для создания снапшотов описываются в VolumeSnashotClass
. Туда передается имя CSI-драйвера и дополнительные параметры в зависимости от вашей СХД и облачного провайдера. Приведу несколько примеров:
После того, как создан VolumeSnapshotClass
, можно приступать к использованию снапшотов. Рассмотрим несколько кейсов.
Кейс 1: Темплейты PVC
В первом кейсе представим, что мы хотим иметь некоторый шаблон PVC с данными и клонировать его по мере необходимости. Это бывает удобно в следующих случаях:
- быстрое создание development-окружений с данными;
- эффективная обработка данных сразу несколькими Pod’ами на разных узлах.
Вся магия заключается в том, что создаётся стандартный PVC, заполняется нужными данными, а когда нам потребуется его склонировать, создаём ещё один PVC, где в качестве источника указываем оригинальный:
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: pvc-worker1
spec:
storageClassName: linstor-ssd-lvmthin-r2
dataSource:
name: pvc-template
kind: PersistentVolumeClaim
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 10Gi
В итоге получаем клон с данными из оригинального PVC, которые можем сразу использовать. Механизм снапшотов здесь работает абсолютно прозрачно, именно поэтому нам даже не пришлось использовать какой-либо из описанных выше ресурсов.
Кейс 2: Снапшоты для тестирования
Этот кейс демонстрирует, как можно безопасно протестировать миграцию БД на живых данных, не затрагивая production.
Мы точно так же создаём клон уже существующего PVC, с которым работает наше приложение, и запускаем новую версию приложения уже со склонированным PVC, чтобы протестировать обновление. В процессе можно обнаружить, что что-то пошло не так, создать новый клон и попробовать ещё раз.
Когда всё протестировано, можем выкатывать в production. Но на всякий случай сначала создадим снапшот mypvc-before-upgrade
, чтобы всегда была возможность вернуться к состоянию до обновления. Снапшоты создаются с помощью сущности VolumeSnapshots
, где просто указывается PVC, для которого будем делать снапшот:
apiVersion: snapshot.storage.k8s.io/v1
kind: VolumeSnapshot
metadata:
name: mypvc-before-upgrade
spec:
volumeSnapshotClassName: linstor
source:
persistentVolumeClaimName: mypvc
После обновления, если возникнет такая необходимость, всегда можно вернуться к этому состоянию, указав снапшот в качестве источника для создания PVC:
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: mypvc
spec:
storageClassName: linstor-ssd-lvmthin-r2
dataSource:
name: mypvc-before-upgrade
kind: VolumeSnapshot
apiGroup: snapshot.storage.k8s.io
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 10Gi
Кейс 3: Снапшоты для консистентного бэкапа
Снапшоты являются неотъемлемой частью для создания консистентных бэкапов в работающем окружении. Без снапшотов априори не получится создать такой бэкап PVC без приостановки работы приложения.
Причина заключается в том, что при снятии бэкапа всего тома с работающего приложения велика вероятность перезаписи отдельных частей этого тома. Чтобы этого избежать, можно сделать снапшот и бэкапить уже его.
Есть различные решения, которые позволяют бэкапить в Kubernetes, учитывая логику вашего приложения и/или используя механизм снапшотов. Одно из таких решений — Velero — позволяет автоматизировать использование снапшотов, назначать дополнительные хуки для сброса данных на диск, а также приостанавливать и возобновлять работу приложения для лучшей консистентности бэкапа.
Тем не менее, некоторые вендоры имеют встроенный функционал для создания бэкапов. Так, например, LINSTOR позволяет автоматически отправлять снапшоты на удалённый S3-сервер. Поддерживаются как полные, так и инкрементальные бэкапы.
Для того, чтобы воспользоваться этой возможностью, потребуется создать отдельный VolumeSnapshotClass
, где указать все необходимые опции для доступа к удаленному S3-серверу:
---
kind: VolumeSnapshotClass
apiVersion: snapshot.storage.k8s.io/v1
metadata:
name: linstor-minio
driver: linstor.csi.linbit.com
deletionPolicy: Retain
parameters:
snap.linstor.csi.linbit.com/type: S3
snap.linstor.csi.linbit.com/remote-name: minio
snap.linstor.csi.linbit.com/allow-incremental: "false"
snap.linstor.csi.linbit.com/s3-bucket: foo
snap.linstor.csi.linbit.com/s3-endpoint: XX.XXX.XX.XXX.nip.io
snap.linstor.csi.linbit.com/s3-signing-region: minio
snap.linstor.csi.linbit.com/s3-use-path-style: "true"
csi.storage.k8s.io/snapshotter-secret-name: linstor-minio
csi.storage.k8s.io/snapshotter-secret-namespace: minio
---
kind: Secret
apiVersion: v1
metadata:
name: linstor-minio
namespace: minio
immutable: true
type: linstor.csi.linbit.com/s3-credentials.v1
stringData:
access-key: minio
secret-key: minio123
Теперь при создании снапшота он будет отправлен на удалённый S3-сервер:
---
apiVersion: snapshot.storage.k8s.io/v1
kind: VolumeSnapshot
metadata:
name: mydb-backup1
spec:
volumeSnapshotClassName: linstor-minio
source:
persistentVolumeClaimName: db-data
Что примечательно, восстановить мы его можем даже в другом Kubernetes-кластере. Но для этого, помимо VolumeSnapshotClass
, понадобится определить еще VolumeSnapshotContent
и VolumeSnapshot
для снапшота:
---
apiVersion: snapshot.storage.k8s.io/v1
kind: VolumeSnapshotContent
metadata:
name: example-backup-from-s3
spec:
deletionPolicy: Delete
driver: linstor.csi.linbit.com
source:
snapshotHandle: snapshot-0a829b3f-9e4a-4c4e-849b-2a22c4a3449a
volumeSnapshotClassName: linstor-minio
volumeSnapshotRef:
apiVersion: snapshot.storage.k8s.io/v1
kind: VolumeSnapshot
name: example-backup-from-s3
namespace: new-cluster
---
apiVersion: snapshot.storage.k8s.io/v1
kind: VolumeSnapshot
metadata:
name: example-backup-from-s3
spec:
source:
volumeSnapshotContentName: example-backup-from-s3
volumeSnapshotClassName: linstor-minio
При создании VolumeSnapshotContent
нам необходимо указать ID снапшота в системе хранения, передав его в параметре snapshotHandle
.
Теперь можно создать новый PVC из бэкапа:
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: restored-data
namespace: new-cluster
spec:
storageClassName: linstor-ssd-lvmthin-r2
dataSource:
name: example-backup-from-s3
kind: VolumeSnapshot
apiGroup: snapshot.storage.k8s.io
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 10Gi
Модуль снапшотов в Deckhouse
Недавно мы внедрили модуль снапшотов для совместимых CSI-драйверов в нашу Kubernetes-платформу Deckhouse. Начиная с релиза v1.33 он включается автоматически для всех поддерживаемых облачных провайдеров и систем хранения, не требуя настройки.
В документации можно найти больше примеров использования.
Заключение
Снапшоты позволяют более эффективно использовать возможности вашего хранилища, позволяя создавать консистентные бэкапы, клонировать тома, а также избежать необходимости полного копирования ваших данных в случаях, когда это в действительности не требуется.
Спасибо за внимание — надеюсь, со снапшотами ваша жизнь станет проще и лучше! :)
P.S.
Читайте также в нашем блоге:
Автор: Andrei Kvapil