Хранилища Kubernetes для начинающего сисадмина - Академия Selectel

Хранилища Kubernetes для начинающего сисадмина

Екатерина Низовцева
Екатерина Низовцева Системный администратор
5 августа 2026

На простых примерах с реальной инфраструктурой изучим всю архитектуру выделения ресурсов. Развернем хранилище в тестовом кластере, смонтируем диск и заглянем внутрь файловой системы ноды.

Изображение записи

Управление данными в Kubernetes часто кажется сложным. Если грамотно не подключить внешнее хранилище, то при любом перезапуске пода все накопленные файлы бесследно исчезнут. Этот материал — практическое руководство для тех, кто только осваивает администрирование K8s и хочет разобраться в логике работы дисков без погружения в запутанную документацию.

Привет! Я Катя, младший системный администратор в Selectel. Надеюсь, моя работа поможет выстроить в голове четкую картину и сэкономит часы на дебаге, если нужный диск однажды откажется подключаться.

Введение

Хранилища предназначены для долговременного хранения данных. На локальных компьютерах эту задачу выполняют физические диски. В зависимости от операционной системы, это могут быть логические тома C:/, D:/ или устройства /dev/sda, /dev/sdb. На них и размещаются различные файлы и документы.

Аналогично и приложения, работающие в подах  кластера Kubernetes, требуют выделенного дискового пространства. Однако в отличие от домашнего ПК, поды эфемерны: они могут перезапускаться или удаляться. Если сохранять информацию только внутри контейнера, при его остановке данные будут потеряны. 

Именно поэтому в Kubernetes предусмотрены специальные механизмы, позволяющие сохранять данные независимо от жизненного цикла пода. Этот принцип схож с камерой хранения в отеле: вещи остаются в сохранности независимо от присутствия их владельца в номере.

Общая архитектура хранения

Приложение ничего не знает о конкретной реализации хранилища и его устройстве. Kubernetes также не управляет дисками напрямую. Чтобы максимально просто и прозрачно предоставить поду место для размещения данных, была придумана следующая схема взаимодействия.

1. Приложение в поде понимает, что ему нужно куда-то сохранять данные.

2. Что делает приложение? Оно создает заявку — Persistent Volume Claim (PVC). В ней честно пишет: «Требуется столько-то гигабайт, нужны права на чтение и запись, и хранилище нужно вот такого типа».

3. Дальше в игру вступает главный диспетчер — Kubernetes. Он изучает заявку и понимает: готового подходящего диска, или как он называется в Kubernetes, Persistent Volume (PV) — нет. Тогда он зовет переводчика — CSI-драйвер (Container Storage Interface). Заявка же (PVC) временно получает статус Pending — как заказ, который еще не подтвержден.

4. Сам Kubernetes — не волшебник. Он не знает, как общаться с каждым облачным провайдером или файловой системой в отдельности. Их же сотни! Поэтому у него есть специальный посредник — CSI-драйвер. Этот помощник знает, как разговаривать с конкретным хранилищем на его родном языке — прямо как синхронный переводчик на конференции.

5. CSI-драйвер берет заявку и создает новый диск прямо в облачном хранилище. Облако отвечает: «Диск готов!». Теперь Kubernetes создает для этого диска PV и наконец-то привязывает его к нашей заявке PVC. Статус Pending меняется на Bound. Все, сделка состоялась!

6. Финал: приложение получает доступ к своему личному, пусть и условному, диску. Теперь оно может размещать на нем базы данных, логи, пользовательские файлы и вообще что угодно. И главное — даже если под перезапустится или его удалят, данные никуда не исчезнут, а останутся лежать спокойно в хранилище в ожидании нового пода.

Подытожим. 

Когда описывается под и запрашивается для него диск, то создается ресурс PersistentVolumeClaim (дословно, «заявка на постоянное хранилище», далее PVC) — запрос, но еще не само хранилище. Позже Kubernetes выделит PersistentVolume (дословно, «постоянное хранилище», далее PV) — уже ссылающийся на диск  конкретный ресурс, которым и оперирует под. 

Чтобы диск был создан в инфраструктуре облачного провайдера, Kubernetes обращается к CSI-драйверу, который, в свою очередь, и делает всю работу, исходя из параметров в PVC.

Общая цепочка выглядит так: Под → PVC (PersistentVolumeClaim) → CSI (Container Storage Interface) → физический диск → PV (PersistentVolume)

Сам K8s никогда не управляет дисками напрямую — вместо этого он контролирует цепочку абстракций, которые и связывают под с реальным хранилищем. 

