Работа с хранилищами в Kubernetes

d7840aac30e383af3259cca28ac947a5.png

Kubernetes пожалуй является самым распространенным средством оркестрации контейнеризированных приложений. С его помощью можно автоматизировать развертывание, масштабирование и координацию работы с контейнером в условиях кластера. Благодаря этим свойствам Kubernetes (который также называют k8s) существенно сокращает время сборки, поставки и масштабирования контейнерных приложений.

Одним из замечательных свойств контейнеров k8s является их неизменяемость, то есть, если мы закрываем контейнер, то вся информация, хранящаяся в нем теряется. Это очень удобно для всевозможных тестовых и учебных сред, решений типа «песочница» и аналогичных. Суть сводится к тому, что что бы мы не делали с контейнером, после его закрытия все будет уничтожено. Мы можем запустить подозрительный файл в контейнере не опасаясь за последствия (если конечно у нас правильно настроено монтирование) и через пару минут уничтожить этот контейнер, также без последствий.

Однако существует также еще масса приложений, таких как базы данных, для которых требуется сохранение результатов работы контейнера, проще говоря, требуется постоянное хранилище. В этой статье мы подробно рассмотрим работу с постоянными хранилищами в Kubernetes.

Что такое хранилище в K8s

Для работы с постоянными томами в Kubernetes имеются объекты API позволяющие на постоянной основе выделять  ресурсы хранения и управлять ими. Начнем с подсистемы Persistent Volumes (PV).

Данная подсистема позволяет создавать ресурс хранения, который не зависит от того, какой модуль использует его. Использование PV гарантирует, что хранилище останется постоянным. При этом PV могут быть динамически предоставлены с помощью классов хранилища (о них мы поговорим чуть дальше), а могут быть подготовлены администраторами вручную. Например, если у вас имеется несколько физических хранилищ, допустим быстрые SSD диски и медленные HDD. Тогда мы можем создать два PV и в зависимости от выполняемых приложениями задач выделять подам с соответствующими контейнерами место в этих томах. При этом стоит заметить, что Kubernetes умеет работать с множеством различных томов, такими как NFS, iSCSI, Fibre Channel и другими.

Еще одно понятие k8s, которое нам необходимо для работы с хранилищами это StorageClass (SC). С помощью SC можно описать классы хранения, которые предлагают хранилища. Как уже упоминалось, хранилища могут отличаться по скорости, по политикам бэкапа, либо какими-то еще произвольными политиками. Каждый StorageClass содержит поля provisioner, parameters и reclaimPolicy, которые используются, чтобы динамически создавать PersistentVolume.

В качестве примера создадим Local Persistent Volume — хранилище Kubernetes на локальных дисках. Для промышленной эксплуатации это, наверное, не самая лучшая идея, а вот для разработки в контейнерах и отладки вполне подойдет.

Сначала объявим StorageClass в файле sc.yaml:

kind: StorageClass

apiVersion: storage.k8s.io/v1

metadata:

  name: mystorage

provisioner: kubernetes.io/no-provisioner

volumeBindingMode: WaitForFirstConsumer

Обратите внимание на значение параметра provisioner — no provisioner. В k8s отсутствует встроенный провизионер для создания локальных хранилищ.

Применим созданный файл настроек.

# kubectl apply -f sc.yaml

d1fe050bbeac9db28921bf3f7f77a0cb.png

Далее нам необходимо создать Persistent Volume которое будет использовать ресурсы нашего локального хранилища. Для этого создадим файл pvlocal-1.yaml с описанием PV, который будет располагаться на сервере node-1 в /mnt/local-storage. Естественно, этот каталог должен быть создан на данном сервере.

apiVersion: v1

kind: PersistentVolume

metadata:

  name: node-1

spec:

  capacity:

    storage: 10Gi

  volumeMode: Filesystem

  accessModes:

  - ReadWriteOnce

  persistentVolumeReclaimPolicy: Retain

  storageClassName: local-storage

  local:

    path: /mnt/local-storage

  nodeAffinity:

    required:

      nodeSelectorTerms:

      - matchExpressions:

        - key: kubernetes.io/hostname

          operator: In

          values:

          - kub-node-1

Мы создали Persistent Volume в нашем Kubernetes, далее посмотрим, как можно использовать данное хранилище.

Также применим созданный файл.

# kubectl apply -f pvlocal.yaml

