Проблемы со storage в Kubernetes часто выглядят одинаково: Pending у PVC, FailedMount у пода, внезапный ReadOnly у файловой системы или «вечное удаление» PV/PVC из-за finalizers. На практике это разные стадии одного lifecycle: provision → bind → attach → mount → cleanup. Если понять, на каком этапе «застряло», решение обычно находится быстро.
Ниже — рабочий чеклист: что смотреть в объектах и событиях, какие проверки безопасны, и где начинаются действия с риском для данных (detach/force-delete/finalizers).
Карта симптомов: где именно застряло
Сначала классифицируйте проблему по стадиям — это экономит время и убирает лишние гипотезы.
- PVC Pending — том ещё не создан или не связан с PV (provision/binding).
- Pod FailedMount — PV/PVC уже существуют, но под не может смонтировать том (attach/mount).
- ReadOnly — том смонтирован, но запись невозможна (политика доступа или ошибки ФС/устройства).
- PV/PVC Terminating + finalizers — Kubernetes ждёт cleanup (detach/delete) и блокирует удаление.
- VolumeAttachment завис/ошибка — типично для CSI attach/detach после падений нод или проблем контроллеров.
Базовая диагностика за 10 минут (без предположений)
Наша цель — собрать факты: состояние PVC/PV, события, StorageClass и наличие CSI-attach через VolumeAttachment. Начинайте именно так даже в «очевидных» случаях: по событиям часто видно первопричину.
1) Проверяем PVC/PV и события
kubectl get pvc -A
kubectl get pv
kubectl describe pvc -n NAMESPACE PVC_NAME
kubectl describe pv PV_NAME
В describe важны блоки Events и поля:
storageClassNameу PVCvolumeName(если уже забинден)accessModesиvolumeModepersistentVolumeReclaimPolicyу PV
2) Проверяем StorageClass и provisioner
kubectl get storageclass
kubectl describe storageclass SC_NAME
Смотрите provisioner, volumeBindingMode и параметры драйвера. Критичные моменты:
volumeBindingMode: WaitForFirstConsumer— PVC может быть Pending до появления пода и выбора ноды/зоны (это не всегда ошибка).allowVolumeExpansionи корректность параметров (тип диска, зона/пул,fsTypeи т.д.).
3) Если проблема на уровне пода: describe pod
kubectl describe pod -n NAMESPACE POD_NAME
Ищите события и ошибки: FailedMount, AttachVolume.Attach failed, MountVolume.MountDevice failed, rpc error. Это уже зона ответственности CSI и конкретной ноды.
4) Проверяем CSI-объекты: VolumeAttachment
kubectl get volumeattachment
kubectl describe volumeattachment VA_NAME
Если VolumeAttachment в ошибке или «висит» при удалении, это прямой индикатор проблем attach/detach (часто после аварий нод).
5) Проверяем компоненты CSI (поды и логи)
kubectl get pods -n kube-system
kubectl get pods -A | grep -i csi
Дальше — точечные логи у controller/node-плагинов (названия зависят от драйвера):
kubectl logs -n kube-system POD_NAME -c CONTAINER_NAME --tail=200
Чаще всего в логах всплывает одно из: проблемы авторизации к API облака/СХД, нехватка прав у ServiceAccount, topology/zone mismatch, таймауты, несовпадение volumeHandle, сбои node-plugin.
Если кластер крутится на собственных серверах, под Kubernetes обычно удобнее держать отдельные воркеры на VDS: проще контролировать ядро, модули, iSCSI/NFS-клиенты и версию container runtime — а значит, меньше сюрпризов на этапе mount.

