Сливаем репозитарий с автоматическим provisioning:
git clone https://github.com/kubernetes-sigs/sig-storage-local-static-provisioner
Создаём storage class (будет называться fast-disks):
kubectl apply -f sig-storage-local-static-provisioner/deployment/kubernetes/example/default_example_storageclass.yaml
Создаём по helm template'у нужные нам файлы. Полный список опций, которые можно использовать в provisioner_values можно посмотреть в sig-storage-local-static-provisioner/helm/provisioner/values.yaml.
cat <<EOF >provisioner_values.yml
daemonset:
nodeLabels:
- kubernetes.io/hostname
EOF
helm template pv-provisioner --namespace kube-system --values provisioner_values.yml sig-storage-local-static-provisioner/helm/provisioner >provisioner_generated.yml
В helm/provisioner/values.yaml можно увидеть mount point для всех наших дисков.
Перед тем как применять сгенерированный yaml, надо создать mount point и создать сами диски. K8s не умеет забирать папки. Он умеет забирать только целые mount point'ы. Мы используем btrfs, поэтому мы можем просто создать пачку subvolumes и использовать их. Но надо их сделать отдельным mount point'ом каждый:
mkdir /mnt/pvs /mnt/fast-disks
VOLUME=UUID=$(grep -Po '(?<=^UUID=")[^"]*(?=.*subvol=root)' /etc/fstab)
mount -o noatime,nodiratime $VOLUME /mnt/pvs
btrfs subvolume create /mnt/pvs/k8s
umount /mnt/pvs
echo "$VOLUME /mnt/pvs btrfs noatime,nodiratime,space_cache=v2,discard=async,compress=zstd,subvol=k8s,rshared 0 0" >>/etc/fstab
mount /mnt/pvs
Запускаем наш чарт:
kubectl apply -f provisioner_generated.yml
В следующем разделе показано, как добавлять новые PV.
Создаем скрипт для создания нового PV
cat <<EOF >add-pv.sh
[ -z "\$1" ] && exit 1
mkdir /mnt/fast-disks/\$1
btrfs subvolume create /mnt/pvs/\$1
echo "UUID=$(grep -Po '(?<=^UUID=")[^"]*(?=.*subvol=root)' /etc/fstab) /mnt/fast-disks/\$1 btrfs noatime,nodiratime,space_cache=v2,discard=async,compress=zstd,subvol=k8s/\$1,rshared 0 0" >>/etc/fstab
mount /mnt/fast-disks/\$1
EOF
chmod +x add-pv.sh
И исполняем нужное количество раз
for i in $(seq 0 9); do ./add-pv.sh pv$i; done
Показать список активных PV
kubectl get pv -A
Список: путь к PV mount, имя PV, имя вершины
cat <<EOF >list-pv.sh
kubectl get pv -o jsonpath="{range .items[*]}[{.spec.nodeAffinity.required.nodeSelectorTerms[0].matchExpressions[0].values[0]}, {.spec.local.path}, {.metadata.name}]{'\n'}{end}" | sort
EOF
chmod +x list-pv.sh
Предполагается, удаление PV будет редкой операцией в нашем setup'е.
cat <<EOF >remove-pv.sh
# Removes given LocalStorage PersistentVolume from the current node
# \$1 - mount name, matches btrfs subvolume name. If name is abc then mount is /mnt/fast-disks/abc and btrfs subvolume is k8s/abc
# Script assumes that node name is equal to \$HOSTNAME
[ -z "\$1" ] && exit 1
PV=\$(kubectl get pv -l kubernetes.io/hostname=\$HOSTNAME -o jsonpath="{.items[?(.spec.local.path=='/mnt/fast-disks/\$1')].metadata.name}")
set -x
umount /mnt/fast-disks/\$1
kubectl delete pv "\$PV"
sed -i "/subvol=k8s\/\$1,/d" /etc/fstab
rmdir /mnt/fast-disks/\$1
btrfs subvolume delete /mnt/pvs/\$1
EOF
chmod +x remove-pv.sh
Note: Альтернативный способ нахождения PV на текущей вершине, не требующий метки равной имени вершины:
PV=$(kubectl get pv -o json |\
jq -r ".items|map(select(.spec.local.path==\"/mnt/fast-disks/$1\" and "\
".spec.nodeAffinity.required.nodeSelectorTerms[0].matchExpressions[0].values[0]==\"\$HOSTNAME\"))|map(.metadata.name)[0]")
Предположим надо удалить PV, соответствующий subvolume k8s-5
.
./remove-pv.sh k8s-5
Здесь есть некоторый шанс, что после umount кто-то успеет запросить PersistentVolumeClaim (PVC) на удаляемый PV. Чтобы этого не произошло можно останавливать static provisioner и позже вновь его запускать:
kubectl delete ds pv-provisioner
# Then delete necessary volumes and run again from ~/k8s
kubectl apply -f sig-storage-local-static-provisioner/deployment/kubernetes/provisioner_generated.yaml
Note: Если имя вершины не совпадает с $HOSTNAME
, то
здесь есть способ нахождения имени текущей вершины.
TODO: Найти более "красивый" способ удаления PV на конкретной машине без остановки provisioner на всех машинах.
- Хотелось не создавать отдельный volume для сервисов. Выбор пал на subvolume btrfs, который можно было маунтить как файловую систему.
- Для того, чтобы provisioner видел mount points, надо чтобы /mnt/fast-disks было частью файловой системы, замаунченной
с опцией rshared. То есть надо либо маунтить
/
как rshared, либо создавать отдельный subvolume и маунтить его в /mnt/fast-disks как rshared. - Subvolume'ы можно было создавать в корне btrfs либо поместить в отдельный subvolume (k8s). По итогу решили поместить в отдельный subvolume, чтобы все сабвольюмы в сабвольюме k8s были предназначены для одной и той же цели.