Каким образом предоставить постоянное хранилище для подов в Kubernetes?

«Каким образом предоставить постоянное хранилище для подов в Kubernetes?» — вопрос из категории Kubernetes, который задают на 23% собеседований Devops Инженер. Ниже — развёрнутый ответ с разбором ключевых моментов.

Ответ

В Kubernetes подам по умолчанию предоставляется эфемерное хранилище, которое уничтожается вместе с подом. Для сохранения данных (логи, состояние БД, загруженные файлы) используются PersistentVolumes (PV) и PersistentVolumeClaims (PVC). Это одна из ключевых концепций для stateful-приложений в K8s.

Основная абстракция: PV и PVC

  • PersistentVolume (PV) — это ресурс кластера, кусок предварительно подготовленного хранилища (например, диск в облаке, NFS-шара, локальный SSD). Администратор кластера создаёт PV.
  • PersistentVolumeClaim (PVC) — это запрос пользователя (пода) на хранилище определённого размера и с определёнными правами доступа. Поды используют PVC.

Статическое выделение (Static Provisioning)

Администратор вручную создаёт PV, а затем пользователь создаёт PVC, который привязывается к подходящему PV.

1. Создание PersistentVolume (например, на основе NFS):

# nfs-pv.yaml
apiVersion: v1
kind: PersistentVolume
metadata:
  name: app-data-pv
spec:
  capacity:
    storage: 10Gi
  accessModes:
    - ReadWriteMany # Могут писать многие поды (подходит для NFS)
  persistentVolumeReclaimPolicy: Retain # Что делать с данными после удаления PVC: Retain, Delete, Recycle
  nfs:
    server: 10.0.1.100
    path: "/exports/app_data"

2. Создание PersistentVolumeClaim:

# app-pvc.yaml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: app-data-pvc
spec:
  accessModes:
    - ReadWriteMany
  resources:
    requests:
      storage: 5Gi # Запрашиваем 5Gi. Будет привязан к PV с >=5Gi (наш 10Gi PV подходит).
  # storageClassName: "" # Для использования статического PV оставляем пустым или не указываем.

3. Использование PVC в поде:

# pod.yaml
apiVersion: v1
kind: Pod
metadata:
  name: app-pod
spec:
  containers:
  - name: app-container
    image: nginx:alpine
    volumeMounts:
    - name: storage-volume
      mountPath: "/usr/share/nginx/html" # Куда в контейнере монтируется том
  volumes:
  - name: storage-volume
    persistentVolumeClaim:
      claimName: app-data-pvc # Ссылаемся на созданный PVC

Динамическое выделение (Dynamic Provisioning) — стандартный подход

Администратор создаёт StorageClass, описывающий «тип» хранилища (например, fast-ssd в Google Cloud). При создании PVC, ссылающейся на этот StorageClass, система автоматически создаёт PV нужного типа.

1. StorageClass (пример для AWS EBS):

# storageclass-gp3.yaml
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: gp3
provisioner: ebs.csi.aws.com # CSI-драйвер для AWS EBS
parameters:
  type: gp3
  fsType: ext4
volumeBindingMode: WaitForFirstConsumer # Диск создастся только при запуске пода (учитывает zone)
reclaimPolicy: Delete

2. PVC, использующий StorageClass:

# dynamic-pvc.yaml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: dynamic-app-pvc
spec:
  accessModes:
    - ReadWriteOnce # Только один под может монтировать том на запись (стандартно для облачных дисков)
  storageClassName: gp3 # Указываем класс хранилища
  resources:
    requests:
      storage: 20Gi

После применения этого манифеста Kubernetes автоматически создаст EBS-диск объемом 20Gi типа gp3 в той же зоне доступности, что и под, и привяжет его.

Практические рекомендации:

  • Для stateful-приложений (базы данных) используйте StatefulSet вместе с динамическим выделением через StorageClass. StatefulSet обеспечивает стабильные сетевые идентификаторы и упорядоченное развёртывание.
  • Выбирайте правильный accessMode: ReadWriteOnce (RWO) для БД, ReadWriteMany (RWX) для общих файловых хранилищ (NFS, CephFS).
  • Мониторьте использование хранилища с помощью kubectl top pod (если установлен metrics-server) и дашбордов в Prometheus/Grafana.