PVC Pending: почему том не создаётся или не биндится
Pending PVC означает: Kubernetes не смог подобрать подходящий PV или динамически создать новый. Ниже — типовые причины и короткие проверки.
1) Не найден или не работает provisioner (StorageClass указывает на несуществующий CSI)
Симптомы в describe pvc: события про failed to provision volume, no volume plugin matched, timed out waiting for external-provisioner.
- Проверьте, что CSI-драйвер установлен и его controller-поды в
Running. - Сверьте
provisionerв StorageClass — он должен совпадать с реальным драйвером. - Посмотрите логи external-provisioner (часто контейнер называется
csi-provisioner).
2) WaitForFirstConsumer: Pending — это «норма», пока нет планирования
Если volumeBindingMode = WaitForFirstConsumer, PVC будет Pending до тех пор, пока под не будет создан и не выберется нода/зона. Ошибка начинается, когда под создан, но планировщик не может выбрать ноду из-за ограничений.
kubectl describe storageclass SC_NAME
kubectl describe pod -n NAMESPACE POD_NAME
Смотрите события планировщика: часто корень в nodeSelector, taints/tolerations, нехватке ресурсов или topology-ограничениях CSI.
3) Несовместимость accessModes/volumeMode/size
- PVC просит
ReadWriteMany, а драйвер умеет толькоReadWriteOnce. - Указан
volumeMode: Block, но приложение ожидает filesystem. - Запрошен размер, который драйвер или пул не может выделить (квоты, лимиты, free space).
Сверяйте спецификацию PVC и возможности StorageClass/CSI (документация драйвера плюс события provisioner).
4) Static PV не подходит (mismatch storageClassName/labels/claimRef)
Для заранее созданного PV частая ошибка — PV и PVC не совпадают по storageClassName, accessModes, volumeMode или capacity. Ещё один «тихий убийца» — старый claimRef на PV.
kubectl get pv PV_NAME -o yaml
kubectl get pvc -n NAMESPACE PVC_NAME -o yaml
FailedMount: PVC Bound, но под не может смонтировать том
FailedMount — это этап после binding. Причины обычно делятся на «attach не случился» и «attach есть, но mount не прошёл».
1) Attach/Detach не проходит: смотрим VolumeAttachment
kubectl get volumeattachment
kubectl describe volumeattachment VA_NAME
attached: falseи текст ошибки в status- какая нода указана в
nodeName(важно при «том держит другая нода»)
2) Том «занят» другой нодой (multi-attach error)
Классика для ReadWriteOnce: под переехал на другую ноду, а хранилище всё ещё считает том подключенным к старой. В событиях пода обычно есть Multi-Attach error.
- Убедитесь, что старый под действительно удалён и больше не держит PVC.
- Проверьте, жива ли старая нода: если нода умерла, detach может зависнуть.
- Удалять проблемный
VolumeAttachmentимеет смысл только когда вы уверены, что том не используется на старой ноде (иначе можно получить повреждение ФС).
3) MountDevice/MountVolume: fsType, утилиты на ноде, доступ к секретам
Если attach успешен, но mount падает, в событиях часто фигурируют:
- ошибки формата/ФС, неверный
fsType - отсутствие нужных утилит на ноде (зависит от драйвера)
- ошибки прав доступа/секретов (часто для сетевых хранилищ)
Ключевой шаг — логи node-plugin CSI на конкретной ноде (обычно DaemonSet). Найдите pod node-plugin, запущенный на нужной ноде, и посмотрите kubectl logs.
4) Topology/zone mismatch (особенно при WaitForFirstConsumer)
Если том создан в определённой зоне/топологии, а под планируется на ноду в другой зоне, получите бесконечные попытки attach/mount. Проверяйте:
- аннотации/топологию PV
- ограничения StorageClass
- labels нод (topology keys) и правила планирования пода
ReadOnly: почему том внезапно стал только для чтения
Ситуация «вчера писали, сегодня read-only» пугает сильнее всего. Но Kubernetes редко является первопричиной: чаще это защита данных на уровне ядра, файловой системы или backend-хранилища.
1) Файловая система переведена в read-only из-за I/O ошибок
При ошибках ввода-вывода ext4/xfs могут переключиться в ro. Kubernetes лишь видит, что записи не проходят.
- Определите масштаб: один под/том или массово на ноде (второе чаще про проблемы диска/СХД/сетевого пути).
- Посмотрите события пода и логи приложения (ошибки записи).
- Планируйте обслуживание: проверка и восстановление ФС (например, fsck/xfs_repair) обычно делаются вне прод-окна и зависят от типа тома и способа подключения.
2) Ошибка режима доступа: пытаемся писать в RO-том
Проверьте, не примонтирован ли том как read-only на уровне манифеста:
kubectl get pod -n NAMESPACE POD_NAME -o yaml
Ищите в volumeMounts поле readOnly: true. Также проверьте, не используете ли один и тот же PVC одновременно в нескольких местах, где драйвер ограничивает запись.
3) Snapshot/clone/restore: ограничения после восстановления
Зависит от CSI и backend. Иногда после restore нужны корректные параметры в StorageClass или другой режим доступа. Здесь почти всегда помогают логи CSI-controller и точный текст ошибок в событиях.

