ZIM-НИЙ SAAALEЗимние скидки: до −50% на старт и −20% на продление
до 31.01.2026 Подробнее
Выберите продукт

Kubernetes Terminating и finalizers: как разморозить namespace, CRD и PV

Если namespace, CRD или PV застряли в Terminating, почти всегда виноваты finalizers и недоработавшие контроллеры. Разберём диагностику: где искать deletionTimestamp и finalizers, как понять «кто держит» объект, и как безопасно снять блокировки через kubectl.
Kubernetes Terminating и finalizers: как разморозить namespace, CRD и PV

Почему в Kubernetes всё «зависает» в Terminating

Статус Terminating — это не «удаление сломалось», а «удаление началось, но ещё не завершилось». Kubernetes удаляет объекты асинхронно: объект получает metadata.deletionTimestamp, после чего контроллеры должны выполнить завершающие действия и убрать блокировки.

Главный механизм таких блокировок — finalizers. Это список строк в metadata.finalizers, который говорит API-серверу: «не удаляй объект физически, пока конкретная логика не отработает cleanup».

В норме это полезно: корректно отсоединить том, удалить внешний балансировщик, почистить записи в сторонней системе. Но если контроллер удалили, он упал, потерял RBAC-права или завис, финалайзер остаётся навсегда, и вы получаете классический «залипший» ресурс.

Как выглядит проблема на практике

  • Namespace stuck terminating: namespace не удаляется часами или днями.
  • CRD зависает в Terminating, потому что остались custom resources или финалайзеры оператора.
  • PV/PVC не удаляются из-за финалайзеров CSI-драйвера или защитных финалайзеров Kubernetes.

Финалайзер — это не ошибка, а контракт между объектом и контроллером. Снимать finalizers вручную стоит только после понимания, какой внешний cleanup вы пропускаете.

Быстрая диагностика: кто держит finalizer и что именно блокирует удаление

Универсальный алгоритм: (1) убедиться, что удаление стартовало (deletionTimestamp), (2) посмотреть finalizers, (3) понять, какой контроллер должен их снять, (4) по возможности восстановить контроллер, и только потом (5) точечно снимать финалайзер вручную.

Шаг 1. Посмотреть YAML и события

Начните с YAML проблемного объекта:

kubectl get namespace my-ns -o yaml
kubectl get crd widgets.example.com -o yaml
kubectl get pv pvc-123456 -o yaml

Ищите:

  • metadata.deletionTimestamp — удаление действительно началось;
  • metadata.finalizers — что именно блокирует;
  • status.conditions (особенно у namespace) — подсказки, какие типы ресурсов не удаляются;
  • события (Events) — часто прямо говорят, где ошибка (RBAC, таймаут, недоступный API, проблемы хранилища).
kubectl describe namespace my-ns
kubectl describe pv pvc-123456

Шаг 2. Понять, «чей» это финалайзер

Типовые источники finalizers:

  • встроенные механизмы Kubernetes (например, финализация namespace);
  • CSI/Storage-контроллеры (PV/PVC, VolumeAttachment и связанные сущности);
  • операторы (CRD/CR): часто добавляют собственные финалайзеры, чтобы убрать внешние зависимости;
  • реже — policy/GitOps-контроллеры и сервисы, которые создают внешние ресурсы.

Если контроллер жив, но финалайзер не снимается, очень часто причина банальна: у контроллера нет прав (RBAC), он не видит нужный объект, или внешняя система (storage/cloud API) возвращает ошибку.

Шаг 3. Проверить здоровье контроллеров

Перед ручным remove finalizer проверьте, что проблема не лечится восстановлением контроллера:

kubectl get pods -A
kubectl get deploy -A
kubectl get events -A --sort-by=.lastTimestamp

Для CSI почти всегда стоит отдельно смотреть kube-system и namespace драйвера (если он не в kube-system).

Если у вас небольшой кластер и вы запускаете Kubernetes на отдельном сервере, удобнее держать контрольную ноду на предсказуемых ресурсах. В таком сценарии часто выбирают VDS, чтобы не бороться с внезапными ограничениями по CPU/RAM и диску.

Namespace stuck Terminating: причины и безопасный план разморозки

Удаление namespace — особый случай: Kubernetes обязан удалить все объекты внутри. Если хоть один объект не удаляется (в том числе из-за финалайзера), namespace останется в Terminating.

1) Посмотреть conditions у namespace

kubectl get namespace my-ns -o jsonpath='{.status.conditions}'

В conditions обычно видно одно из типовых препятствий: остались ресурсы конкретных типов, не получается перечислить ресурсы из API-группы (часто при проблемных CRD), или найдены объекты с финалайзерами.

2) Найти «залипшие» объекты внутри namespace

Быстрый старт:

kubectl get all -n my-ns

Но проблема часто прячется в «не all»-ресурсах: ingress, networkpolicy, pvc, rolebinding, admission-ресурсы и custom resources. Практичный подход — узнать все namespaced типы, которые вообще существуют в кластере:

kubectl api-resources --verbs=list --namespaced -o name

