Хранилище: Volumes и PersistentVolumeClaim

Рейтинг: 80.6% · 16 голосов
Kubernetes для разработчиков: поды, деплойменты, сервисы, ingress, конфиги и отладка. Уроки по главам с обсуждением.
Ответить
Аватара пользователя
anton_k8s
Сообщения: 26
Зарегистрирован: 12 май 2026, 03:23

Хранилище: Volumes и PersistentVolumeClaim

Сообщение anton_k8s »

АкадемияKubernetes на практикеГлава 8 из 19
Оглавление курса (19)
  1. Зачем нужен Kubernetes и из чего состоит кластер
  2. Поднимаем локальный кластер: minikube и kind
  3. Поды: базовая единица запуска
  4. Deployment и ReplicaSet: управляем репликами
  5. Service: сетевой доступ к подам
  6. ConfigMap и Secret: выносим конфигурацию
  7. Ingress: пускаем трафик снаружи
  8. Хранилище: Volumes и PersistentVolumeClaim (вы здесь)
  9. Namespaces, requests и limits
  10. Health checks: liveness и readiness пробы
  11. Отладка: почему под не стартует
  12. Helm: пакетный менеджер для Kubernetes
  13. Базовая безопасность: RBAC и доступы
  14. Job и CronJob: разовые и периодические задачи
  15. StatefulSet и DaemonSet: stateful-нагрузки и системные агенты
  16. Стратегии обновления и планирование: rollout и rollback, graceful shutdown, nodeSelector, affinity, taints
  17. Автомасштабирование: HPA по метрикам, обзор VPA и Cluster Autoscaler
  18. Наблюдаемость: логи, метрики, events, обзор Prometheus и Grafana
  19. Безопасность глубже: securityContext, Pod Security Standards, NetworkPolicy, шифрование секретов
Контейнер живет ровно до перезапуска пода. Все, что приложение записало в свою файловую систему, исчезает вместе с ним. Для stateless-сервисов это нормально, но базе данных, кешу или загруженным пользователями файлам нужно место, которое переживет рестарт. За это в Kubernetes отвечают volumes и PersistentVolumeClaim, и в этой главе мы разберем оба уровня.

emptyDir, временное хранилище на жизнь пода:

Самый простой volume. Создается пустым при старте пода, удаляется вместе с ним. Важный нюанс: перезапуск контейнера внутри пода (упал процесс, kubelet его поднял) данные в emptyDir не трогает. А вот удаление пода стирает все.

Код: Выделить всё

apiVersion: v1
kind: Pod
metadata:
  name: cache-demo
spec:
  containers:
  - name: app
    image: nginx:1.27
    volumeMounts:
    - name: cache
      mountPath: /var/cache/app
  volumes:
  - name: cache
    emptyDir:
      sizeLimit: 500Mi
Типичные применения: рабочий каталог для временных файлов и обмен данными между контейнерами одного пода. ConfigMap и Secret из главы 6 монтируются тем же механизмом volumeMounts, отличается только источник данных. Есть еще hostPath, он монтирует каталог прямо с ноды. В проде это почти всегда плохая идея: под привязывается к конкретной ноде, плюс дыра в изоляции. Годится для экспериментов в minikube и для системных агентов, не больше.

PersistentVolume и PersistentVolumeClaim:

Идея в разделении ролей. PersistentVolume (PV) это кусок реального хранилища: облачный диск, NFS-шара, локальный каталог на ноде. PersistentVolumeClaim (PVC) это заявка приложения: нужно 5Gi с таким-то режимом доступа. Kubernetes сам связывает заявку с подходящим томом, разработчику не нужно знать, где физически лежат данные.

Руками PV сейчас почти не создают. Работает динамический provisioning через StorageClass: появился PVC, провижинер сам создал под него диск. В minikube и kind из коробки есть класс с именем standard, он же дефолтный. Проверить:

Код: Выделить всё

kubectl get storageclass
Пишем заявку:

Код: Выделить всё

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: pg-data
spec:
  accessModes:
  - ReadWriteOnce
  resources:
    requests:
      storage: 5Gi
storageClassName не указан, значит возьмется дефолтный. accessModes бывают четырех видов: ReadWriteOnce (том монтируется на запись одной нодой, именно нодой, а не подом), ReadOnlyMany, ReadWriteMany (нужна поддержка со стороны хранилища, обычный блочный диск так не умеет) и ReadWriteOncePod, который жестко ограничивает том одним подом.

Подключаем к постгресу через Deployment из главы 4:

Код: Выделить всё

apiVersion: apps/v1
kind: Deployment
metadata:
  name: postgres
spec:
  replicas: 1
  selector:
    matchLabels:
      app: postgres
  strategy:
    type: Recreate
  template:
    metadata:
      labels:
        app: postgres
    spec:
      containers:
      - name: postgres
        image: postgres:16
        envFrom:
        - secretRef:
            name: pg-secret
        env:
        - name: PGDATA
          value: /var/lib/postgresql/data/pgdata
        volumeMounts:
        - name: data
          mountPath: /var/lib/postgresql/data
      volumes:
      - name: data
        persistentVolumeClaim:
          claimName: pg-data
