Прим. перев.: оригинальная статья была недавно опубликована в блоге Kubernetes и написана сотрудниками компаний Google и Huawei (Jing Xu, Xing Yang, Saad Ali), активную деятельность которых вы непременно видели в GitHub'е проекта, если когда-либо интересовались фичами и проблемами K8s, связанными с хранением данных. Инженеры рассказывают о предназначении снапшотов томов (volume snapshots), их текущих возможностях и основах работы с ними.

Kubernetes v1.12 представил альфа-версию поддержки снапшотов для томов. Эта возможность позволяет создавать и удалять снапшоты томов, а также создавать новые тома из снапшотов «родными» средствами системы — через Kubernetes API.

Что такое снапшот?


Многие системы хранения (вроде Google Cloud Persistent Disks, Amazon Elastic Block Storage и многочисленных систем хранения категории on-premise) предлагают возможность создания снапшота («снимка») для постоянного тома. Снапшот представляет собой копию тома на конкретный момент времени. Его можно использовать для provision'а нового тома (уже наполненного данными из снапшота) или восстановления существующего тома к предыдущему состоянию (которое представлено в снапшоте).

Зачем добавлять снапшоты в Kubernetes?


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

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

Группа Kubernetes Storage SIG определила операции со снашпотами как критически важные возможности для множества рабочих нагрузок категории stateful. Например, администратор баз данных может захотеть снапшот своей БД перед тем, как выполнять с ней какую-то операцию.

Получив в Kubernetes API стандартный способ для вызова операций над снапшотами, пользователи Kubernetes могут работать с ними без необходимости в обходных путях (и ручного вызова операций, которые специфичны для системы хранения). Вместо этого, пользователям дали возможность встраивать операции над снашпотами в свои инструменты и политики со спокойным пониманием, что всё будет работать с любыми кластерами Kubernetes вне зависимости от нижележащего хранилища.

Ко всему прочему, эти примитивы Kubernetes функционируют как базовые строительные блоки, открывающие возможности для разработки более продвинутых фич enterprise-уровня по управлению хранилищем — например, по защите, репликации и миграции данных.

Какие плагины томов поддерживают снапшоты в Kubernetes?


Kubernetes поддерживает три типа плагинов томов: in-tree, Flex и CSI. Подробности смотрите в Kubernetes Volume Plugin FAQ.

Снапшоты поддерживаются только для драйверов CSI (они не поддерживаются ни для in-tree, ни для Flex). Чтобы воспользоваться этой возможностью, убедитесь, что в кластере Kubernetes развёрнут CSI-драйвер, в котором реализована поддержка снапшотов.

К моменту этой публикации в блоге (9 октября 2018 г. — прим перев.), снапшоты поддерживаются следующими CSI-драйверами:


Поддержка снапшотов для других драйверов находится в разработке и вскоре должна появиться. Больше подробностей о CSI и о том, как разворачивать CSI-драйверы, описано в публикации «Container Storage Interface (CSI) for Kubernetes Goes Beta» (а также см. наш перевод заметки «Понимаем Container Storage Interface (в Kubernetes и не только)» — прим. перев.).

Kubernetes API для снапшотов


Для управления снапшотами в Kubernetes Volume Snapshots представлено три новых объекта API аналогично тому, как это сделано в API для управления постоянными томами (Kubernetes Persistent Volumes):

  • VolumeSnapshot
    • Создаётся пользователем Kubernetes для запроса на создание снапшота для указанного тома. Содержит информацию об операции над снапшотом, такую как timestamp снятия снапшота и готов ли он к использованию.
    • Подобно объекту PersistentVolumeClaim, создание и удаление этого объекта представляет собой желание пользователя создать или удалить ресурс кластера (снапшот).
  • VolumeSnapshotContent
    • Создаётся драйвером CSI, когда снапшот был успешно создан. Содержит информацию о снапшоте включая его ID.
    • Подобно объекту PersistentVolume, представляет уже обслуживаемый кластером ресурс (снапшот).
    • Как и объекты PersistentVolumeClaim и PersistentVolume, когда снапшот создан, объект VolumeSnapshotContent привязывается к VolumeSnapshot, для которого он был создан (используется связь один-к-одному — one-to-one mapping).
  • VolumeSnapshotClass
    • Задаётся администраторами кластера для описания, какие снапшоты могут быть созданы. Включает в себя информацию о драйвере, секреты для доступа к снашпотам и т.п.

