Снапшоты в Kubernetes: что это и как ими пользоваться

С появлением snapshot-controller в Kubernetes появилась возможность создавать снапшоты для совместимых с ними CSI-драйверов и облачных провайдеров.

image

Как и всё в Kubernetes, имплементация API является универсальной и не зависит от какого-либо вендора, что позволяет нам рассмотреть данный функционал в общем порядке. Как же устроены снапшоты и какую пользу они могут принести пользователям Kubernetes?

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

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

Для того, чтобы возможность создания снапшотов заработала, в Kubernetes-кластере должен быть установлен snapshot-controller (общий компонент для всех CSI-драйверов), а также соответствующие CRD:


  • VolumeSnapshotClass — аналог StorageClass для снапшотов;
  • VolumeSnapshotContent — аналог PV для снапшотов;
  • VolumeSnapshot — аналог PVC для снапшотов.

Помимо этого, наш CSI-драйвер должен иметь соответствующий контроллер csi-snapshotter и поддерживать создание снапшотов.

Логика простая. У нас есть несколько сущностей, в VolumeSnapshotClass описываются параметры для создания снапшотов. В первую очередь это используемый CSI-драйвер, а также там можно указать дополнительные настройки, например, должны ли снапшоты быть инкрементальными и где они должны храниться.

При создании снапшота VolumeSnapshot необходимо указать PersistentVolumeClaim, для которого этот снапшот будет создаваться.

Когда CSI-драйвер успешно выполняет процедуру создания снапшота, он создаёт в кластере ресурс, который называется VolumeSnapshotContent и указывает данные созданного снапшота в нём (как правило, его ID).

После этого, как в случае с PV и PVC, происходит процедура Bound, когда snapshot-controller связывает наш VolumeSnapshot с VolumeSnapshotContent.

При создании нового PersistentVolume можно указать созданный VolumeSnapshot в dataSource, и тогда в нём появятся данные из нашего снапшота.

Параметры для создания снапшотов описываются в VolumeSnashotClass. Туда передается имя CSI-драйвера и дополнительные параметры в зависимости от вашей СХД и облачного провайдера. Приведу несколько примеров:

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

asciicast

В первом кейсе представим, что мы хотим иметь некоторый шаблон 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, которые можем сразу использовать. Механизм снапшотов здесь работает абсолютно прозрачно, именно поэтому нам даже не пришлось использовать какой-либо из описанных выше ресурсов.

asciicast

Этот кейс демонстрирует, как можно безопасно протестировать миграцию БД на живых данных, не затрагивая 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

asciicast

Снапшоты являются неотъемлемой частью для создания консистентных бэкапов в работающем окружении. Без снапшотов априори не получится создать такой бэкап 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

Недавно мы внедрили модуль снапшотов для совместимых CSI-драйверов в нашу Kubernetes-платформу Deckhouse. Начиная с релиза v1.33 он включается автоматически для всех поддерживаемых облачных провайдеров и систем хранения, не требуя настройки.

В документации можно найти больше примеров использования.

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

Спасибо за внимание — надеюсь, со снапшотами ваша жизнь станет проще и лучше! :)

Читайте также в нашем блоге:


© Habrahabr.ru