Стадия жизненного цикла модуля: Общедоступная версия
У модуля есть требования для установки
Использование снапшотов
Чтобы использовать снапшоты, укажите конкретный VolumeSnapshotClass.
Чтобы получить список доступных VolumeSnapshotClass в вашем кластере, выполните:
d8 k get volumesnapshotclasses.snapshot.storage.k8s.ioЗатем вы сможете использовать VolumeSnapshotClass для создания снапшота из существующего тома:
apiVersion: snapshot.storage.k8s.io/v1
kind: VolumeSnapshot
metadata:
name: my-first-snapshot
spec:
volumeSnapshotClassName: sds-replicated-volume
source:
persistentVolumeClaimName: my-first-volumeСпустя небольшой промежуток времени снапшот будет готов:
$ d8 k describe volumesnapshots.snapshot.storage.k8s.io my-first-snapshot
...
Spec:
Source:
Persistent Volume Claim Name: my-first-snapshot
Volume Snapshot Class Name: sds-replicated-volume
Status:
Bound Volume Snapshot Content Name: snapcontent-b6072ab7-6ddf-482b-a4e3-693088136d2c
Creation Time: 2020-06-04T13:02:28Z
Ready To Use: true
Restore Size: 500Mi
Можно восстановить содержимое этого снапшота, создав новый PVC. Для этого необходимо указать снапшот в качестве источника:
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: my-first-volume-from-snapshot
spec:
storageClassName: sds-replicated-volume-data-r2
dataSource:
name: my-first-snapshot
kind: VolumeSnapshot
apiGroup: snapshot.storage.k8s.io
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 500MiКлонирование CSI-томов
На основе концепции снапшотов также можно выполнить клонирование Persistent Volume, а именно, существующих PersistentVolumeClaim (PVC). Однако спецификация CSI допускает ряд ограничений при клонировании томов в неймспейсах или StorageClass-ах, отличных от оригинального PVC. Подробнее об ограничениях см. в документации Kubernetes.
Чтобы клонировать том, создайте новый PVC и укажите исходный PVC в dataSource:
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: my-cloned-pvc
spec:
storageClassName: sds-replicated-volume-data-r2
dataSource:
name: my-origin-pvc
kind: PersistentVolumeClaim
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 500MiЭкспорт и импорт содержимого томов по HTTP
Создание ресурсов DataExport и DataImport с помощью утилиты d8
Для создания и управления ресурсами DataExport и DataImport используется утилита d8.
Создание DataExport — целевой том передаётся позиционным аргументом <тип>/<имя>:
d8 data export create -n <namespace> <имя DataExport> <тип>/<имя> --ttl 5m --publish=(true/false)Поддерживаемые типы целевых ресурсов: pvc/persistentvolumeclaim, vs/volumesnapshot, vd/virtualdisk, vds/virtualdisksnapshot.
Создание DataImport — целевой PVC задаётся манифестом через -f (позиционного аргумента тома нет):
d8 data import create -n <namespace> <имя DataImport> -f <манифест-pvc> --ttl 2m --publish --wffcРесурсы PVC могут быть экспортированы только в том случае, если они не смонтированы какими-либо подами в данный момент.
Пример: создание ресурса DataExport для PVC с именем data в неймспейсе project с TTL 5 минут:
d8 data export create -n project my-export pvc/data --ttl 5mДля просмотра деталей созданного ресурса DataExport:
d8 k -n project get de my-exportДля скачивания файлов из экспортированных томов:
d8 data export download -n <namespace> <тип ресурса (pvc/vs/dataexport)>/<имя ресурса>/<путь к файлу> -o <имя файла> --publish=trueПример:
d8 data export download -n project dataexport/my-export -o test_file.txt --publish=trueАльтернативные способы создания и использования DataExport и DataImport без утилиты d8
Помимо утилиты d8, ресурсы DataExport и DataImport можно создавать напрямую через YAML-манифест. В примере ниже для удобства используются переменные окружения, замените их значения на нужные:
export NAMESPACE="d8-storage-foundation"
export DATA_EXPORT_RESOURCE_NAME="example-dataexport"
export TARGET_TYPE="PersistentVolumeClaim"
export TARGET_NAME="fs-pvc-data-exporter-fs-0"d8 k apply -f -<<EOF
apiVersion: storage-foundation.deckhouse.io/v1alpha1
kind: DataExport
metadata:
name: ${DATA_EXPORT_RESOURCE_NAME}
namespace: ${NAMESPACE}
spec:
ttl: 10h
targetRef:
kind: ${TARGET_TYPE}
name: ${TARGET_NAME}
EOFПосле создания ресурса извлеките CA-сертификат:
d8 k -n $NAMESPACE get dataexport $DATA_EXPORT_RESOURCE_NAME -o jsonpath='{.status.ca}' | base64 -d > ca.pemПроверьте сертификат:
openssl x509 -in ca.pem -noout -text | headПример вывода:
Issuer: CN = data-exporter-CA
Signature Algorithm: ecdsa-with-SHA256
Извлеките URL из ресурса DataExport и проверьте экспорт:
export POD_URL=$(d8 k -n $NAMESPACE get dataexport $DATA_EXPORT_RESOURCE_NAME -o jsonpath='{.status.url}')
echo "POD_URL: $POD_URL"После создания ресурса DataExport и извлечения необходимых данных подключиться к экспортеру можно одним из следующих способов.
1. Аутентификация с помощью сертификата и ключа из локального kubeconfig
Этот способ использует существующие учётные данные из локального файла kubeconfig:
cat ~/.kube/config | grep "client-certificate-data" | awk '{print $2}' | base64 -d > client.crt
cat ~/.kube/config | grep "client-key-data" | awk '{print $2}' | base64 -d > client.keyПроверьте содержимое целевого PVC:
curl -v --cacert ca.pem ${POD_URL}api/v1/files/ --key client.key --cert client.crt2. Аутентификация с помощью токена и ролей
Этот способ предполагает создание отдельного ServiceAccount с нужными правами доступа:
d8 k -n $NAMESPACE create serviceaccount data-exporter-testСоздайте токен:
export TOKEN=$(d8 k -n $NAMESPACE create token data-exporter-test --duration=24h)
echo $TOKENЗапросы к эндпоинтам данных запущенного экспорта или импорта — /api/v1/files… и /api/v1/block…, как по короткому пути, так и по полному — авторизуются через SubjectAccessReview на глагол create для субресурса download в API-группе storage-foundation.deckhouse.io: dataexports/download — при чтении данных из тома, dataimports/download — при записи данных в том. Обратите внимание: импорт авторизуется тем же субресурсом download, а не upload — субресурса upload не существует, поэтому роль, выдающая права на него, не даёт доступа вовсе.
Выдайте права сервис-аккаунту одним из двух способов ниже.
Привязка встроенной роли (предпочтительный способ)
В составе модуля поставляется ClusterRole d8:use:capability:module:storage-foundation:transfer_volume_data. Она выдаёт create на оба субресурса download, а также позволяет создавать, читать и удалять сами ресурсы DataExport и DataImport — это то, что нужно клиенту, чтобы выполнить перенос самостоятельно. Роль не сложена ни в один из общекластерных уровней доступа, поэтому вместе с таким уровнем права переноса не приходят.
При этом роль входит в набор, который собирает ClusterRole d8:use:capability:module:state-snapshotter:backup_agent — одна роль, выдаваемая внешней системе резервного копирования вместо нескольких отдельных. Этот агрегат тоже выдаётся только явной привязкой, поэтому сами по себе права никому не достаются; но действуют они в каждом namespace, где привязана либо эта роль, либо агрегат. Учитывайте это при аудите того, кто может вычитать том из кластера или записать в него данные: привязки, у которых в roleRef стоит эта роль, — ещё не полный ответ, привязки агрегата дают то же самое. В кластере без модуля, в котором определён агрегат, агрегата не существует, и значимы только привязки этой роли.
Привяжите роль к сервис-аккаунту:
d8 k create -f - <<EOF
kind: RoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
name: data-exporter-test-transfer-binding
namespace: ${NAMESPACE}
subjects:
- kind: ServiceAccount
name: data-exporter-test
namespace: ${NAMESPACE}
roleRef:
kind: ClusterRole
name: d8:use:capability:module:storage-foundation:transfer_volume_data
apiGroup: rbac.authorization.k8s.io
EOFСоздание роли вручную
Если встроенная роль разрешает больше, чем вы хотите выдать, создайте свою ClusterRole только с нужными субресурсами. Роль ниже разрешает скачивание экспортированных данных и ничего больше; добавьте dataimports/download в resources, чтобы разрешить ещё и загрузку данных в том:
d8 k create -f - <<EOF
kind: ClusterRole
apiVersion: rbac.authorization.k8s.io/v1
metadata:
name: data-exporter-test-role
rules:
- apiGroups: ["storage-foundation.deckhouse.io"]
resources: ["dataexports/download"]
verbs: ["create"]
EOFТакая роль покрывает только HTTP-запросы: сами ресурсы DataExport и DataImport должен создать тот, у кого есть отдельные права на них.
Создайте RoleBinding:
d8 k create -f - <<EOF
kind: RoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
name: data-exporter-test-role-binding
namespace: ${NAMESPACE}
subjects:
- kind: ServiceAccount
name: data-exporter-test
namespace: ${NAMESPACE}
roleRef:
kind: ClusterRole
name: data-exporter-test-role
apiGroup: rbac.authorization.k8s.io
EOFПроверка доступа
Какую бы из двух ролей вы ни привязали, проверьте содержимое целевого PVC:
curl -H "Authorization: Bearer $TOKEN" \
-v --cacert ca.pem ${POD_URL}api/v1/files/HTTP API: экспорт данных
Все эндпоинты доступны по короткому пути /api/v1/files|block и по полному пути /{namespace}/{kindShort}/{name}/api/v1/files|block.
Обозначения:
- kindShort — короткое имя экспортируемого ресурса (persistentVolumeClaim => pvc);
- name — имя экспортируемого ресурса.
Файловый режим (экспорт)
- Получить файл
GET /api/v1/files/{path} HTTP/1.1
Query (опционально, для дополнительных атрибутов):
attribute=stat — вернуть статические атрибуты (uid, gid, права, modtime)
attribute=hash.md5 — вернуть MD5 для обычных файлов- Просмотр содержимого директории
GET /api/v1/files/{path/} HTTP/1.1 # путь к директории должен заканчиваться на '/'- Метаданные без тела
HEAD /api/v1/files/{path|path/} HTTP/1.1Основные ошибки файлового режима:
400 Bad Request — несоответствие типа (файл/директория), запрещённые ссылки и т. п.
404 Not Found — путь не найден
405 Method Not Allowed — методы, отличные от GET/HEAD
500 Internal Server Error — внутренняя ошибкаБлочный режим (экспорт)
- Скачать блочное устройство (raw-образ)
GET /api/v1/block HTTP/1.1- Метаданные без тела
HEAD /api/v1/block HTTP/1.1HTTP API: импорт данных
Все эндпоинты доступны по короткому пути /api/v1/files|block и по полному пути /{namespace}/{kindShort}/{name}/api/v1/files|block.
Файловый режим (импорт)
- Загрузить файл
PUT /api/v1/files/{path} HTTP/1.1
Content-Length: <bytes> — обязательный заголовок
X-Attribute-Permissions: 0775 — обязательный заголовок (восьмеричное 0000–0777)
X-Attribute-Uid: <uid> — обязательный заголовок
X-Attribute-Gid: <gid> — обязательный заголовок
X-Offset: 0 — смещение от начала файла (неотрицательное целое)
X-Content-Length: 10 — обязательный заголовок, ожидаемый полный размер файла
X-Attribute-ModTime: метка времени RFC3339- Запрос текущего прогресса загрузки
HEAD /api/v1/files/{path} HTTP/1.1
Content-Length: 0- Завершение импорта (сигнал на остановку файлового сервера)
POST /api/v1/finished HTTP/1.1
Content-Length: 0Блочный режим (импорт)
- Загрузить raw-образ блочного устройства
PUT /api/v1/block HTTP/1.1
Content-Length: <bytes> — обязательный заголовок
X-Attribute-Permissions: 0775 — обязательный заголовок (восьмеричное 0000–0777)
X-Attribute-Uid: <uid> — обязательный заголовок
X-Attribute-Gid: <gid> — обязательный заголовок
X-Offset: 0 — опционально; смещение от начала устройства
X-Content-Length: 10 — опционально; ожидаемый полный размер- Запрос текущего прогресса загрузки
HEAD /api/v1/block HTTP/1.1
Content-Length: 0- Завершение импорта (сигнал на остановку файлового сервера)
POST /api/v1/finished HTTP/1.1
Content-Length: 0Важные замечания по работе с API
При работе с экспортированными данными через HTTP API учитывайте следующее:
- Скачивание файлов: файлы скачиваются обычными GET-запросами с указанием пути к файлу в URL, например
GET /api/v1/files/largeimage.iso. Путь к файлу не должен заканчиваться на/. Этот способ поддерживается стандартными инструментами (браузеры, curl и т. д.). Возобновление скачивания поддерживается, сжатие — нет. - Просмотр директорий: доступ к директории выполняется аналогичным GET-запросом, где путь к директории должен заканчиваться на
/, напримерGET /api/v1/files/для корня илиGET /api/v1/files/directory/для поддиректории. - Список файлов: при обращении к директории в теле ответа возвращается JSON со списком файлов (имя, тип и размер). Размеры файлов не кэшируются и пересчитываются при каждом запросе.
Модуль storage-volume-data-manager можно держать включённым одновременно с storage-foundation. Оба модуля продолжают работать: каждый обслуживает свои собственные ресурсы DataExport и DataImport независимо — своя API-группа, своё пространство имён и свои финализаторы на задействованных томах. При этом один и тот же живой том (PersistentVolumeClaim или VirtualDisk) следует экспортировать только через один из двух модулей. Причин тому две, и они независимы:
- Публичные адреса совпадают. Оба модуля собирают
status.publicURLна одном и том же хосте и из одного и того же пути/<namespace>/<вид-таргета>/<имя-таргета>, поэтому экспорт одного и того же тома получает один и тот же публичный адрес независимо от того, какой из двух модулей его создал. Если экспорт удалить в одном модуле и затем создать для того же тома в другом, адрес переиспользуется, и клиент, запомнивший адрес, не сможет определить, чей экспорт ему отвечает. Всегда берите адрес из поляstatus.publicURLтого ресурса, который создали сами. - Экспорт живого тома забирает его
PersistentVolume. Том на время экспорта перепривязывается к экспортеру, поэтому один и тот же том нельзя экспортировать через оба модуля одновременно: экспорт, начатый вторым, до публикации не доходит вовсе и завершается конфликтом заPersistentVolume. Прежде чем экспортировать тот же том через другой модуль, удалите первый экспорт или дождитесь истечения его срока.