Finalizers: почему PV/PVC/VolumeAttachment «не удаляются»
Finalizer — механизм Kubernetes, который не даёт объекту исчезнуть, пока контроллер не выполнит «последнее действие»: detach, delete, cleanup на backend.
Снятие finalizers — это принудительное завершение lifecycle. Делайте это только когда вы уверены, что контроллер не выполнит cleanup сам, и вы понимаете последствия для данных и ресурсов (например, «залипший» диск может остаться на backend и продолжать тарифицироваться).
1) Выясняем, какой finalizer мешает
kubectl get pvc -n NAMESPACE PVC_NAME -o jsonpath='{.metadata.finalizers}'
kubectl get pv PV_NAME -o jsonpath='{.metadata.finalizers}'
kubectl get volumeattachment VA_NAME -o jsonpath='{.metadata.finalizers}'
Часто встречаются finalizers защиты PV/PVC и finalizers внешнего провижионера/CSI.
2) Смотрим reclaim policy: что будет с данными
persistentVolumeReclaimPolicy определяет судьбу backend-ресурса:
- Delete — удаление PVC/PV должно удалить реальный диск/шару (если контроллер работает).
- Retain — диск останется, PV перейдёт в
Released, дальше всё вручную.
kubectl get pv PV_NAME -o jsonpath='{.spec.persistentVolumeReclaimPolicy}'
3) Типовые причины stuck finalizer
- CSI-controller не работает или не имеет прав удалять/отвязывать том на backend.
VolumeAttachmentзавис: backend считает том подключенным, detach не проходит.- Нода умерла, а контроллер не может корректно завершить detach.
- Ресурс на backend удалён вручную, а Kubernetes ждёт подтверждение удаления.
4) Минимальная безопасная последовательность перед принудительным снятием
- Убедитесь, что поды, использующие PVC, удалены или остановлены.
- Проверьте, нет ли активного attach на другой ноде (через
VolumeAttachmentи события). - Проверьте состояние CSI-подов и их логи. Иногда достаточно восстановить права/секреты или перезапустить контроллер.
- Поймите последствия
persistentVolumeReclaimPolicyи нужно ли сохранить данные.
5) Как снять finalizer точечно (когда проверки уже сделаны)
Самый аккуратный вариант — JSON patch, чтобы убрать список finalizers:
kubectl patch pvc -n NAMESPACE PVC_NAME --type=json -p='[{"op":"remove","path":"/metadata/finalizers"}]'
kubectl patch pv PV_NAME --type=json -p='[{"op":"remove","path":"/metadata/finalizers"}]'
kubectl patch volumeattachment VA_NAME --type=json -p='[{"op":"remove","path":"/metadata/finalizers"}]'
Если remove не проходит из-за отсутствия поля, задайте пустой список:
kubectl patch pvc -n NAMESPACE PVC_NAME --type=merge -p='{"metadata":{"finalizers":[]}}'
VolumeAttachment: как понять, что именно «держит» том
Когда FailedMount или удаление PV/PVC упирается в attach/detach, объект VolumeAttachment часто является лучшей подсказкой. Поля, на которые стоит смотреть в describe:
attacher(какой драйвер)nodeName(куда пытается attach)- сообщение об ошибке в статусе
Практика: убедитесь, что для конкретного PV/тома активен ровно один attachment и он указывает на ожидаемую ноду. Если attachments «размножились» после аварии, сначала фиксируйте реальное состояние на backend, и только потом чистите объекты в Kubernetes.
Чеклист: быстрые решения по симптомам
PVC Pending
- Проверить
storageClassNameиprovisionerу StorageClass. - Учесть
WaitForFirstConsumer: создать под и посмотреть scheduling events. - Сверить
accessModes/volumeMode/размер с возможностями драйвера. - Проверить квоты/лимиты namespace и права CSI (по логам provisioner/controller).
FailedMount
- Посмотреть
kubectl describe podи точный текст ошибки. - Проверить
VolumeAttachment, статусattachedи multi-attach. - Посмотреть логи CSI node-plugin на нужной ноде.
- Проверить topology/zone и ограничения планирования.
ReadOnly
- Проверить
readOnly: trueвvolumeMounts. - Искать признаки I/O ошибок и деградации backend-хранилища.
- Планировать проверку/восстановление ФС по процедуре, подходящей вашему типу тома.
Stuck finalizer / Terminating
- Сначала выяснить
persistentVolumeReclaimPolicyи требования к сохранению данных. - Убедиться, что поды удалены и нет активного attach.
- Восстановить работу CSI-controller (часто достаточно починить права/секреты или перезапустить компоненты).
- Только потом — снять finalizers патчем.
Нюансы, о которых часто забывают
Resize/expand и «подвисшие» операции
При включенном расширении томов (allowVolumeExpansion) PVC может зависать в промежуточных состояниях, если node-side расширение не происходит (например, под не перезапущен или драйвер требует re-mount). Если видите события про resize, проверьте требования конкретного CSI-драйвера и состояние пода.
Retain-policy и «кладбище» Released PV
Если persistentVolumeReclaimPolicy = Retain, удаление PVC не удалит данные, но PV останется в Released и не будет автоматически переиспользован, пока вы вручную не очистите claimRef и не приведёте PV в пригодное состояние. Это нормально, но часто воспринимается как утечка ресурсов.
Когда не стоит чинить «в лоб»
Если есть признаки деградации хранилища (I/O ошибки, массовые ReadOnly, нестабильный attach), сначала стабилизируйте backend. Иначе «патчи finalizers» и ручные detach будут маскировать симптомы и повышать риск потери данных.
Итог: как быстрее всего прийти к причине
В задачах с PV/PVC выигрывает дисциплина: начинайте с kubectl describe и событий, затем сопоставляйте StorageClass → CSI → VolumeAttachment → pod events. Pending обычно про provision/binding и StorageClass, FailedMount — про attach/mount и VolumeAttachment, ReadOnly — про состояние ФС/доступ, а stuck finalizer — про незавершённый cleanup.
Если параллельно усиливаете изоляцию и безопасность контейнеров на нодах (чтобы проблемные workload’ы меньше влияли на окружение), полезно разобраться в подходах к изоляции: gVisor и Firecracker для изоляции контейнеров.
Хотите адаптацию чеклиста под ваш CSI (Ceph RBD, NFS, Longhorn, OpenEBS, облачные диски): подготовьте вывод kubectl describe pvc, kubectl describe pod и один kubectl describe volumeattachment — и можно разбирать по шагам.


