Стадия жизненного цикла модуля: General Availability
Альтернативные способы создания и использования ресурсов DataExport и DataImport без утилиты d8
Помимо использования утилиты d8, ресурсы DataExport и DataImport можно создать напрямую через YAML-манифест. В приведенном ниже примере используются переменные окружения для упрощения настройки, замените их значения на необходимые:
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"
kubectl 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-сертификат, выполнив следующую команду:
kubectl -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=$(kubectl -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.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"}
]}
2. Аутентификация с использованием токена и ролей
Этот метод предполагает создание отдельного ServiceAccount с соответствующими правами доступа. Создайте ServiceAccount, используя команду:
kubectl -n $NAMESPACE create serviceaccount data-exporter-test
Создайте ClusterRole, выполнив следующую команду:
kubectl create -f - <<EOF
kind: ClusterRole
apiVersion: rbac.authorization.k8s.io/v1
metadata:
name: data-exporter-test-role
rules:
- apiGroups: ["storage.deckhouse.io"]
resources: ["dataexports/download"]
verbs: ["create"]
EOF
Создайте токен, запустив команды:
export TOKEN=$(kubectl create token data-exporter-test --duration=24h)
echo $TOKEN
Создайте ClusterRoleBinding, применив манифест:
kubectl 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/
Пример вывода:
..
..
<
* 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"}
]}
API data export
Все конечные точки доступны как по короткому пути /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
Режим 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
API data import
Все конечные точки доступны как по короткому пути /api/v1/files|block, так и по полному /{namespace}/{kindShort}/{name}/api/v1/files|block
Обозначения:
- kindShort - короткое наименование испортируемого ресурса (persistentVolumeClaim => pvc)
- name - имя ресурса в который происходит импорт
Режим 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-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: 0
HTTP/1.1 200 OK
X-Next-Offset: 10 - текущее смещение. Целое неотрицательное число
- Завершение импорта данных (сигнал на остановку файлового сервера)
POST /api/v1/finished HTTP/1.1
Content-Length: 0
HTTP/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: 0
HTTP/1.1 200 OK
X-Next-Offset - 10 - текущее смещение. Целое неотрицательное число
X-Device-Size - 1024 - текущий размер устройства в байтах
- Завершение импорта данных (сигнал на остановку файлового сервера)
POST /api/v1/finished HTTP/1.1
Content-Length: 0
HTTP/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-строка, содержащая список файлов с информацией о имени, типе и размере. Размеры файлов не кэшируются и вычисляются заново при каждом запросе директории.