Стадия жизненного цикла модуля: General Availability
У модуля есть требования для установки
Как экспортировать и скачать том без утилиты d8?
Ресурсы DataExport можно создать напрямую через YAML-манифест и обращаться к экспортеру по HTTP. В приведённом ниже примере используются переменные окружения для упрощения настройки; замените их значения на необходимые:
export NAMESPACE="d8-storage-volume-data-manager"
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.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 и извлечения необходимых данных вы можете подключиться к экспортеру одним из следующих способов:
Аутентификация с использованием сертификата и ключа из локального kubeconfig
Этот метод использует существующие учётные данные из вашего локального kubeconfig-файла. Извлеките ключи из конфигурации, выполнив следующие команды:
cat ~/.kube/config | grep "client-certificate-data" | awk '{print $2}' | base64 -d > client.crtcat ~/.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.crtПример вывода:
..
..
<
* TLSv1.2 (IN), TLS header, Supplemental data (23):
{"apiVersion": "v1", "items": [{"name":"4.txt","size":12,"modTime":"2025-04-01 08:02:00.228156524 +0000 UTC","type":"file"}
,{"name":"hello","size":5,"modTime":"2025-03-03 10:53:06.895434814 +0000 UTC","type":"file"}
,{"name":"7.txt","size":12,"modTime":"2025-04-01 08:02:00.228156524 +0000 UTC","type":"file"}
,{"name":"lost+found","modTime":"2025-03-03 10:29:31 +0000 UTC","type":"dir"}
,{"name":"8.txt","size":12,"modTime":"2025-04-01 08:02:00.228156524 +0000 UTC","type":"file"}
,{"name":"10.txt","size":13,"modTime":"2025-04-01 08:02:00.228156524 +0000 UTC","type":"file"}
,{"name":"9.txt","size":12,"modTime":"2025-04-01 08:02:00.228156524 +0000 UTC","type":"file"}
,{"name":"3.txt","size":12,"modTime":"2025-04-01 08:02:00.228156524 +0000 UTC","type":"file"}
,{"name":"2.txt","size":12,"modTime":"2025-04-01 08:02:00.228156524 +0000 UTC","type":"file"}
,{"name":"1.txt","size":12,"modTime":"2025-04-01 08:02:00.228156524 +0000 UTC","type":"file"}
,{"name":"6.txt","size":12,"modTime":"2025-04-01 08:02:00.228156524 +0000 UTC","type":"file"}
,{"name":"5.txt","size":12,"modTime":"2025-04-01 08:02:00.228156524 +0000 UTC","type":"file"}
]}Аутентификация с использованием токена и ролей
Этот метод предполагает создание отдельного ServiceAccount с соответствующими правами доступа. Создайте ServiceAccount, используя команду:
d8 k -n $NAMESPACE create serviceaccount data-exporter-testСоздайте ClusterRole, выполнив следующую команду:
d8 k create -f - <<EOF
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: data-exporter-test-role
rules:
- apiGroups: ["storage.deckhouse.io"]
resources: ["dataexports/download"]
verbs: ["create"]
EOFСоздайте токен, запустив команды:
export TOKEN=$(d8 k -n $NAMESPACE create token data-exporter-test --duration=24h)
echo $TOKENСоздайте RoleBinding, применив манифест:
d8 k create -f - <<EOF
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
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/Пример вывода:
..
..
<
* TLSv1.2 (IN), TLS header, Supplemental data (23):
{"apiVersion": "v1", "items": [{"name":"4.txt","size":12,"modTime":"2025-04-01 08:02:00.228156524 +0000 UTC","type":"file"}
,{"name":"hello","size":5,"modTime":"2025-03-03 10:53:06.895434814 +0000 UTC","type":"file"}
,{"name":"7.txt","size":12,"modTime":"2025-04-01 08:02:00.228156524 +0000 UTC","type":"file"}
,{"name":"lost+found","modTime":"2025-03-03 10:29:31 +0000 UTC","type":"dir"}
,{"name":"8.txt","size":12,"modTime":"2025-04-01 08:02:00.228156524 +0000 UTC","type":"file"}
,{"name":"10.txt","size":13,"modTime":"2025-04-01 08:02:00.228156524 +0000 UTC","type":"file"}
,{"name":"9.txt","size":12,"modTime":"2025-04-01 08:02:00.228156524 +0000 UTC","type":"file"}
,{"name":"3.txt","size":12,"modTime":"2025-04-01 08:02:00.228156524 +0000 UTC","type":"file"}
,{"name":"2.txt","size":12,"modTime":"2025-04-01 08:02:00.228156524 +0000 UTC","type":"file"}
,{"name":"1.txt","size":12,"modTime":"2025-04-01 08:02:00.228156524 +0000 UTC","type":"file"}
,{"name":"6.txt","size":12,"modTime":"2025-04-01 08:02:00.228156524 +0000 UTC","type":"file"}
,{"name":"5.txt","size":12,"modTime":"2025-04-01 08:02:00.228156524 +0000 UTC","type":"file"}
]}Как импортировать и загрузить том без утилиты d8?
Создайте ресурс DataImport с шаблоном PVC через манифест, извлеките URL доступа и CA-сертификат из status ресурса, затем загрузите данные через HTTP API импорта.
Пример: создание ресурса DataImport:
d8 k apply -f -<<EOF
apiVersion: storage.deckhouse.io/v1alpha1
kind: DataImport
metadata:
name: example-dataimport
namespace: d8-storage-volume-data-manager
spec:
ttl: 10h
publish: true
targetRef:
kind: PersistentVolumeClaim
pvcTemplate:
metadata:
name: imported-data
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 1Gi
storageClassName: local-path
volumeMode: Filesystem
EOFИзвлеките URL и CA-сертификат:
export POD_URL=$(d8 k -n d8-storage-volume-data-manager get dataimport example-dataimport -o jsonpath='{.status.url}')
d8 k -n d8-storage-volume-data-manager get dataimport example-dataimport -o jsonpath='{.status.ca}' | base64 -d > ca.pemЗагрузите файл с помощью curl (обязательные заголовки см. в справочнике HTTP API ниже):
curl --cacert ca.pem \
--cert client.crt --key client.key \
-X PUT "${POD_URL}api/v1/files/myfile" \
-H "Content-Length: $(stat -c%s ./local-file)" \
-H "X-Content-Length: $(stat -c%s ./local-file)" \
-H "X-Attribute-Permissions: 0644" \
-H "X-Attribute-Uid: 0" \
-H "X-Attribute-Gid: 0" \
-H "X-Offset: 0" \
--data-binary @./local-fileСигнал о завершении импорта:
curl --cacert ca.pem \
--cert client.crt --key client.key \
-X POST "${POD_URL}api/v1/finished" \
-H "Content-Length: 0"Справочник HTTP API
Все конечные точки доступны как по короткому пути /api/v1/files|block, так и по полному /{namespace}/{kindShort}/{name}/api/v1/files|block.
Обозначения:
- kindShort — короткое наименование экспортируемого ресурса (persistentVolumeClaim => pvc)
- name — имя экспортируемого ресурса
Экспорт: режим Filesystem
- Получение файла
GET /api/v1/files/{path} HTTP/1.1
Query (опционально, для дополнительных атрибутов):
- attribute=stat — вернуть статические атрибуты (uid, gid, permissions, modtime)
- attribute=hash.md5 — вернуть MD5 для обычных файловОтвет (файл):
HTTP/1.1 200 OK
Content-Type: application/octet-stream
Content-Length: <size>
Content-Disposition: attachment; filename=<basename>
X-Attribute-Uid: <uid>
X-Attribute-Gid: <gid>
X-Attribute-Permissions: <perm>
X-Attribute-Modtime: <RFC3339>
X-Attribute-Hash-Md5: <md5> # если запрошено attribute=hash.md5
X-Type: file
<file content>Ответ (символическая ссылка):
HTTP/1.1 200 OK
Content-Type: application/x-symlink
Content-Length: 0 - постоянное значение для символической ссылки
X-Attribute-Uid: <uid>
X-Attribute-Gid: <gid>
X-Attribute-Permissions: <perm>
X-Attribute-Modtime: <RFC3339>
X-Link-Target: /dir/file
X-Type: link- Листинг директории
GET /api/v1/files/{path/} HTTP/1.1 # путь к директории должен заканчиваться '/'
Query (опционально): attribute=stat | attribute=hash.md5Ответ:
HTTP/1.1 200 OK
Content-Type: application/json
Transfer-Encoding: chunked
X-Attribute-Uid: <uid>
X-Attribute-Gid: <gid>
X-Attribute-Permissions: <perm>
X-Attribute-Modtime: <RFC3339>
X-Type: dir
{"apiVersion":"v1","items":[{...}]}- Получение метаданных без тела
HEAD /api/v1/files/{path|path/} HTTP/1.1Ответ (файл):
HTTP/1.1 200 OK
Content-Type: application/octet-stream
Content-Length: <size>
Content-Disposition: attachment; filename=<basename>
X-Attribute-Uid: <uid>
X-Attribute-Gid: <gid>
X-Attribute-Permissions: <perm>
X-Attribute-Modtime: <RFC3339>
X-Type: file
X-Attribute-Hash-Md5: <md5> # если запрошено attribute=hash.md5Ответ (директория):
HTTP/1.1 200 OK
Content-Type: application/json
Transfer-Encoding: chunked
X-Attribute-Uid: <uid>
X-Attribute-Gid: <gid>
X-Attribute-Permissions: <perm>
X-Attribute-Modtime: <RFC3339>
X-Type: dirОтвет (символическая ссылка):
HTTP/1.1 200 OK
Content-Type: application/x-symlink
Content-Length: 0 - постоянное значение для символической ссылки
X-Attribute-Uid: <uid>
X-Attribute-Gid: <gid>
X-Attribute-Permissions: <perm>
X-Attribute-Modtime: <RFC3339>
X-Link-Target: /dir/file
X-Type: linkОшибки (Filesystem):
400 Bad Request — несоответствие типа (файл/директория), запрещённые ссылки и т.д.
404 Not Found — путь не найден
405 Method Not Allowed — методы кроме GET/HEAD
500 Internal Server Error — внутренняя ошибкаЭкспорт: режим Block
- Получение данных блочного устройства (raw-образ)
GET /api/v1/block HTTP/1.1Ответ:
HTTP/1.1 200 OK
Content-Type: application/octet-stream
Content-Length: <device_size>
Content-Disposition: attachment; filename=data.img
<raw block content>- Запрос метаданных без тела
HEAD /api/v1/block HTTP/1.1Ответ:
HTTP/1.1 200 OK
Content-Type: application/octet-stream
Content-Length: <device_size>
Content-Disposition: attachment; filename=data.imgИмпорт: режим Filesystem
- Загрузка файла
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 (2024-12-27T10:16:53.4537715+03:00)Ответ при частичной загрузке:
HTTP/1.1 204 No Content
X-Next-Offset: 10 — текущее смещение (целое неотрицательное число)Ответ при завершении загрузки:
HTTP/1.1 201 Created- Запрос текущего прогресса загрузки
HEAD /api/v1/files/{path} HTTP/1.1
Content-Length: 0HTTP/1.1 200 OK
X-Next-Offset: 10 — текущее смещение (целое неотрицательное число)- Завершение импорта (сигнал на остановку файлового сервера)
POST /api/v1/finished HTTP/1.1
Content-Length: 0HTTP/1.1 200 OKИмпорт: режим Block
- Загрузка 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 — опционально; ожидаемый итоговый размер (целое неотрицательное)Ответ при частичной загрузке:
HTTP/1.1 204 No Content
X-Next-Offset: 10 — текущее смещение (целое неотрицательное число)Ответ при завершении загрузки:
HTTP/1.1 201 Created
X-Next-Offset: 10 — итоговое смещение (совпадает с X-Content-Length)- Запрос текущего прогресса загрузки
HEAD /api/v1/block HTTP/1.1
Content-Length: 0HTTP/1.1 200 OK
X-Next-Offset - 10 — текущее смещение (целое неотрицательное число)
X-Device-Size - 1024 — текущий размер устройства в байтах- Завершение импорта (сигнал на остановку файлового сервера)
POST /api/v1/finished HTTP/1.1
Content-Length: 0HTTP/1.1 200 OKОшибки (Filesystem)
400 Bad Request — некорректные/отсутствующие обязательные заголовки, неверные значения X-Offset / X-Content-Length / X-Attribute-ModTime
405 Method Not Allowed — метод не поддерживается (кроме PUT/HEAD/POST)
403 Forbidden — нет прав записи в файловую систему
404 Not Found — HEAD для несуществующего файла
409 Conflict — параллельная запись; смещение не совпадает с ожидаемым; файл уже загружен целиком
500 Internal Server Error — внутренние ошибки доступа к ФС, установка атрибутов и прочееОшибки (Block)
400 Bad Request — некорректный путь; неверные заголовки
405 Method Not Allowed — метод не поддерживается (кроме PUT/HEAD/POST)
403 Forbidden — недостаточно прав на запись в блочное устройство
409 Conflict — параллельная запись; смещение не совпадает с текущим
416 Requested Range Not Satisfiable — смещение меньше 0 или больше размера устройства; Content-Length превышает оставшийся размер
422 Unprocessable Entity — записанный объём превысил объявленный X-Content-Length
500 Internal Server Error — внутренние ошибки сервераВажные примечания по работе с API
При работе с экспортированными данными через HTTP API учитывайте следующие особенности:
- Скачивание файлов — файлы скачиваются с помощью стандартных GET-запросов, содержащих путь к файлу в URL:
GET /api/v1/files/largeimage.iso,GET /api/v1/files/directory/largeimage.iso. Путь к файлу не должен заканчиваться символом/. Такой метод скачивания поддерживается стандартными средствами (браузеры, curl и т.д.). Поддерживается докачка файлов, но сжатие не поддерживается. - Просмотр директорий — обращение к директории осуществляется аналогичным GET-запросом, при этом путь к директории должен заканчиваться символом
/:GET /api/v1/files/— путь к корню,GET /api/v1/files/directory/— путь к директории. - Листинг файлов — при обращении к директории возвращается JSON-строка, содержащая список файлов с информацией о имени, типе и размере. Размеры файлов не кэшируются и вычисляются заново при каждом запросе директории.