Важно отметить, что — в отличие от основных объектов Persistent Volume в Kubernetes — эти объекты снапшотов определены как CustomResourceDefinitions (CRDs). Проект Kubernetes постепенно уходит от предварительно определённых типов ресурсов в API Server, приближаясь к модели, в которой API Server не зависит от объектов API. Такой подход позволяет повторно использовать сервер API в других проектах (помимо Kubernetes), а consumer'ы (вроде того же Kubernetes) могут устанавливать нужные им типы ресурсов как CRD.

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

В дополнение к этим новым объектам у уже существующего PersistentVolumeClaim появилось новое поле DataSource:

type PersistentVolumeClaimSpec struct {
	AccessModes []PersistentVolumeAccessMode
	Selector *metav1.LabelSelector
	Resources ResourceRequirements
	VolumeName string
	StorageClassName *string
	VolumeMode *PersistentVolumeMode
	DataSource *TypedLocalObjectReference
}

Это поле (в статусе альфа-версии) позволяет при создании нового тома автоматически наполнять его данными из существующего снапшота.

Требования к снапшотам Kubernetes


Перед использованием снапшотов томов в Kubernetes необходимо:

  • убедиться, что CSI-драйвер, реализующий снапшоты, развёрнут и запущен на кластере;
  • включить функцию Kubernetes Volume Snapshotting через новый feature gate (по умолчанию отключён для альфа-версии):
    • выставить следующий флаг для исполняемого файла API Server: --feature-gates=VolumeSnapshotDataSource=true

Перед созданием снашпота необходимо также определить используемый CSI-драйвер, что осуществляется созданием объекта VolumeSnapshotClass и указанием CSI-драйвера в поле snapshotter. В приведённом ниже примере с VolumeSnapshotClass таким драйвером является com.example.csi-driver. Для каждого поставщика снапшотов требуется иметь как минимум один объект VolumeSnapshotClass. Также возможно определение VolumeSnapshotClass по умолчанию для каждого CSI-драйвера — это делается установкой аннотации snapshot.storage.kubernetes.io/is-default-class: "true" в определении класса:

apiVersion: snapshot.storage.k8s.io/v1alpha1
kind: VolumeSnapshotClass
metadata:
  name: default-snapclass
  annotations:
    snapshot.storage.kubernetes.io/is-default-class: "true"
snapshotter: com.example.csi-driver

apiVersion: snapshot.storage.k8s.io/v1alpha1
kind: VolumeSnapshotClass
metadata:
  name: csi-snapclass
snapshotter: com.example.csi-driver
parameters:
  fakeSnapshotOption: foo
  csiSnapshotterSecretName: csi-secret
  csiSnapshotterSecretNamespace: csi-namespace

Все обязательные параметры должны быть установлены в соответствии с документацией CSI-драйвера. В примере выше CSI-драйверу во время создания и удаления снапшота будут переданы параметр fakeSnapshotOption: foo и все упомянутые секреты. CSI external-snapshotter по умолчанию сохраняет ключи параметров csiSnapshotterSecretName и csiSnapshotterSecretNamespace.

Наконец, перед созданием снапшота необходимо создать том через CSI-драйвер и наполнить его данными, которые вы хотите там увидеть (см. эту публикацию с подробностями о том, как использовать тома CSI).

Создание нового снапшота в Kubernetes


Как только объект VolumeSnapshotClass определён и есть том, с которого вы хотите снять снапшот, можно выполнить эту операцию, создав объект VolumeSnapshot.

Источник для снапшота определяется двумя параметрами:

  • kind — здесь указывается PersistentVolumeClaim;
  • name — собственно имя объекта PVC.

Подразумевается, что пространство имён тома, для которого создаётся снапшот, определяется пространством имён объекта VolumeSnapshot.

apiVersion: snapshot.storage.k8s.io/v1alpha1
kind: VolumeSnapshot
metadata:
  name: new-snapshot-demo
  namespace: demo-namespace
spec:
  snapshotClassName: csi-snapclass
  source:
    name: mypvc
    kind: PersistentVolumeClaim