Теперь, уяснив принципы взаимодействия пода с диском, и начнем наше знакомство с работой хранилища в K8s.

Источником диска всегда выступает внешнее хранилище: ресурсы облачных провайдеров или мощные локальные серверы с решениями Ceph и NFS.

CSI и StorageClass— создание и управление дисками

CSI (Container Storage Interface) — это драйвер, который непосредственно работает с дисками. Именно ему Kubernetes делегирует задачи по созданию, удалению и подключению томов к узлам кластера.

Технически компоненты CSI представляют собой обычные поды. Проверить их наличие можно командой:


      kubectl -n kube-system get pods | grep csi

Обычно разворачивается два типа подов:

  • controller  — отвечает за создание тома (volume),
  • node — монтирует созданный том на конкретную ноду для использования подом.

В нашем тестовом кластере с одной worker-нодой мы видим два запущенных пода — controller и node-плагин:


      kube-system   csi-cinder-controllerplugin-78858d84f9-pdj2r          6/6     Running   0          59m


kube-system   csi-cinder-nodeplugin-nbvhk                           3/3     Running   0          59m

StorageClass — это профиль конфигурации, который указывает кластеру, какой тип накопителя нужно выделить и с помощью какого CSI-драйвера. Он работает как шаблон, где заранее зафиксированы технические параметры будущего диска, его производительность и правила удаления данных.

Пример манифеста StorageClass:


      apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: fast.ru-3b
provisioner: cinder.csi.openstack.org
reclaimPolicy: Delete
volumeBindingMode: Immediate
parameters:
  type: fast.ru-3b

В примере выше используется CSI‑драйвер Cinder от платформы Openstack. 

Посмотреть, какие есть в кластере классы хранилищ и их основные характеристики можно с помощью команды:


      kubectl get storageclass

Пример вывода:


      NAME         PROVISIONER                RECLAIMPOLICY   VOLUMEBINDINGMODE   ALLOWVOLUMEEXPANSION   AGE
fast.ru-3b   cinder.csi.openstack.org   Delete          Immediate           true                   14m

Можно также увидеть и подробную информацию о конкретном классе:


      kubectl describe storageclass fast.ru-3b

Команда выше незаменима, когда нужно разобраться в детальных настройках профиля или найти причину ошибки при создании диска.

Как связаны между собой PV и PVC

Связь между заявкой (PVC) и выделенным томом (PV) устанавливается через процесс привязки (binding). Kubernetes не выдает первый попавшийся диск: контроллер проводит строгую проверку совместимости объектов. Чтобы связка состоялась, должны совпасть три ключевых условия:

  • идентичный класс хранилища — StorageClass;
  • совпадающие режимы доступа — Access Modes;
  • достаточный объем — емкость выделяемого PV должна быть больше или равна размеру, запрошенному в PVC.

Если подходящего тома нет, контроллер инициирует его динамическое создание или переводит заявку в режим ожидания.

Требования к будущему хранилищу фиксируются в спецификации. Пример минимального манифеста PVC, который будет использоваться в практической части:


      apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: busybox-pvc
spec:
  accessModes:
    - ReadWriteOnce
  storageClassName: fast.ru-3b
  resources:
    requests:
      storage: 5Gi

Разберем поля манифеста:

  • apiVersion — используемая версия Kubernetes API;
  • kind — тип создаваемого объекта;
  • metadata.name — уникальное имя заявки (busybox-pvc);
  • spec.accessModes — конфигурация прав доступа к диску;
  • spec.resources.requests.storage — запрашиваемый объем дискового пространства (5Gi).

После применения манифеста Kubernetes либо находит подходящий PersistentVolume, либо создает новый с помощью StorageClass и CSI‑драйвера. Далее происходит сама привязка (binding) — когда для PVC находится подходящий PV.

В текущем пространстве имен статус объектов проверяется командами:


      kubectl get pvc
kubectl get pv

Флаг -A (или его полная версия --all-namespaces) указывает Kubernetes, что нужно показать ресурсы во всех пространствах имен кластера — например:


      kubectl get pvc -A

Пример вывода:


      NAMESPACE   NAME          STATUS   VOLUME                                     CAPACITY   ACCESS MODES   STORAGECLASS   VOLUMEATTRIBUTESCLASS   AGE
default     busybox-pvc   Bound    pvc-528f4464-5253-4796-8908-6942af89a125   5Gi        RWO            fast.ru-3b     <unset>                 64m