strategy: Recreate здесь не случайно. С дефолтным RollingUpdate новый под попытается стартовать до смерти старого, и если они окажутся на разных нодах, при RollingUpdate новый под повиснет в Pending, ожидая том, который держит старый. Проверяем результат:

Код: Выделить всё

kubectl get pvc pg-data
kubectl get pv
STATUS Bound значит заявка нашла том. Pending значит что-то не срослось, идем в kubectl describe pvc, там в Events будет причина.

Отдельно про reclaimPolicy у PV. Delete (дефолт для динамически созданных томов) удаляет диск вместе с данными при удалении PVC. Retain оставляет том, данные можно вытащить. В managed-кластерах Yandex Cloud, VK Cloud или Selectel дефолт обычно Delete, проверьте это до того, как удалите PVC боевой базы.

Типичные грабли:

PVC висит в Pending. Чаще всего нет дефолтного StorageClass или в заявке опечатка в имени класса. describe покажет событие с текстом про provisioner.

Запросили ReadWriteMany на блочном хранилище. Облачные диски дают только RWO, RWX умеют файловые системы вроде NFS или CephFS. Заявка останется Pending навсегда.

Postgres падает с жалобой на непустой каталог. На свежем ext4-томе в корне лежит lost+found, и initdb отказывается работать. Лечится переменной PGDATA с подкаталогом, как в примере выше, или через subPath в volumeMounts.

Несколько реплик Deployment с одним RWO-томом. Для баз и прочего stateful правильный инструмент это StatefulSet с volumeClaimTemplates, где каждая реплика получает свой PVC. Он за рамками этой главы, но знайте, куда копать.

Что усвоили: emptyDir для временного, PVC для постоянного, StorageClass решает, откуда возьмется диск, а reclaimPolicy решает судьбу данных. В главе 9 займемся namespaces, requests и limits, то есть наведем порядок в ресурсах кластера.
👍1 ❤️3 🔥 😄 🤔1
✔ Лучший ответ сформирован автоматически — kafkacoder
anton_k8s писал(а):при RollingUpdate новый под повиснет в Pending, ожидая том, который держит старый а как тогда обновлять постгрес без простоя? Recreate же роняет базу на время рестарта. Или в k8s для одиночной базы это считается нормой и без даунтайма только через репликацию и операторы типа CloudNativePG?
Перейти к ответу →
Аватара пользователя
kafkacoder
Сообщения: 1
Зарегистрирован: 24 май 2026, 06:46

Re: Хранилище: Volumes и PersistentVolumeClaim

Сообщение kafkacoder »

✔ Лучший ответ — сформирован автоматически
anton_k8s писал(а):при RollingUpdate новый под повиснет в Pending, ожидая том, который держит старый
а как тогда обновлять постгрес без простоя? Recreate же роняет базу на время рестарта. Или в k8s для одиночной базы это считается нормой и без даунтайма только через репликацию и операторы типа CloudNativePG?
👍1 ❤️ 🔥1 😄 🤔1
Аватара пользователя
veteran_marina
Сообщения: 1
Зарегистрирован: 13 май 2026, 14:58

Re: Хранилище: Volumes и PersistentVolumeClaim

Сообщение veteran_marina »

Спасибо за lost+found, реально полезно. Я на это наступил пару недель назад, под в CrashLoopBackOff, в логах initdb: directory exists but is not empty. Полчаса грешил на секреты, пока не догадался сделать exec в контейнер и посмотреть каталог. PGDATA с подкаталогом сразу все починило.
👍1 ❤️1 🔥1 😄 🤔1
Аватара пользователя
lonelygopher
Сообщения: 1
Зарегистрирован: 13 май 2026, 22:06

Re: Хранилище: Volumes и PersistentVolumeClaim

Сообщение lonelygopher »

А если я кластер kind снесу через kind delete cluster, PV выживут? Подозреваю что нет, нода же это докер-контейнер и local-path пишет внутрь него. Тогда получается для локальной разработки данные базы все равно одноразовые?
👍 ❤️ 🔥 😄 🤔
Аватара пользователя
pro_sanya
Сообщения: 1
Зарегистрирован: 01 июн 2026, 05:13

Re: Хранилище: Volumes и PersistentVolumeClaim

Сообщение pro_sanya »

Маленькое уточнение для тех кто как я сначала прочитал невнимательно: RWO это про ноду, не про под. У нас две реплики веб-приложения спокойно писали в один RWO том, потому что шедулер случайно положил их на одну ноду. А потом при деплое одна уехала на другую и встала в Pending, искали причину полдня.
👍 ❤️ 🔥 😄 🤔
Ответить
← Предыдущая глава
Ingress: пускаем трафик снаружи
Следующая глава →
Namespaces, requests и limits

Все главы курса «Kubernetes на практике»

Поделиться темой: ✈ Telegram VK
  • Похожие темы

Вернуться в «Kubernetes на практике»

Кто сейчас на конференции

Сейчас этот форум просматривают: нет зарегистрированных пользователей и 0 гостей