← Back to notes

Volumes: PV, PVC
и StorageClass


Файловая система контейнера эфемерна: при перезапуске контейнера все данные теряются. Volumes решают две задачи: сохранение данных при перезапуске контейнера и обмен данными между контейнерами в одном Pod.

[source: kubernetes.io/docs/concepts/storage/volumes/]

Типы Volume

emptyDir

Пустая директория, создаётся при назначении Pod на узел. Существует пока Pod работает. При удалении Pod данные теряются. При перезапуске контейнера — данные сохраняются (это часто удивляет).

Применение: временный кеш, обмен данными между контейнерами одного Pod.

apiVersion: v1
kind: Pod
metadata:
  name: shared-data
spec:
  containers:
    - name: writer
      image: busybox:1.36
      command: ["sh", "-c", "echo 'hello' > /data/message && sleep 3600"]
      volumeMounts:
        - name: shared
          mountPath: /data
    - name: reader
      image: busybox:1.36
      command: ["sh", "-c", "cat /data/message && sleep 3600"]
      volumeMounts:
        - name: shared
          mountPath: /data
  volumes:
    - name: shared
      emptyDir: {}

emptyDir с medium: Memory хранит данные в tmpfs (RAM) — быстрее, но считается в memory limits Pod:

volumes:
  - name: cache
    emptyDir:
      medium: Memory
      sizeLimit: 256Mi

hostPath

Монтирует файл или директорию с файловой системы узла. Данные привязаны к конкретному узлу — при перемещении Pod на другой узел данные недоступны.

volumes:
  - name: host-data
    hostPath:
      path: /var/log/app
      type: DirectoryOrCreate

Типы: Directory, DirectoryOrCreate, File, FileOrCreate, Socket, CharDevice, BlockDevice.

Используется редко — в основном для системных компонентов (DaemonSet для мониторинга или логирования). В production приложениях не использовать: нет переносимости, риски безопасности.

configMap и secret

Монтируют данные из ConfigMap или Secret как файлы (подробно в главе 08):

volumes:
  - name: config
    configMap:
      name: app-config
  - name: creds
    secret:
      secretName: db-credentials

PersistentVolume (PV)

PV — ресурс хранения на уровне кластера, подготовленный администратором или динамически через StorageClass. Существует независимо от Pod.

[source: kubernetes.io/docs/concepts/storage/persistent-volumes/]

apiVersion: v1
kind: PersistentVolume
metadata:
  name: pv-data
spec:
  capacity:
    storage: 10Gi
  accessModes:
    - ReadWriteOnce
  persistentVolumeReclaimPolicy: Retain
  storageClassName: standard
  hostPath:
    path: /mnt/data    # только для dev/test; production использует CSI-драйвер

PersistentVolumeClaim (PVC)

PVC — запрос пользователя на хранилище. PVC привязывается к подходящему PV по размеру, access mode и StorageClass.

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: app-data-claim
spec:
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 5Gi
  storageClassName: standard

Привязка PVC к PV

Kubernetes ищет PV, удовлетворяющий требованиям PVC:

  • capacity.storage >= запрошенный размер
  • Совпадение accessModes
  • Совпадение storageClassName

Привязка — one-to-one: один PV привязывается к одному PVC.

kubectl get pv
kubectl get pvc

# Статусы PV:
# Available  PV свободен
# Bound      PV привязан к PVC
# Released   PVC удалён, PV ожидает очистки
# Failed     ошибка автоматической очистки

Использование PVC в Pod

apiVersion: v1
kind: Pod
metadata:
  name: app-with-storage
spec:
  containers:
    - name: app
      image: myapp:1.0
      volumeMounts:
        - name: data
          mountPath: /var/lib/app/data
  volumes:
    - name: data
      persistentVolumeClaim:
        claimName: app-data-claim

StorageClass

StorageClass определяет «класс» хранилища и позволяет динамически создавать PV при запросе PVC. Не нужно создавать PV вручную.

[source: kubernetes.io/docs/concepts/storage/storage-classes/]

apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: fast-ssd
provisioner: ebs.csi.aws.com
parameters:
  type: gp3
  iops: "3000"
  throughput: "125"
reclaimPolicy: Delete
volumeBindingMode: WaitForFirstConsumer
allowVolumeExpansion: true

При создании PVC с storageClassName: fast-ssd StorageClass автоматически создаст PV.

volumeBindingMode:

  • Immediate — PV создаётся сразу при создании PVC
  • WaitForFirstConsumer — PV создаётся при назначении Pod на узел, учитывает топологию (availability zone)

WaitForFirstConsumer предпочтительнее для облачных сред — гарантирует создание диска в той же AZ, что и Pod.

Access Modes

Не все типы хранилищ поддерживают все режимы. AWS EBS — только RWO. NFS, Azure Files, GCS Fuse — RWX. RWOP — самый строгий, гарантирует эксклюзивный доступ на уровне Pod.

Reclaim Policy

Определяет, что происходит с PV после удаления PVC:

Retain безопаснее для production — данные можно восстановить. Delete удобнее в dev/test для автоочистки.

Полный пример: PV + PVC + Pod (PostgreSQL)

apiVersion: v1
kind: PersistentVolume
metadata:
  name: postgres-pv
spec:
  capacity:
    storage: 20Gi
  accessModes:
    - ReadWriteOnce
  persistentVolumeReclaimPolicy: Retain
  storageClassName: standard
  hostPath:
    path: /mnt/postgres
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: postgres-pvc
spec:
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 20Gi
  storageClassName: standard
---
apiVersion: v1
kind: Pod
metadata:
  name: postgres
spec:
  containers:
    - name: postgres
      image: postgres:16
      env:
        - name: POSTGRES_PASSWORD
          valueFrom:
            secretKeyRef:
              name: pg-secret
              key: password
      ports:
        - containerPort: 5432
      volumeMounts:
        - name: pg-data
          mountPath: /var/lib/postgresql/data
  volumes:
    - name: pg-data
      persistentVolumeClaim:
        claimName: postgres-pvc

В production PostgreSQL запускается через StatefulSet с volumeClaimTemplates — для каждой реплики автоматически создаётся отдельный PVC (глава 11).

Типичные ошибки

PVC зависает в Pending. Причины: нет подходящего PV (если нет динамического провижининга), StorageClass не существует, volumeBindingMode: WaitForFirstConsumer и Pod ещё не назначен на узел. Диагностика: kubectl describe pvc <name>.

Удаление PVC при живом Pod. PVC защищён финализатором kubernetes.io/pvc-protection — удаление зависнет пока Pod жив. Сначала удали Pod.

Неверный AccessMode. Создать PVC с RWX для AWS EBS невозможно — EBS не поддерживает. API Server примет PVC, но привязка к PV завершится ошибкой.

hostPath в production для данных. При перемещении Pod на другой узел данные недоступны. Для multi-replica Deployment с hostPath каждый Pod видит данные только своего узла.

Не настроить allowVolumeExpansion: true. После создания StorageClass без этого параметра расширить PVC невозможно без пересоздания.

Альтернативы

CSI-драйверы (Container Storage Interface) — стандартный механизм подключения хранилищ. AWS EBS CSI, GCE PD CSI, Ceph CSI, Longhorn. Заменили устаревшие in-tree плагины.

Rook/Ceph — распределённое хранилище поверх локальных дисков узлов. Поддерживает RWO, ROX, RWX. Сложен в эксплуатации.

Longhorn — лёгкое распределённое блочное хранилище от Rancher. Проще Ceph, встроенный UI, снапшоты, резервное копирование.

Object Storage (S3, GCS, MinIO) — для неструктурированных данных, медиафайлов, бэкапов. Не через Volume, а через SDK или FUSE-mount (s3fs, mountpoint-s3).


08: ConfigMap и Secret

10: Probes

Volumes: PV, PVC и StorageClass | Aleksandr Suprun