Piraeus Datastore — это open-source решение для управления распределённым хранилищем в Kubernetes, построенное на базе LINSTOR и DRBD. Оно предоставляет CSI-интерфейс и поддерживает динамическое создание томов, репликацию, снапшоты и резервное копирование в облачные хранилища, включая S3.
Для начала работы вам потребуется созданный кластер по крайней мере с одним узлом и подключенным к нему дополнительным диском. В данном примере будет рассмотрен кластер с тремя узлами ищущими метку csi-type=piraeus.
Если вы не указали метки при создании группы узлов, можете добавить их внучную:
kubectl label nodes worker-1 worker-2 worker-3 csi-type=piraeus
kubectl apply --server-side -f "https://github.com/piraeusdatastore/piraeus-operator/releases/latest/download/manifest.yaml"
Проверим статус подов:
kubectl get pods -n piraeus-datastore
NAME READY STATUS RESTARTS AGE
piraeus-operator-controller-manager-7d786569f-v9rlp 1/1 Running 0 2m41s
piraeus-operator-gencert-86b9d78f75-spl57 1/1 Running 0 2m41s
LINSTOR будет использовать этот секрет при создании зашифрованных томов, а так же для сохранения реквизитов доступа для сохранения резервных копий
ВАЖНО: замените значение
MASTER_PASSPHRASEна надёжную фразу!
kubectl apply -f - <<EOF
---
apiVersion: v1
kind: Secret
metadata:
name: linstor-passphrase
namespace: piraeus-datastore
stringData:
MASTER_PASSPHRASE: B4dd_Ex4Mple_Pa$$PHr4ze
EOF
LinstorClusterkubectl apply -f - <<EOF
apiVersion: piraeus.io/v1
kind: LinstorCluster
metadata:
name: linstorcluster
spec:
linstorPassphraseSecret: linstor-passphrase
nodeAffinity:
nodeSelectorTerms:
- matchExpressions:
- key: csi-type
operator: In
values:
- piraeus
highAvailabilityController:
enabled: false
nfsServer:
enabled: false
csiNode:
enabled: true
podTemplate:
spec:
containers:
- name: linstor-csi
volumeMounts:
- mountPath: /var/lib/k0s/kubelet
mountPropagation: Bidirectional
name: publish-dir
- args:
- --v=5
- --csi-address=/csi/csi.sock
- --kubelet-registration-path=/var/lib/k0s/kubelet/plugins/linstor.csi.linbit.com/csi.sock
- --health-port=9809
name: csi-node-driver-registrar
volumes:
- hostPath:
path: /var/lib/k0s/kubelet
name: publish-dir
- hostPath:
path: /var/lib/k0s/kubelet/plugins_registry
name: registration-dir
- hostPath:
path: /var/lib/k0s/kubelet/plugins/linstor.csi.linbit.com
name: plugin-dir
EOF
LinstorSatelliteConfiguration — использование /dev/vdbkubectl apply -f - <<'EOF'
apiVersion: piraeus.io/v1
kind: LinstorSatelliteConfiguration
metadata:
name: storage-satellites
spec:
podTemplate:
spec:
# Отключаем автоматическую загрузку DRBD-модуля (если он уже есть)
initContainers:
- name: drbd-module-loader
$patch: delete
nodeAffinity:
nodeSelectorTerms:
- matchExpressions:
- key: csi-type
operator: In
values:
- piraeus
storagePools:
- name: vg1
lvmThinPool:
volumeGroup: vg1
source:
hostDevices:
- /dev/vdb
EOF
О типах хранилищ в LINSTOR
LINSTOR поддерживает следующие типы storage pools (определяются вLinstorSatelliteConfiguration):
Тип пула Описание Особенности lvmPoolКлассический LVM Volume Group (thick) — По умолчанию использует storage pool nameкак имя VG
— Можно переопределить черезvolumeGroup: <vg-name>
— Выделение места — full/denselvmThinPoolLVM Thin Pool (thin LVM) — По умолчанию: VG = linstor_<pool-name>, LV =<pool-name>
— Можно переопределитьvolumeGroup,thinPool
— Экономия места, поддержка снапшотов и быстрое создание томовfilePoolФайловое хранилище (каждому тому — файл в FS) — По умолчанию: /var/lib/linstor-pools/<pool-name>
— Подходит для отладки и лёгких нагрузокfileThinPoolТонкое (sparse) файловое хранилище — Поведение как filePool, но файлы создаются sparse (разрежённые)
— Требует поддержки sparse файлов (XFS, ext4 с опциейsparse)zfsPoolZFS ZPool (thick zVol) — По умолчанию использует pool nameкак имя zpool
— Можно переопределить черезzPool: <zpool-name>zfsThinPoolZFS ZPool с sparse zVol — Поведение как zfsPool, но тома создаются sparse (reservation=0)
— Подходит для экономии места на ZFS
Дождемся заверщения запуска подов в неймспейсе piraeus-datastore и проверим статус саттелитов:
kubectl -n piraeus-datastore exec -it deployments/linstor-controller -- linstor storage-pool list
[ ... таблица без emoji ... ]
На каждом из узлов на дополнительных дисках был инициализирован LVM. Приступим к выделению PV
StorageClasskubectl apply -f - <<EOF
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: piraeus-storage-lvm
annotations:
storageclass.kubernetes.io/is-default-class: "true"
provisioner: linstor.csi.linbit.com
allowVolumeExpansion: true
volumeBindingMode: WaitForFirstConsumer
parameters:
linstor.csi.linbit.com/storagePool: "vg1"
linstor.csi.linbit.com/layerList: "storage"
EOF
Проверим:
kubectl get storageclass
NAME PROVISIONER RECLAIMPOLICY VOLUMEBINDINGMODE ALLOWVOLUMEEXPANSION AGE
piraeus-storage-lvm (default) linstor.csi.linbit.com Delete WaitForFirstConsumer true 10s
PVCkubectl apply -f - <<EOF
apiVersion: v1
kind: Namespace
metadata:
name: nginx-piraeus
---
kind: PersistentVolumeClaim
apiVersion: v1
metadata:
namespace: nginx-piraeus
name: nginx-piraeus
spec:
storageClassName: piraeus-storage-lvm
accessModes:
- ReadWriteOnce
volumeMode: Filesystem
resources:
requests:
storage: 2Gi
EOF
Pod и сервисаkubectl apply -f - <<EOF
apiVersion: v1
kind: Pod
metadata:
namespace: nginx-piraeus
name: nginx-piraeus
spec:
containers:
- name: nginx
image: library/nginx:1.28-alpine
ports:
- containerPort: 80
volumeMounts:
- mountPath: "/var/www/html"
name: data
volumes:
- name: data
persistentVolumeClaim:
claimName: nginx-piraeus
---
apiVersion: v1
kind: Service
metadata:
name: nginx-piraeus
namespace: nginx-piraeus
spec:
selector:
app.kubernetes.io/name: nginx-piraeus
ports:
- port: 80
targetPort: 80
type: LoadBalancer
EOF
Проверим состояние PVC:
kubectl get pvc -n nginx-piraeus
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS VOLUMEATTRIBUTESCLASS AGE
nginx-piraeus Bound pvc-deaf559a-6e96-4a3f-801f-bc56eb165c66 2Gi RWO piraeus-storage-lvm <unset> 2m18s
Проверим PV:
kubectl get pv
NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS VOLUMEATTRIBUTESCLASS REASON AGE
pvc-deaf559a-6e96-4a3f-801f-bc56eb165c66 2Gi RWO Delete Bound nginx-piraeus/nginx-piraeus piraeus-storage-lvm <unset> 80s
Уточним размещение Persistent Volume:
kubectl describe pvc -n nginx-piraeus nginx-piraeus | grep volume.kubernetes.io/selected-node:
volume.kubernetes.io/selected-node: worker-3
Проверим состояние LV на узле, где был размещен Persistent Volume:
kubectl -n piraeus-datastore exec pods/linstor-satellite.worker-3-bdhng -- lvs
LV VG Attr LSize Pool Origin Data% Meta% Move Log Cpy%Sync Convert
pvc-deaf559a-6e96-4a3f-801f-bc56eb165c66_00000 vg1 Vwi-aotz-- 2.00g vg1 4.76
vg1 vg1 twi-aotz-- 9.97g 0.96 10.90
Проверим Pod и Service:
kubectl get -n nginx-piraeus pods,svc
NAME READY STATUS RESTARTS AGE
pod/nginx-piraeus 1/1 Running 0 92s
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
service/nginx-piraeus LoadBalancer 10.96.28.209 172.16.100.251 80:30539/TCP 92s
Проверим монтирование и файловую систему внутри контейнера:
kubectl -n nginx-piraeus exec -it pods/nginx-piraeus -- df -h /var/www/html
Filesystem Size Used Available Use% Mounted on
/dev/mapper/vg1-pvc--deaf559a--6e96--4a3f--801f--bc56eb165c66_00000
1.9G 24.0K 1.8G 0% /var/www/html
Проверим тип файловой системы и параметры монтирования:
kubectl -n nginx-piraeus exec -it pods/nginx-piraeus -- grep www /proc/mounts
/dev/mapper/vg1-pvc--deaf559a--6e96--4a3f--801f--bc56eb165c66_00000 /var/www/html ext4 rw,relatime,stripe=16 0 0
Как видим, Persistent Volume был успешно создан и подключен к созданному поду
Важное примечание: до создания
VolumeSnapshotClassнеобходимо установить CRD и snapshot-контроллер, который обеспечивает поддержку снапшотов в Kubernetes:
kubectl apply -k https://github.com/kubernetes-csi/external-snapshotter/client/config/crd
kubectl apply -k https://github.com/kubernetes-csi/external-snapshotter/deploy/kubernetes/snapshot-controller
Проверим, что контроллер запущен:
kubectl get pods -n kube-system | grep snapshot-controller
snapshot-controller-65b49c865c-vbr8l 1/1 Running 0 1m24s
snapshot-controller-65b49c865c-z5g77 1/1 Running 0 1m24s
kubectl apply -f - <<EOF
apiVersion: snapshot.storage.k8s.io/v1
kind: VolumeSnapshotClass
metadata:
name: piraeus-snapshots
namespace: piraeus-datastore
driver: linstor.csi.linbit.com
deletionPolicy: Delete
EOF
kubectl apply -f - <<EOF
apiVersion: snapshot.storage.k8s.io/v1
kind: VolumeSnapshot
metadata:
name: nginx-piraeus
namespace: nginx-piraeus
spec:
volumeSnapshotClassName: piraeus-snapshots
source:
persistentVolumeClaimName: nginx-piraeus
EOF
Проверим:
kubectl get volumesnapshot -n nginx-piraeus
NAME READYTOUSE SOURCEPVC SOURCESNAPSHOTCONTENT RESTORESIZE SNAPSHOTCLASS SNAPSHOTCONTENT CREATIONTIME AGE
nginx-piraeus true nginx-piraeus 2Gi piraeus-snapshots snapcontent-efe15b67-cbd1-4fef-ac5a-c39a215db2e9 5s 7s
kubectl -n piraeus-datastore exec pods/linstor-satellite.worker-3-bdhng -- lvs
LV VG Attr LSize Pool Origin Data% Meta% Move Log Cpy%Sync Convert
pvc-deaf559a-6e96-4a3f-801f-bc56eb165c66_00000 vg1 Vwi-aotz-- 2.00g vg1 4.76
pvc-deaf559a-6e96-4a3f-801f-bc56eb165c66_00000_snapshot-efe15b67-cbd1-4fef-ac5a-c39a215db2e9 vg1 Vri-a-tz-k 2.00g vg1 pvc-deaf559a-6e96-4a3f-801f-bc56eb165c66_00000 4.76
vg1 vg1 twi-aotz-- 9.97g 0.96 10.97
kubectl apply -f - <<EOF
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: nginx-piraeus-v2
namespace: nginx-piraeus
spec:
accessModes:
- ReadWriteOnce
volumeMode: Filesystem
resources:
requests:
storage: 2Gi
dataSource:
apiGroup: snapshot.storage.k8s.io
kind: VolumeSnapshot
name: nginx-piraeus
EOF
kubectl delete pod nginx-piraeus -n nginx-piraeus
kubectl apply -f - <<EOF
apiVersion: v1
kind: Pod
metadata:
namespace: nginx-piraeus
name: nginx-piraeus
spec:
containers:
- name: nginx
image: library/nginx:1.28-alpine
ports:
- containerPort: 80
volumeMounts:
- mountPath: "/var/www/html"
name: data
volumes:
- name: data
persistentVolumeClaim:
claimName: nginx-piraeus-v2
EOF
Проверим блочные устройства на worker-узле:
kubectl -n piraeus-datastore exec pods/linstor-satellite.worker-3-bdhng -- lvs
LV VG Attr LSize Pool Origin Data% Meta% Move Log Cpy%Sync Convert
pvc-25263fd1-ed50-4da7-bf8a-8685c5cb4e52_00000 vg1 Vwi-aotz-k 2.00g vg1 pvc-deaf559a-6e96-4a3f-801f-bc56eb165c66_00000_snapshot-efe15b67-cbd1-4fef-ac5a-c39a215db2e9 4.76
pvc-deaf559a-6e96-4a3f-801f-bc56eb165c66_00000 vg1 Vwi-a-tz-- 2.00g vg1 4.76
pvc-deaf559a-6e96-4a3f-801f-bc56eb165c66_00000_snapshot-efe15b67-cbd1-4fef-ac5a-c39a215db2e9 vg1 Vri-a-tz-k 2.00g vg1 pvc-deaf559a-6e96-4a3f-801f-bc56eb165c66_00000 4.76
vg1 vg1 twi-aotz-- 9.97g 0.96 11.07
И снова проверим уструктуру Logical Volume
kubectl delete volumesnapshot -n nginx-piraeus nginx-piraeus
kubectl -n piraeus-datastore exec pods/linstor-satellite.worker-3-bdhng -- lvs
volumesnapshot.snapshot.storage.k8s.io "nginx-piraeus" deleted
LV VG Attr LSize Pool Origin Data% Meta% Move Log Cpy%Sync Convert
pvc-25263fd1-ed50-4da7-bf8a-8685c5cb4e52_00000 vg1 Vwi-aotz-k 2.00g vg1 4.76
pvc-deaf559a-6e96-4a3f-801f-bc56eb165c66_00000 vg1 Vwi-a-tz-- 2.00g vg1 4.76
vg1 vg1 twi-aotz-- 9.97g 0.96 11.00
Создадим Secret с учетными данными и VolumeSnapshotClass для S3:
kubectl apply -f - <<EOF
---
apiVersion: v1
kind: Secret
metadata:
name: linstor-csi-s3-access
namespace: piraeus-datastore
immutable: true
type: linstor.csi.linbit.com/s3-credentials.v1
stringData:
access-key: YOUR_AWS_ACCESS_KEY_ID
secret-key: YOUR_AWS_SECRET_ACCESS_KEY
---
apiVersion: snapshot.storage.k8s.io/v1
kind: VolumeSnapshotClass
metadata:
name: piraeus-snapshots-s3
driver: linstor.csi.linbit.com
deletionPolicy: Delete
parameters:
snap.linstor.csi.linbit.com/type: S3
snap.linstor.csi.linbit.com/remote-name: example
snap.linstor.csi.linbit.com/delete-local: "true"
snap.linstor.csi.linbit.com/allow-incremental: "false"
snap.linstor.csi.linbit.com/s3-bucket: piraeus-backups
snap.linstor.csi.linbit.com/s3-endpoint: s3.example.com
snap.linstor.csi.linbit.com/s3-signing-region: ru-1
snap.linstor.csi.linbit.com/s3-use-path-style: "true"
csi.storage.k8s.io/snapshotter-secret-name: linstor-csi-s3-access
csi.storage.k8s.io/snapshotter-secret-namespace: piraeus-datastore
EOF
Замените имя бакета, точку подключения, и учетные данные на свои.
kubectl apply -f - <<EOF
apiVersion: snapshot.storage.k8s.io/v1
kind: VolumeSnapshot
metadata:
name: nginx-piraeus-s3
namespace: nginx-piraeus
spec:
volumeSnapshotClassName: piraeus-snapshots-s3
source:
persistentVolumeClaimName: nginx-piraeus
EOF
Проверим статус снапшота — readyToUse: true означает успешную загрузку в S3:
kubectl get volumesnapshot -n nginx-piraeus nginx-piraeus-s3
NAME READYTOUSE SOURCEPVC SOURCESNAPSHOTCONTENT RESTORESIZE SNAPSHOTCLASS SNAPSHOTCONTENT CREATIONTIME AGE
nginx-piraeus true nginx-piraeus 2Gi piraeus-snapshots-s3 snapcontent-c7aa2b19-8331-47d1-a549-4c615d60bb46 3m10s 3m10s
Используем тот же VolumeSnapshot, что и выше:
kubectl apply -f - <<EOF
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: nginx-piraeus-from-s3
namespace: nginx-piraeus
spec:
accessModes:
- ReadWriteOnce
volumeMode: Filesystem
resources:
requests:
storage: 2Gi
dataSource:
apiGroup: snapshot.storage.k8s.io
kind: VolumeSnapshot
name: nginx-piraeus-s3
EOF
Важно: если в VolumeStorageClass параметр
delete-localустановлен в значениеtrue, то локальный снапшот удаляется после загрузки данных в S3 — он будет восстановлен из облака при создании PVC.
Piraeus Datastore — это зрелое и гибкое решение для распределённого и локального хранения в Kubernetes:
Простая установка через манифест и Helm-чарты
Гибкая настройка саттелитов под любые конфигурации дисков
Динамическое создание PV/PVC через CSI
Локальные и облачные снапшоты с поддержкой загрузки в S3
Благодаря S3-интеграции вы можете не только быстро восстанавливать тома из снапшотов, но и обеспечивать длительное хранение и трансферные точки восстановления в облаке. ```