В Kubernetes для работы с постоянным хранилищем данных используются Persistent Volume (PV) и Persistent Volume Claim (PVC). Для узлов кластера, где есть дополнительные диски, можно создавать локальные тома, которые будут использоваться как PV.
В NetAngels к узлам кластера Kubernetes можно подключать дополнительные локальные диски, которые использовать для хранения данных приложений. Разберемя как использовать блочные устройства на узлах в качетсве Persistent Volumes.
Перед началом работы вам понадобится:
Кластер Kubernetes в NetAngels с двумя узлами в группе
Подключенная к узлам группа дополнительных дисков минимального размера
Установленная утилита kubectl
Сначала создадим StorageClass для локальных томов:
kubectl apply -f - <<EOF
kind: StorageClass
apiVersion: storage.k8s.io/v1
metadata:
name: local-storage
annotations:
storageclass.kubernetes.io/is-default-class: "true"
provisioner: kubernetes.io/no-provisioner
volumeBindingMode: WaitForFirstConsumer
EOF
Проверим созданный StorageClass:
kubectl get storageclass local-storage
NAME PROVISIONER RECLAIMPOLICY VOLUMEBINDINGMODE ALLOWVOLUMEEXPANSION AGE
local-storage (default) kubernetes.io/no-provisioner Delete WaitForFirstConsumer false 57s
Создадим PV, используя диск /dev/vdb на узле worker-1:
kubectl apply -f - <<EOF
apiVersion: v1
kind: PersistentVolume
metadata:
name: local-pv-worker-1
spec:
volumeMode: Filesystem
capacity:
storage: 2Gi
accessModes:
- ReadWriteOnce
persistentVolumeReclaimPolicy: Delete
storageClassName: local-storage
local:
path: /dev/vdb
nodeAffinity:
required:
nodeSelectorTerms:
- matchExpressions:
- key: kubernetes.io/hostname
operator: In
values:
- worker-1
EOF
Проверим созданный PV:
kubectl get pv local-pv-worker-1
NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS VOLUMEATTRIBUTESCLASS REASON AGE
local-pv-worker-1 2Gi RWO Delete Available local-storage <unset> 11s
Создадим PVC, который будет связан с созданным PV:
kubectl apply -f - <<EOF
kind: PersistentVolumeClaim
apiVersion: v1
metadata:
name: nginx-pvc
spec:
accessModes:
- ReadWriteOnce
volumeMode: Filesystem
resources:
requests:
storage: 2Gi
EOF
Проверим созданный PVC:
kubectl get pvc nginx-pvc
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS VOLUMEATTRIBUTESCLASS AGE
nginx-pvc Pending local-storage <unset> 12s
Создадим Pod, который будет использовать PVC для монтирования тома:
kubectl apply -f - <<EOF
---
apiVersion: v1
kind: Pod
metadata:
name: nginx-local
spec:
containers:
- name: nginx-local
image: library/nginx:1.28-alpine
ports:
- containerPort: 80
volumeMounts:
- mountPath: "/var/www/html"
name: data
volumes:
- name: data
persistentVolumeClaim:
claimName: nginx-pvc
EOF
Проверим состояние PV
kubectl get pv local-pv-worker-1
NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS VOLUMEATTRIBUTESCLASS REASON AGE
local-pv-worker-1 2Gi RWO Delete Bound default/nginx-pvc local-storage <unset> 3m52s
Проверим состояние PVC
kubectl get pvc nginx-pvc
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS VOLUMEATTRIBUTESCLASS AGE
nginx-pvc Bound local-pv-worker-1 2Gi RWO local-storage <unset> 2m56s
Проверим запущенный Pod:
kubectl get pod nginx-local
NAME READY STATUS RESTARTS AGE
nginx-local 1/1 Running 0 43s
Проверим смонтированный каталог
kubectl exec -it pods/nginx-local -- df -h /var/www/html
kubectl exec -it pods/nginx-local -- grep /var/www/html /proc/mounts
Filesystem Size Used Available Use% Mounted on
/dev/vdb 19.5G 24.0K 19.5G 0% /var/www/html
/dev/vdb /var/www/html ext4 rw,relatime 0 0
Блочное устойство было успешно подключено к поду. Как видно из вывода, фактический размер блочного устройства значительно выше, чем было запрошено в PVC. Это объясняется тем, что к узлу кластера подключен дополнительный диск размером 20ГБ, а к поду он, при таком способе подключения, подключается целиком
Для автоматизации создания PV на основе локальных дисков используется Local Static Provisioner. Этот поставщик позволяет автоматически создавать PV на основе указанных шаблонов устройств.
Создадим файл values-block.yaml с конфигурацией для провизионера:
# Mount the host's `/dev/` by default so that block device symlinks can be
# resolved by the containers
mountDevVolume: false
# Configuration for classes of static volumes.
classes:
- name: local-storage-vdb # Defines name of storage classes.
# Path on the host where local volumes of this storage class are mounted
# under.
hostDir: /dev
# File name pattern to discover. By default, discover all file names.
namePattern: "vdb"
# The volume mode of created PersistentVolume object. Default to Filesystem
# if not specified.
volumeMode: Filesystem
# Access mode of the volume. default to ReadWriteOnce if not specified.
accessMode: ReadWriteOnce
# Filesystem type to mount.
# It applies only when the source path is a block device,
# and desire volume mode is Filesystem.
# Must be a filesystem type supported by the host operating system.
fsType: ext4
# Restrict topology of provisioned volumes to specific labels
allowedTopologies:
- matchLabelExpressions:
- key: kubernetes.io/hostname
values:
- worker-2
blockCleanerCommand:
- "/scripts/shred.sh"
- "2"
# Uncomment to create storage class object with default configuration.
storageClass: true
# Uncomment to create storage class object and configure it.
storageClass:
name: local-storage-vdb
reclaimPolicy: Delete # Available reclaim policies: Delete/Retain, defaults: Delete.
isDefaultClass: true # set as default class
# If you are using cluster autoscaler to scale the workload using volume provisioned by this storage class,
# set the provisioner of the storage class to another value other than the default.
# Ref: https://github.com/kubernetes-sigs/sig-storage-local-static-provisioner/issues/288
provisioner: kubernetes.io/no-provisioner
# Обнаружение устройств только на узле с именем worker-2
affinity:
nodeAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
nodeSelectorTerms:
- matchExpressions:
- key: kubernetes.io/hostname
operator: In
values:
- worker-2
Сгенерируем манифест для установки провизионера с помощью Helm:
helm template sig-storage-local-static-provisioner/local-static-provisioner --version 2.8.0 --namespace sig-storage -f values-block.yaml > lsp-block.yaml
Применим полученный манифест:
kubectl create ns sig-storage
kubectl apply -f lsp-block.yaml
namespace/sig-storage created
serviceaccount/release-name-local-static-provisioner created
configmap/release-name-local-static-provisioner-config created
storageclass.storage.k8s.io/local-storage-vdb created
clusterrole.rbac.authorization.k8s.io/release-name-local-static-provisioner-node-clusterrole created
clusterrolebinding.rbac.authorization.k8s.io/release-name-local-static-provisioner-node-binding created
daemonset.apps/release-name-local-static-provisioner created
После установки провизионера он автоматически отследит наличие /dev/vdb на узлах и создаст соответствующие PV. Проверим статус:
kubectl get pv
NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS VOLUMEATTRIBUTESCLASS REASON AGE
local-pv-dd2e6dbf 20Gi RWO Delete Available local-storage-vdb <unset> 71s
Этот подход позволяет автоматически создавать PV при появлении нужных устройств, обеспечивая гибкость управления локальным хранилищем в Kubernetes кластере. В остальном работа с PVC происходит аналогично ручному выделению PV
kubectl apply -f - <<EOF
kind: PersistentVolumeClaim
apiVersion: v1
metadata:
name: nginx-pvc-vdb
spec:
accessModes:
- ReadWriteOnce
volumeMode: Filesystem
resources:
requests:
storage: 2Gi
storageClassName: local-storage-vdb
---
apiVersion: v1
kind: Pod
metadata:
name: nginx-local-vdb
spec:
containers:
- name: nginx-local
image: library/nginx:1.28-alpine
ports:
- containerPort: 80
volumeMounts:
- mountPath: "/var/www/html"
name: data
volumes:
- name: data
persistentVolumeClaim:
claimName: nginx-pvc-vdb
EOF
Проверим созданные ресурсы
kubectl get pv/local-pv-dd2e6dbf pvc/nginx-pvc-vdb pod/nginx-local-vdb
NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS VOLUMEATTRIBUTESCLASS REASON AGE
persistentvolume/local-pv-dd2e6dbf 20Gi RWO Delete Bound default/nginx-pvc-vdb local-storage-vdb <unset> 7m32s
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS VOLUMEATTRIBUTESCLASS AGE
persistentvolumeclaim/nginx-pvc-vdb Bound local-pv-dd2e6dbf 20Gi RWO local-storage-vdb <unset> 39s
NAME READY STATUS RESTARTS AGE
pod/nginx-local-vdb 1/1 Running 0 49s
kubectl delete pod nginx-local
kubectl delete pvc nginx-pvc
kubectl delete pv local-pv-worker-1
kubectl delete pod/nginx-local-vdb
kubectl delete pvc/nginx-pvc
Проверим состояние PV
kubectl describe pv local-pv-dd2e6dbf
Name: local-pv-dd2e6dbf
Labels: <none>
Annotations: pv.kubernetes.io/bound-by-controller: yes
pv.kubernetes.io/provisioned-by: local-volume-provisioner-worker-2-f5468500-3f7b-41aa-8127-d39d6f6122c1
Finalizers: [kubernetes.io/pv-protection]
StorageClass: local-storage-vdb
Status: Released
Claim: default/nginx-pvc-vdb
Reclaim Policy: Delete
Access Modes: RWO
VolumeMode: Filesystem
Capacity: 20Gi
Node Affinity:
Required Terms:
Term 0: kubernetes.io/hostname in [worker-2]
Message:
Source:
Type: LocalVolume (a persistent volume backed by local storage on a node)
Path: /dev/vdb
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal VolumeDelete 90s local-volume-provisioner-worker-2-f5468500-3f7b-41aa-8127-d39d6f6122c1 Starting cleanup of Block PV "local-pv-dd2e6dbf", this may take a while
Вывод говорит о том, что для PV запустилась процедура очистки. Ранее, в файле values.yaml опцией blockCleanerCommand был задан способ очиски PV перед выдачей другим подам. Сейчас она выполняется на узле worker-2, а после завершения PV станет доступен для повторного использования.
Отметим, что при ручном создании PV, без использования local-volume-provisioner данные PV остаются на диске и будут доступны в следующем поде, к которому будет подключен диск.