Если все в порядке, мы должны увидеть, что у PVC статус Bound, а у PV появилась ссылка на PVC. Однако у PVC еще может быть статус Pending. В чем отличие?  

Статус Bound показывает, что успешно произошла связка PVC ↔ PV, все работает. Статус же Pending значит, нет свободного PV, не хватает емкости или возникла ошибка при создании диска на стороне провайдера.

В выводе видим  Access Modes и Reclaim Policy — это важные настройки, поэтому их рассмотрим подробнее.

Есть три вида прав доступа к PV.

  • ReadWriteOnce (RWO) — чтение и запись. Том монтируется только к одной физической ноде. Несколько подов могут делить этот диск, только если они запущены на одном узле.
  • ReadWriteMany (RWX) — чтение и запись с возможностью монтирования к нескольким нодам. Разные поды могут работать с одним ресурсом одновременно,  даже если находятся на разных физических нодах кластера.
  • ReadOnlyMany (ROX) — режим «только чтение», доступный для множества нод.

Политика ReclaimPolicy определяет поведение кластера с данными после удаления PVC. Есть три варианта:

  • Delete — диск удаляется вместе со всеми данными, PV также удаляется из кластера;
  • Retain — физический том и данные сохраняются для дальнейшего ручного администрирования;
  • Recycle — данные с диска удаляются, но PV становится доступным для дальнейшего использования в кластере.

Еще важно отметить, что есть два режима выделения томов в кластере: статический (Static Provisioning) и динамический (Dynamic Provisioning). На практике почти всегда используется динамический (через StorageClass), но важно понимать разницу:

  • статическое выделение — подразумевает ручное создание PV администратором с последующим формированием ссылающегося на него PVC;
  • динамическое — автоматизирует процесс: создается только PVC, а Kubernetes генерирует хранилище самостоятельно.

В практических примерах мы как раз воспользуемся динамическим созданием хранилища в кластере.

Практика

Закрепим знания на простых примерах. Создадим базовый кластер в Managed Kubernetes с одной воркер-нодой. Для этого перейдите в панель управления → ПродуктыManaged KubernetesСоздать кластер.

Первая шаг создания кластера Managed Kubernetes в панели управления Selectel. Здесь задаются имя кластера, локация, версия Kubernetes, тип кластера и возможность подключения к нему из интернета.
Второй шаг - настройка нод. Выбирается тип сервера (облачный или выделенный), сегмент пула, конфигурация ноды, количество нод, метки. При необходимости здесь же можно сделать группу нод прерываемой.
Финальный шаг создания кластера. Здесь можно включить и задать параметры автовосстановления нод, а также включить аудитные логи.

Подробнее о том как, начать работать с Managed Kubernetes — в нашей документации.

Первое, что стоит сделать в любом кластере — посмотреть существующие тома:


      kubectl get pvc -A
kubectl get pv -A

Если кластер пуст, команды вернут:


      No resources found

Вывод ожидаемый, так как в кластере нет клиентских подов. Создадим тестовый под на базе легковесного образа busybox и заявку на хранилище — PVC.


      kubectl apply -f - <<EOF
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: busybox-pvc
spec:
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 5Gi
---
apiVersion: v1
kind: Pod
metadata:
  name: busybox
spec:
  containers:
    - name: busybox
      image: busybox:latest
      command: ["/bin/sh", "-c"]
      args:
        - |
          while true; do
            date >> /data/log.txt
            sleep 10
          done
      volumeMounts:
        - name: storage
          mountPath: /data
  volumes:
    - name: storage
      persistentVolumeClaim:
        claimName: busybox-pvc
EOF

YAML‑файл для формирования PVC мы уже разобрали выше, теперь посмотрим, как создавать под.

Начинаем с указания базовой информации:

  • apiVersion — версия Kubernetes API;
  • kind — тип объекта;
  • metadata.name — служебная информация об объекте, указываем Pod;

Далее описываем список контейнеров внутри пода. В нашем случае контейнер только один:

  • spec.containers.name — имя контейнера;
  • image — образ контейнера;
  • command — команда запуска контейнера;
  • args — аргументы для команды запускают цикл, который каждые 10 секунд записывает текущую дату в файл /data/log.txt;
  • volumeMounts — монтирует том хранилища storage внутрь контейнера по пути /data;
  • volumes — задает список томов для пода: name — имя тома, persistentVolumeClaim — указывает источником тома заявку busybox-pvc.

Проверим, что заявка PVC благополучно сформирована:


      kubectl get pvc -A

