Небольшое how-to по базовой настройке Cloud Native Distributed Block Storage for Kubernetes.
ВАЖНО: установить open-iscsi
и NFS
можно и через DaemonSet. Однако это не самый оптимальный способ, так как отладка будет сложнее — не только на уровне ОС, но и дополнительно через DaemonSet.
Longhorn требует в зависимостях NFS
и open-iscsi
, которые выполняют разные задачи.
Когда нужен:
Когда нужен:
longhorn-nfs-provisioner
), который позволяет использовать Longhorn-тома как NFS-ресурсы.Таким образом, в большинстве случаев требуются оба компонента.
Чтобы убедится, что ваши воркер ноды корректно настроены воспользуемся https://longhorn.io/docs/1.8.0/deploy/install/#using-the-longhorn-command-line-tool
Создайте конфиг с подключениями к кубу admin.conf, после тестовой проверки можно будет удалить
curl -sSfL -o longhornctl https://github.com/longhorn/cli/releases/download/v1.8.0/longhornctl-linux-amd64 chmod +x longhornctl export KUBECONFIG=/etc/kubernetes/admin.conf ./longhornctl check preflight
Либо используя скрипт
sudo apt -y install jq curl -sSfL https://raw.githubusercontent.com/longhorn/longhorn/v1.8.0/scripts/environment_check.sh | bash
Состоит из трёх мастеров и четырёх рабочих нод.
[k8s_master] k8s-01.beta-82.win k8s-02.beta-82.win k8s-03.beta-82.win [k8s_worker] w-01.beta-82.win w-02.beta-82.win w-03.beta-82.win w-04.beta-82.win
Для большей надёжности можно использовать replicaCount=3
, если нужно защититься от второго сбоя.
replicaCount
.
Для демонстрации faulted при отказе 2 из 4 нод я буду использовать replicaCount=3
. Однако вы должны выбирать его самостоятельно, в зависимости от ваших задач.
В примере ниже будем устанавливать самую актуальную на данный момент версию 1.8.0. Подробнее об установке можно почитать здесь: Официальная документация Longhorn
Также может быть полезен файл longhorn.yml
: Longhorn YAML
Приступим
helm repo add longhorn https://charts.longhorn.io helm repo update helm install longhorn longhorn/longhorn \ --namespace longhorn-system --create-namespace --version 1.8.0 \ --set defaultSettings.replicaCount=3 \ --set defaultSettings.storageOverProvisioningPercentage=100 \ --set defaultSettings.storageMinimalAvailablePercentage=15 \ --set defaultSettings.nodeDownPodDeletionPolicy="delete-both-statefulset-and-deployment-pod" \ --set defaultSettings.replicaAutoBalance="best-effort" \ --set defaultSettings.dataLocality="best-effort" \ --set defaultSettings.rebalanceDisksWhenLowSpace=true
Описание параметров
Параметр | Значение | Описание |
---|---|---|
defaultSettings.replicaCount | 3 | Количество реплик каждого тома. Чем больше, тем выше отказоустойчивость, но выше нагрузка на диск. При 3 том остаётся доступным при отказе 1 ноды. |
defaultSettings.storageOverProvisioningPercentage | 100 | Указывает, на сколько процентов Longhorn может «переподписывать» (overprovision) дисковое пространство. Значение 100 означает, что выделять можно в 2 раза больше, чем фактический объём диска. |
defaultSettings.storageMinimalAvailablePercentage | 15 | Минимальный порог свободного дискового пространства перед тем, как Longhorn перестанет создавать новые реплики. 15% означает, что если остаётся менее 15% места, новые реплики не создаются. |
defaultSettings.nodeDownPodDeletionPolicy | «delete-both-statefulset-and-deployment-pod» | Определяет, что делать с подами, привязанными к сбойным нодам. Данное значение удаляет поды как из StatefulSet , так и из Deployment , если нода ушла в NotReady . |
defaultSettings.replicaAutoBalance | «best-effort» | Автоматический баланс реплик между нодами. best-effort означает, что Longhorn старается равномерно распределять реплики при изменениях в кластере. |
defaultSettings.dataLocality | «best-effort» | Определяет, будет ли Longhorn пытаться хранить реплику тома на той же ноде, где работает под. best-effort создаёт локальную копию, если это возможно, но не требует её наличия. |
defaultSettings.rebalanceDisksWhenLowSpace | true | Включает автоматическое перемещение реплик с дисков, на которых заканчивается место, на менее загруженные диски или ноды. |
Проверяем наши поды, как видим они поднялись
$ k get pods NAME READY STATUS RESTARTS AGE csi-attacher-79866cdcf8-grgs9 1/1 Running 0 2m7s csi-attacher-79866cdcf8-jvg8z 1/1 Running 0 2m7s csi-attacher-79866cdcf8-qknmr 1/1 Running 0 2m7s csi-provisioner-664cb5bdd5-dxpnt 1/1 Running 0 2m7s csi-provisioner-664cb5bdd5-qc85t 1/1 Running 0 2m7s csi-provisioner-664cb5bdd5-r2vfj 1/1 Running 0 2m7s csi-resizer-64f6fb4459-98qtn 1/1 Running 0 2m7s csi-resizer-64f6fb4459-l9v27 1/1 Running 0 2m7s csi-resizer-64f6fb4459-tvqr6 1/1 Running 0 2m7s csi-snapshotter-7b7db78f9-6gpj4 1/1 Running 0 2m7s csi-snapshotter-7b7db78f9-7k52m 1/1 Running 0 2m7s csi-snapshotter-7b7db78f9-jnc4k 1/1 Running 0 2m7s engine-image-ei-c2d50bcc-5jcgl 1/1 Running 0 3m engine-image-ei-c2d50bcc-8fb7v 1/1 Running 0 3m engine-image-ei-c2d50bcc-gmvhc 1/1 Running 0 3m engine-image-ei-c2d50bcc-jnhd4 1/1 Running 0 3m instance-manager-79fbdba3783ea7f6d1ea35496adae27f 1/1 Running 0 2m25s instance-manager-93ee330ab4dd110c09b6c0dba65a6184 1/1 Running 0 2m19s instance-manager-b565b7b70c365483ceeab00de81eb424 1/1 Running 0 2m29s instance-manager-d5b24c31c174dd52fdabdad19038bbff 1/1 Running 0 2m19s longhorn-csi-plugin-6995m 3/3 Running 0 2m7s longhorn-csi-plugin-9fhk5 3/3 Running 0 2m7s longhorn-csi-plugin-sb42r 3/3 Running 0 2m7s longhorn-csi-plugin-zxcpm 3/3 Running 0 2m7s longhorn-driver-deployer-7fc7ffcb7f-vbft2 1/1 Running 2 (2m57s ago) 3m24s longhorn-manager-gfr55 2/2 Running 0 3m24s longhorn-manager-kswnm 2/2 Running 0 3m24s longhorn-manager-rsnw7 2/2 Running 0 3m24s longhorn-manager-wvjvk 2/2 Running 0 3m24s longhorn-ui-5fcc4bcfc7-4wl6s 1/1 Running 0 3m24s longhorn-ui-5fcc4bcfc7-lmrg9 1/1 Running 0 3m24s
Для удобства может потребоваться доступ к Longhorn UI. Получить его можно с помощью следующей команды:
$ kubectl port-forward svc/longhorn-frontend -n longhorn-system 8080:80
после чего в браузере станет доступно http://127.0.0.1:8080/#/dashboard
Сейчас я запущу небольшое тестовое приложение, на котором протестируем:
Я запустил небольшой манифест, в котором сначала создаётся PersistentVolumeClaim (longhorn-pvc
) для StorageClass
— longhorn
, после чего развернул Deployment с подом, который использует наш PVC
--- apiVersion: v1 kind: PersistentVolumeClaim metadata: name: longhorn-pvc spec: accessModes: - ReadWriteOnce resources: requests: storage: 1Gi storageClassName: longhorn --- apiVersion: apps/v1 kind: Deployment metadata: name: go-app spec: replicas: 1 selector: matchLabels: app: go-app-label template: metadata: labels: app: go-app-label spec: containers: - image: denisitpro/go-example:latest name: go-app env: - name: AI_NAME value: "Ivan" ports: - containerPort: 8082 readinessProbe: failureThreshold: 5 httpGet: path: / port: 8082 periodSeconds: 10 successThreshold: 2 timeoutSeconds: 3 livenessProbe: failureThreshold: 3 httpGet: path: / port: 8082 periodSeconds: 10 successThreshold: 1 timeoutSeconds: 3 initialDelaySeconds: 10 resources: requests: cpu: 100m memory: 64Mi limits: cpu: 300m memory: 512Mi volumeMounts: - mountPath: /opt/example name: data-volume volumes: - name: data-volume persistentVolumeClaim: claimName: "longhorn-pvc"
Далее проверим текущее состояние Pod, PVC и PV в кластере.
$ k get pods NAME READY STATUS RESTARTS AGE go-app-fd8f8d4c-4s4wg 1/1 Running 0 59s $ k get pvc NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS VOLUMEATTRIBUTESCLASS AGE longhorn-pvc Bound pvc-0b7a49a6-352c-4c0a-84af-e2b014dd75c5 1Gi RWO longhorn <unset> 62s $ k get pv NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS VOLUMEATTRIBUTESCLASS REASON AGE pvc-0b7a49a6-352c-4c0a-84af-e2b014dd75c5 1Gi RWO Delete Bound go/longhorn-pvc longhorn <unset> 58s
Имеет смысл также проверить, что наш StorageClass
стал дефолтным.
$ k get sc NAME PROVISIONER RECLAIMPOLICY VOLUMEBINDINGMODE ALLOWVOLUMEEXPANSION AGE longhorn (default) driver.longhorn.io Delete Immediate true 4m31s longhorn-static driver.longhorn.io Delete Immediate true 4m29s
Убеждаемся, что Pod действительно запущен на w-01
.
$k get pv $kubectl -n longhorn-system get volumes pvc-0b7a49a6-352c-4c0a-84af-e2b014dd75c5 -o jsonpath='{.status.ownerID}' ### output w-01
Сначала создадим небольшой тестовый файл, прежде чем отключать рабочие ноды кластера.
$ kubectl exec -it go-app-fd8f8d4c-4s4wg -- bash $ echo "42"> /opt/example/42.txt $ cat /opt/example/42.txt 42
Убеждаемся, что Pod действительно запущен на w-01
.
$ k get pods -o wide NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES go-app-fd8f8d4c-4s4wg 1/1 Running 0 9m10s 10.48.6.85 w-01 <none> <none>
Отключаем рабочую ноду и проверяем, что кластер зафиксировал её как нерабочую.
Важно:
Под не завершится сразу — процесс займёт примерно 10 минут, это время можно сократить правильными настройками, но для данного теста это не имеет значения
$ k get nodes NAME STATUS ROLES AGE VERSION k8s-01 Ready control-plane 43m v1.31.1 k8s-02 Ready control-plane 42m v1.31.1 k8s-03 Ready control-plane 42m v1.31.1 w-01 NotReady <none> 40m v1.31.1 w-02 Ready <none> 40m v1.31.1 w-03 Ready <none> 40m v1.31.1 w-04 Ready <none> 40m v1.31.1 $k get pods -o wide NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES go-app-fd8f8d4c-4s4wg 1/1 Terminating 0 16m 10.48.6.85 w-01 <none> <none> go-app-fd8f8d4c-c4trm 0/1 ContainerCreating 0 26s <none> w-03 <none> <none>
Теперь проверим, что происходит с данными внутри пода.
$ k get pod NAME READY STATUS RESTARTS AGE go-app-fd8f8d4c-5xq2n 1/1 Running 0 4m39s $ kubectl exec -it go-app-fd8f8d4c-5xq2n -- bash $root@go-app-fd8f8d4c-5xq2n:/opt/app# cat /opt/example/42.txt 42
Продолжим эксперимент — отключим вторую рабочую ноду, предварительно добавив ещё одну строку в файл.
$ kubectl exec -it go-app-fd8f8d4c-5xq2n -- bash $ echo "43">> /opt/example/42.txt $ cat /opt/example/42.txt 42 43
Отключаем w-02
, так как под сейчас находится на этой ноде.
$ k get pods -o wide NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES go-app-fd8f8d4c-5xq2n 1/1 Running 0 8m18s 10.48.4.213 w-02 <none> <none> $ kubectl -n longhorn-system get volumes pvc-0b7a49a6-352c-4c0a-84af-e2b014dd75c5 -o jsonpath='{.status.ownerID}' w-02%
Теперь проверяем текущее состояние кластера и на какой ноде запущен под.
$ k get nodes NAME STATUS ROLES AGE VERSION k8s-01 Ready control-plane 61m v1.31.1 k8s-02 Ready control-plane 61m v1.31.1 k8s-03 Ready control-plane 61m v1.31.1 w-01 NotReady <none> 59m v1.31.1 w-02 NotReady <none> 59m v1.31.1 w-03 Ready <none> 59m v1.31.1 w-04 Ready <none> 59m v1.31.1 $ k get pods -o wide NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES go-app-fd8f8d4c-fcwc2 1/1 Running 0 4m39s 10.48.4.23 w-04 <none> <none>
Проверка состояния тома после миграции пода, как мы видим, под переместился на w-04
. Теперь проверим состояние volume.
k get volume -n longhorn-system NAME DATA ENGINE STATE ROBUSTNESS SCHEDULED SIZE NODE AGE pvc-0b7a49a6-352c-4c0a-84af-e2b014dd75c5 v1 attached degraded 1073741824 w-04 36m
Теперь проверим, сохранились ли данные после миграции пода.
$ k get pods -o wide NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES go-app-fd8f8d4c-fcwc2 1/1 Running 0 8m10s 10.48.4.23 w-04 <none> <none> $ kubectl exec -it go-app-fd8f8d4c-fcwc2 -- bash $ cat /opt/example/42.txt 42 43
Сейчас добавим данные, включим ранее выключенные ноды, затем отключим активную ноду и проверим состояние данных.
Это финальная проверка
$ kubectl exec -it go-app-fd8f8d4c-fcwc2 -- bash $ echo "44">> /opt/example/42.txt $ cat /opt/app# cat /opt/example/42.txt 42 43 44
Пришло время провести завершающую проверку
$ k get nodes NAME STATUS ROLES AGE VERSION k8s-01 Ready control-plane 79m v1.31.1 k8s-02 Ready control-plane 78m v1.31.1 k8s-03 Ready control-plane 78m v1.31.1 w-01 Ready <none> 76m v1.31.1 w-02 Ready <none> 76m v1.31.1 w-03 Ready <none> 76m v1.31.1 w-04 NotReady <none> 76m v1.31.1 $ k get pods -o wide NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES go-app-fd8f8d4c-fpfp4 1/1 Running 0 32s 10.48.6.94 w-01 <none> <none> $ kubectl exec -it go-app-fd8f8d4c-fpfp4 -- bash $ cat /opt/example/42.txt 42 43 44
ВАЖНО: при установке через Helm параметр auto-salvage
уже установлен в true
, но я решил оставить эту информацию для понимания логики работы.
В сценарии, когда у нас есть 4 рабочие ноды, а replicaCount=3
, при потере двух нод мы получаем не совсем то, что ожидали — том станет faulted. Печально.
replicaCount=3
.
Настроить Replica Auto Salvage: defaultSettings.autoSalvage=true
В целом, лучше придерживаться схемы: количество нод должно быть replicaCount + 2.
Проверка состояния PVC
kubectl get volumes.longhorn.io -n longhorn-system