В спецификации VolumeSnapshot может быть определён VolumeSnapshotClass, в котором содержится информация о том, какой CSI-драйвер будет задействован для создания снапшота. Как уже сообщалось, после создания объекта VolumeSnapshot параметр fakeSnapshotOption: foo и все упомянутые секреты VolumeSnapshotClass передаются плагину CSI com.example.csi-driver в вызове CreateSnapshot.

В ответ на такой запрос CSI-драйвер делает снапшот тома и затем автоматически создаёт объект VolumeSnapshotContent, который представляет новый снапшот, и привязывает этот объект к VolumeSnapshot, делая его готовым к использованию. Если CSI-драйвер не может создать снапшот и возвращает ошибку, контроллер снапшотов сообщает об этой ошибке в статусе объекта VolumeSnapshot и не предпринимает новых попыток (такое поведение отличается от других контроллеров в Kubernetes — оно реализовано для того, чтобы не создавать снапшот в непредсказуемое время).

Если класс снапшота не задан, external-snapshotter попытается найти класс по умолчанию и воспользоваться им для создаваемого снапшота. При этом CSI-драйвер, на который указывает snapshotter в классе по умолчанию, должен соответствовать CSI-драйверу, на который указывает provisioner в классе хранилища PVC.

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

Убедиться, что объект VolumeSnapshot создан и связан с VolumeSnapshotContent, можно командой kubectl describe volumesnapshot:

  • Ready должно иметь значение true в Status, что будет указывать на готовность снапшота тома к использованию.
  • Поле Creation Time показывает, когда снапшот был в действительности сделан.
  • Поле Restore Size — минимальный размер тома для восстановления снапшота.
  • Поле Snapshot Content Name в спецификации указывает на объект VolumeSnapshotContent, созданный для этого снапшота.

Импортирование существующего снапшота в Kubernetes


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

Когда объект VolumeSnapshotContent создан, пользователь может создать другой объект — VolumeSnapshot, — который будет указывать на него. Контроллер external-snapshotter пометит снапшот как готовый после проверки его на существование и корректности связи между объектами VolumeSnapshot и VolumeSnapshotContent. Снапшот готов для использования в Kubernetes, когда эта связь установлена.

Объект VolumeSnapshotContent должен создаваться со следующими полями, представляющими предварительно подготовленный (pre-provisioned) снапшот:

  • csiVolumeSnapshotSource — информация, идентифицирующая снапшот:
    • snapshotHandle — название/идентификатор для снапшота. Обязательное поле;
    • driver — CSI-драйвер, используемый для работы с этим томом. Обязательное поле. Должен соответствовать названию snapshotter в контроллере (snapshot controller);
    • creationTime и restoreSize — для предварительно подготовленных томов эти поля не являются обязательными. Контроллер external-snapshotter автоматически обновит их после создания снапшота.
  • volumeSnapshotRef — указатель на объект VolumeSnapshot, к которому этот объект (т.е. VolumeSnapshotContent) должен быть привязан:
    • name и namespace — имя и пространство имён объекта VolumeSnapshot, содержимое которого привязывается;
    • UID — необязательное (для предварительно подготовленных томов) поле. Контроллер external-snapshotter автоматически обновит это поле после привязывания. Если пользователь определяет это поле, необходимо убедиться, что оно соответствует UID снапшота, для которого происходит связывание. Если этого соответствия нет, содержимое считается неактуальным (orphan-объектом) и посему контроллер удалит и его, и связанный с ним снапшот.
  • snapshotClassName — опциональное поле. Контроллер external-snapshotter автоматически обновит его после привязывания.

apiVersion: snapshot.storage.k8s.io/v1alpha1
kind: VolumeSnapshotContent
metadata:
  name: static-snapshot-content
spec:
  csiVolumeSnapshotSource:
    driver: com.example.csi-driver
    snapshotHandle: snapshotcontent-example-id
  volumeSnapshotRef:
    kind: VolumeSnapshot
    name: static-snapshot-demo
    namespace: demo-namespace

Объект VolumeSnapshot должен быть создан, чтобы пользователь мог работать со снапшотом. В нём:

  • snapshotClassName — название класса снапшота тома. Опциональное поле. Если установлено, поле snapshotter в классе снапшота должно соответствовать названию контроллера снапшота. Если не установлено, контроллер будет искать класс снапшота по умолчанию;
  • snapshotContentName — название содержимого снапшота тома. Обязательное поле для предварительно подготовленных томов.