Пример вывода:


      NAMESPACE   NAME          STATUS   VOLUME                                     CAPACITY   ACCESS MODES   STORAGECLASS   VOLUMEATTRIBUTESCLASS   AGE
default     busybox-pvc   Bound    pvc-528f4464-5253-4796-8908-6942af89a125   5Gi        RWO            fast.ru-3b     <unset>                 64m

Видим не только параметры самой заявки, но и уже созданный том (volume) для пода, что видно по статусу Bound у заявки. Процесс происходит обычно быстро, вплоть до нескольких секунд.

Статус Bound также значит, что у PVC появилась связка с PV. Посмотрим ресурсы в кластере:


      kubectl get pv -A

Вывод:


      NAME   CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS   CLAIM                 STORAGECLASS   VOLUMEATTRIBUTESCLASS   REASON   AGE
pvc-528f4464-5253-4796-8908-6942af89a125   5Gi        RWO            Delete           Bound    default/busybox-pvc   fast.ru-3b     <unset>                          64m

Действительно есть PV размером 5 Гбайт — как и было указано в PVC. 

Детальную конфигурацию, которая не отображается в выводе kubectl get pvc, а также возможные причины ошибок, при их наличии, можно изучить с помощью команды:


      kubectl describe pvc busybox-pvc

Здесь можно обратить внимание на статус (Bound или Pending), к какому PV он привязан, какой StorageClass используется.

Монтирование и устройство на ноде

Посмотрим, как volume попадает в под и что происходит при его старте.

Когда под запускается с подключенным томом, включается kubelet — агент, управляющий контейнерами на ноде. Он выполняет следующие шаги:

  • считывает спецификацию пода и информацию о требуемом томе;
  • с помощью драйвера CSI подключает диск к физическому серверу (ноде);
  • монтирует хранилище в файловую систему ноды;
  • прокидывает директорию внутрь изолированного контейнера.

Для приложения внутри контейнера том выглядит как обычный каталог с файлами. Все тома Kubernetes монтирует в одну директорию на ноде — /var/lib/kubelet/pods/. Путь к хранилищу конкретного пода в любой момент времени можно посмотреть по следующему пути: /var/lib/kubelet/pods/<pod-uuid>/volumes/.

Визуально выглядит это так:

Общая схема процесса монтирования Persistent Volume в Kubernetes Pod через CSI.
Процесс монтирования тома в контейнер.

Практика

Чтобы узнать уникальный идентификатор пода (UUID), выполняется команда:


      kubectl get pod <имя-пода> -o jsonpath='{.metadata.uid}'

После подключения к ноде можно перейти в директорию монтирования:


      /var/lib/kubelet/pods/<pod-uuid>/volumes/

Вывод:


      /var/lib/kubelet/pods/a958e390-1f3c-4dea-89fd-d407ab644a63/volumes/kubernetes.io~csi/pvc-528f4464-5253-4796-8908-6942af89a125/mount# ls -l
total 36
-rw-r--r-- 1 root root 18821 May 14 11:30 log.txt
drwx------ 2 root root 16384 May 14 09:42 lost+found

Команда ls -l показывает сгенерированный тестовым контейнером файл log.txt, который мы указывали в deployment при развертывании busybox.

У вас будут другие UUID, так как у каждого пода свой уникальный идентификатор.

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

Работа с PVC:


      kubectl get pvc
kubectl describe pvc

Получение информации о PV:


      kubectl get pv

Отладка подов:


      kubectl describe pod

Просмотр логов драйвера CSI:


      kubectl -n kube-system get pods | grep csi
kubectl logs <csi-pod>

Заключение

Система хранения в Kubernetes выглядит структурированно, если рассматривать ее как поэтапный процесс:

  • администратор формирует PVC,
  • Kubernetes через StorageClass вызывает CSI,
  • CSI создает реальный диск в инфраструктуре,
  • появляется PV,
  • PVC и PV связываются,
  • Под начинает использовать volume.

Хранилища в Kubernetes кажутся сложными ровно до тех пор, пока не раскладываются на цепочку: Под → PVC → CSI → внешнее хранилище → PV. Такой подход объясняет происхождение дисков, алгоритмы определения их размера и зоны ответственности каждого компонента.

Понимание границы между абстракциями кластера и реальным накопителем значительно облегчает диагностику проблем с выделением ресурсов.

Главный вывод: Kubernetes не занимается физическим хранением данных, а лишь управляет надежными и безопасными маршрутами доступа к ним.