Дальше проверяйте типы, которые реально используются в этом namespace. Для выборочного поиска объектов с deletionTimestamp или finalizers удобно подключать jq (если он доступен на вашей админ-машине):

kubectl get pods -n my-ns -o json | jq -r '.items[] | select(.metadata.deletionTimestamp!=null or (.metadata.finalizers|length>0)) | .metadata.name + "\t" + ((.metadata.finalizers//[])|join(","))'

Эту же идею повторяйте для pvc, ingress и нужных CR.

3) Частый корень зла: CRD/CR внутри namespace

Если namespace удаляется, а оператор уже удалён или сломан, custom resources могут оставаться «торчать» с финалайзерами и удерживать удаление. Варианты лечения: временно вернуть оператор (лучший вариант) или точечно снять финалайзеры с конкретных CR и удалить их.

Если вы работаете с лёгкими кластерами, где часть функциональности (ingress, балансировка, политики) выбирается руками, полезно иметь под рукой сравнение подходов и типовых ловушек: выбор ingress-контроллера в k3s.

4) Снять финалайзер у namespace (крайняя мера)

Если вы уверены, что внутри не осталось важных внешних ресурсов, и восстановить контроллеры невозможно, можно убрать финалайзеры у namespace. Тогда API «выкинет» namespace даже если не все объекты были корректно доубраны.

Сначала проверьте текущие финалайзеры:

kubectl get namespace my-ns -o jsonpath='{.metadata.finalizers}'

Дальше используйте patch. Вариант 1: удалить поле целиком (JSON Patch):

kubectl patch namespace my-ns --type=json -p='[{"op":"remove","path":"/metadata/finalizers"}]'

Вариант 2: заменить массив на пустой (merge patch):

kubectl patch namespace my-ns --type=merge -p='{"metadata":{"finalizers":[]}}'

После снятия финалайзеров namespace может исчезнуть мгновенно, но внешние зависимости (тома, балансировщики, записи в сторонних системах) уже никто автоматически не почистит.

Схема удаления namespace в Kubernetes: deletionTimestamp, финалайзеры и контроллеры

CRD finalizers: почему CRD зависает при удалении и что делать

CRD — это «схема» и точка входа для custom resources. Пока в кластере существуют объекты этого типа, Kubernetes должен их корректно обработать. Плюс сами CR часто имеют финалайзеры от оператора.

Сценарий A: CRD удаляется, но остаются custom resources

Проверьте, остались ли CR этого типа (пример для widgets.example.com):

kubectl get widgets --all-namespaces

Если список не пустой — удаляйте CR штатно. Если они тоже в Terminating, смотрите metadata.finalizers у конкретного CR:

kubectl get widget my-widget -n my-ns -o yaml

Если оператор, который должен снять финалайзер, уже удалён, это и будет причиной «вечного Terminating».

Сценарий B: CRD застрял из-за финалайзера на самом CRD

Иногда финалайзер висит прямо на CRD. Проверьте:

kubectl get crd widgets.example.com -o jsonpath='{.metadata.finalizers}'

Если вы осознанно идёте на ручное снятие, используйте merge patch:

kubectl patch crd widgets.example.com --type=merge -p='{"metadata":{"finalizers":[]}}'

Сценарий C: проблемы discovery/aggregated API и «висячие» группы

Иногда удаление ломается на этапе API discovery: namespace conditions сообщают, что невозможно перечислить ресурсы определённой API-группы. Такое бывает при битых APIService (aggregated API) или при некорректно удалённых расширениях.

Практичные варианты: вернуть CRD и оператор на время, чтобы они сняли финалайзеры с CR и удалились корректно; либо, если восстановление невозможно, точечно снять финалайзеры с CR, а затем с CRD. Если в кластере есть aggregated API, проверьте его состояние отдельно (ошибки обычно видны в events и логах компонентов).

FastFox VDS
Облачный VDS-сервер в России
Аренда виртуальных серверов с моментальным развертыванием инфраструктуры от 195₽ / мес

PV finalizers и Storage: почему тома не удаляются

С PV/PVC часто путаются ожидания: «удалил PVC — почему PV или диск остался?» или «PV в Terminating и не уходит». Тут одновременно влияют финалайзеры и политика spec.persistentVolumeReclaimPolicy.

Сначала уточняем, какой результат вам нужен

  • Удалить PVC, но сохранить диск для ручного восстановления: обычно используется Retain, и внешний диск останется.
  • Удалить PVC и удалить диск: нужен Delete, а CSI-контроллер обязан быть жив и иметь права на удаление внешнего тома.

Типовые финалайзеры на PV/PVC

  • kubernetes.io/pv-protection и kubernetes.io/pvc-protection — защита от удаления, пока есть привязки;
  • финалайзеры CSI (зависят от драйвера) — пока драйвер не отсоединит или не удалит внешний том.

Проверяем связку PVC → PV и текущие статусы

kubectl get pvc -A -o wide
kubectl get pv -o wide

Дальше смотрим YAML проблемного PV:

kubectl get pv pvc-123456 -o yaml