c7450f77ebb72ab0659564c8513746dd.png

PersistentVolumeClaim

Теперь, когда мы создали PV мы можем попробовать воспользоваться его ресурсами.  Пользователь может сам запросить у PV ресурсы для хранения данных. Такая подсистема называется PersistentVolumeClaim (PVC).  При этом важно понимать, что PVC может потреблять только тот ресурс PV, который соответствует требованиям к его размеру и режимам доступа. Если соответствующий PV доступен, он привязывается к PVC. В противном случае Kubernetes динамически подготавливает PV (если это технически возможно) или запрос завершается ошибкой. Обычно запрашивают следующие параметры: требуемый объем хранилища pvc и тип доступа.

Типы доступа у PVC могут быть следующие:

  • ReadWriteOnce — том может быть смонтирован на чтение и запись к одному поду.

  • ReadOnlyMany — том может быть смонтирован на много подов в режиме только чтения.

  • ReadWriteMany — том может быть смонтирован к множеству подов в режиме чтения и записи.

По поводу технических возможностей стоит отметить, что ограничения по типу доступа могут зависеть и от типа хранилища. Так, к примеру на iSCSI нельзя использовать ReadWriteMany.

Довольно теории, вернемся к нашему PV и рассмотрим пример запроса PVC c 2Гб и типом доступа ReadWriteOnce.

---

kind: PersistentVolumeClaim

apiVersion: v1

metadata:

  name: mypvc

spec:

  accessModes:

    - ReadWriteOnce

  volumeMode: Filesystem

  resources:

    requests:

      storage: 2Gi

Сохраняемся в файле pvc.yaml и применяем данный файл с помощью команды:

kubectl apply -f pvc.yaml

И посмотрим, результат:

# kubectl get pvc

e3e000f9c7d5b9678dc0b592ab56d251.png

Как видно, запрашиваемое PVC на 2 Гигабайта нам успешно выделили. Теперь необходимо проверить работу нашего хранилища, для этого мы создадим сначала один под использующий pvc, в нем создадим текстовый файл. Затем немного поправив описание создадим второй под, использующий этот же PVC и убедимся в доступности в нем созданного файла.

Наши поды будут запускать /bin/bash и делать довольно продолжительную паузу. Для этого сначала создаем файл pod-pvc.yaml следующего содержания:

kind: Pod

apiVersion: v1

metadata:

  name: pod-pvc

spec:

  containers:

    - name: app

      image: alpine

      volumeMounts:

      - name: mystorage

        mountPath: /mnt

      command: ["/bin/sh"]

      args: ["-c", "sleep 100000"]

  volumes:

  - name: mystorage

    persistentVolumeClaim:

      claimName: mypvc

Создадим наш под с помощью следующей команды:

# kubectl apply -f pod-pvc.yaml

Для проверки  работы нашего Local Persistent Volume мы создадим текстовый файл.

# kubectl exec -it pod-pvc sh

# echo "test file created" >> /mnt/local.txt

Теперь немного поправим наш файл для создания пода: поменяем значение name на pod-pvc2 и применим его с помощью kubectl.

kind: Pod

apiVersion: v1

metadata:

  name: pod-pvc2

spec:

  containers:

    - name: app

      image: alpine

      volumeMounts:

      - name: mystorage

        mountPath: /mnt

      command: ["/bin/sh"]

      args: ["-c", "sleep 100000"]

  volumes:

  - name: mystorage

    persistentVolumeClaim:

      claimName: mypvc

и

# kubectl apply -f pod-pvc.yaml

Посмотрим, как себя чувствуют наши поды:

# kubectl get pod

b794a0ce34869bcfefffa2a11efe5a43.png

Зайдем на pod-pvc2 и посмотрим содержимое /mnt/local.txt

# kubectl exec -it pod-pvc2 sh

354c556b308a6c53e45268e0a8ada763.png

Как видно файл и его содержимое на месте.

Заключение

В этой статье мы рассмотрели основные моменты, связанные с созданием постоянных хранилищ в Kubernetes. Теперь вы можете без проблем использовать Persistent Volumes для своих приложений в K8s.

Также хочу пригласить всех на бесплатный урок, в рамках которого изучим тонкости архитектурного устройства kubernetes, поговорим о концепциях data и control plane и подробнее остановимся на ключевом компоненте — etcd и алгоритме консенсуса raft.

© Habrahabr.ru