apiVersion: snapshot.storage.k8s.io/v1alpha1
kind: VolumeSnapshot
metadata:
  name: static-snapshot-demo
  namespace: demo-namespace
spec:
  snapshotClassName: csi-snapclass
  snapshotContentName: static-snapshot-content

Когда эти объекты созданы, snapshot controller свяжет их, установит поле ReadyStatus) как True, указывая на готовность снапшота к использованию.

Подготовка нового тома из снапшота в Kubernetes


Чтобы создать новый том, предварительно наполненный данными из объекта снапшота, воспользуйтесь новым полем dataSource в PersistentVolumeClaim. У него три параметра:

  • name — название объекта VolumeSnapshot, представляющего снапшот-источник;
  • kind — должен быть задан как VolumeSnapshot;
  • apiGroup — должен быть snapshot.storage.k8s.io.

Предполагается, что пространство имён источника — объекта VolumeSnapshot — соответствует пространству имён объекта PersistentVolumeClaim.

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: pvc-restore
  Namespace: demo-namespace
spec:
  storageClassName: csi-storageclass
  dataSource:
    name: new-snapshot-demo
    kind: VolumeSnapshot
    apiGroup: snapshot.storage.k8s.io
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 1Gi

Когда объект PersistentVolumeClaim будет создан, он вызовет provisioning нового тома, предварительно наполненного данными из указанного снапшота.

Как добавить поддержку снапшотов в мой CSI-драйвер, если я разработчик хранилища?


Для обеспечения поддержки снапшотов в CSI-драйвер должны быть добавлены дополнительные возможности контроллера: CREATE_DELETE_SNAPSHOT и LIST_SNAPSHOTS, — а также реализованы дополнительные RPC контроллера: CreateSnapshot, DeleteSnapshot, ListSnapshots. Подробности смотрите в спецификации CSI.

Хотя Kubernetes даёт самые минимальные указания по пакетированию и развёртыванию CSI Volume Driver, имеется рекомендуемый механизм для деплоя произвольного контейнеризированного CSI-драйвера в Kubernetes, чтобы упростить этот процесс.

Как часть рекомендуемого процесса деплоя команда Kubernetes предлагает использовать множество sidecar- (т.е. вспомогательных) контейнеров, в том числе — sidecar-контейнер с external-snapshotter.

Упомянутый external-snapshotter следит в API Server за объектами VolumeSnapshot и VolumeSnapshotContent, вызывая операции CreateSnapshot и DeleteSnapshot для CSI endpoint. Sidecar-контейнер с CSI external-provisioner также был обновлён для поддержки восстановления тома из снапшота с использованием нового поля dataSource в PVC.

Для поддержки возможностей снапшота производителям хранилищ рекомендуется деплоить sidecar-контейнеры с external-snapshotter в дополнение к external provisioner, а CSI-драйвер помещать в StatefulSet, как показано на схеме ниже:



В этом примере Deployment представлены два sidecar-контейнера, external provisioner и external snapshotter, а CSI-драйверы деплоятся вместе с плагином CSI hostpath в рамках StatefulSet-пода. CSI hostpath — это пример плагина, который не предназначен для использования в production.

Каковы ограничения альфа-версии?


Альфа-версия реализации снапшотов в Kubernetes имеет следующие ограничения:

  • Не поддерживается откат существующего тома к предыдущему состоянию, представленному снапшотом (поддерживается только provisioning нового тома из снапшота).
  • Не поддерживается «in-place restore» существующего PersistentVolumeClaim из снапшота: т.е. работает provisioning нового тома из снапшота, но не обновление существующего PersistentVolumeClaim таким образом, чтобы он указывал на новый том и PVC откатывался к более раннему состоянию (поддерживается только использование нового тома, созданного из снапшота через новый PV/PVC).
  • Гарантии по консистентности снапшота не выходят за рамки гарантий, предоставляемых системой хранения (например, целостность при падении).

Что дальше?


Команда Kubernetes планирует довести реализацию снапшотов для CSI до бета-версии в релизах 1.13 или 1.14 в зависимости от полученной обратной связи и адаптации технологии.

Как узнать больше подробностей?


Смотрите дополнительную документацию по снапшотам на k8s.io/docs/concepts/storage/volume-snapshots и kubernetes-csi.github.io/docs.

P.S. от переводчика


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

Комментарии (0)