Полезные поля:

  • spec.claimRef — какой PVC держит PV;
  • spec.persistentVolumeReclaimPolicyDelete или Retain;
  • metadata.finalizers — кто блокирует удаление;
  • spec.csi — какой драйвер отвечает за том.

Когда допустимо вручную убрать финалайзеры у PV

Если CSI-драйвер удалён или сломан, и вы понимаете последствия (например, внешний диск уже удалён вручную, или он вам не нужен), можно снять финалайзеры с PV. Kubernetes завершит удаление объекта, но внешний ресурс при этом не «магически» очистится.

kubectl patch pv pvc-123456 --type=merge -p='{"metadata":{"finalizers":[]}}'

Если блокирует kubernetes.io/pv-protection, сначала убедитесь, что нет живого PVC, который ссылается на PV, и что PV не используется pod’ами (иначе вы рискуете получить потерю данных или странные ошибки монтирования).

Garbage collection в Kubernetes и почему он не спасает от финалайзеров

Встроенный сборщик мусора (garbage collection) удаляет зависимые объекты по ownerReferences и выбранной стратегии propagation. Но GC не «ломает» финалайзеры: если объект защищён finalizers, GC будет ждать, пока финалайзер снимут.

Из-за этого удаление «по цепочке» иногда выглядит бесконечным: вы удалили верхний объект, GC пытается удалять нижние, но один из них застрял на финалайзере, и весь процесс визуально «завис».

Полезные флаги при удалении (не панацея)

Иногда помогает корректно настроить ожидание, но это не обходит финалайзеры:

kubectl delete ns my-ns --wait=false
kubectl delete crd widgets.example.com --wait=false

--wait=false не ускоряет удаление, а лишь не блокирует терминал ожиданием завершения.

Практика: безопасный чеклист перед тем как снимать finalizers вручную

  1. Проверьте, что контроллер существует: оператор/CSI/контроллер облака запущен и не падает.

  2. Проверьте права: нередко контроллер жив, но после изменений потерял RBAC и не может завершить cleanup.

  3. Посмотрите логи контроллера: там обычно есть прямое объяснение (нет доступа к API, ошибка провайдера, quota, таймаут).

  4. Поймите внешний эффект: что именно финализируется (диск, LB, запись в внешней системе).

  5. Действуйте точечно: сначала финалайзер на конкретном CR/PV, и только потом думайте про namespace целиком.

  6. После ручного вмешательства проверьте «хвосты»: внешние ресурсы могут остаться и потребовать ручной уборки.

Пример команд kubectl patch для снятия финалайзеров с CRD и PV

Команды-шпаргалки

Показать финалайзеры

kubectl get namespace my-ns -o jsonpath='{.metadata.finalizers}'
kubectl get crd widgets.example.com -o jsonpath='{.metadata.finalizers}'
kubectl get pv pvc-123456 -o jsonpath='{.metadata.finalizers}'

Снять финалайзеры (merge patch)

kubectl patch namespace my-ns --type=merge -p='{"metadata":{"finalizers":[]}}'
kubectl patch crd widgets.example.com --type=merge -p='{"metadata":{"finalizers":[]}}'
kubectl patch pv pvc-123456 --type=merge -p='{"metadata":{"finalizers":[]}}'

Найти ресурсы в namespace, которые ещё существуют

kubectl get all -n my-ns
kubectl get pvc -n my-ns
kubectl get ingress -n my-ns
kubectl get events -n my-ns --sort-by=.lastTimestamp

Итоги

Если вы видите Terminating, это почти всегда означает одно: объект помечен на удаление, но финализация не завершилась. Для namespace stuck terminating причина обычно в «залипших» объектах внутри (часто CR/CRD) или в контроллерах, которые должны снять финалайзеры. Для crd finalizers и pv finalizers ключевой шаг — понять, какой оператор/CSI отвечает за cleanup, и восстановить его работу. Ручное снятие финалайзеров через kubectl patch — инструмент последней линии: полезный, но требующий осознанных действий и проверки внешних последствий.

Поделиться статьей

Вам будет интересно

Nginx upstream и DNS: как обновлять IP без stale cache и таймаутов OpenAI Статья написана AI (GPT 5)

Nginx upstream и DNS: как обновлять IP без stale cache и таймаутов

Когда Nginx проксирует на upstream по имени, он может «залипать» на старом IP: контейнер пересоздали, запись A поменялась, а прокс ...
SSHD MaxStartups и rate limit: как снизить CPU от brute force и DDoS на SSH OpenAI Статья написана AI (GPT 5)

SSHD MaxStartups и rate limit: как снизить CPU от brute force и DDoS на SSH

Когда SSH засыпают ботами, растут CPU, число полуоткрытых сессий и задержки входа. Разберём MaxStartups и LoginGraceTime в OpenSSH ...
NVMe в Linux: SMART и Health для мониторинга дисков OpenAI Статья написана AI (GPT 5)

NVMe в Linux: SMART и Health для мониторинга дисков

Пошагово разбираем, как в Linux читать SMART/health у NVMe-дисков: команды nvme-cli и smartctl, важные поля (critical_warning, med ...