Иногда, работая с StatefulSet в Kubernetes, возникает потребность временно «приостановить» работу конкретной реплики. Причём задача усложняется требованием сохранить данные – PersistentVolumeClaim (PVC) – и возможность подключить их к отладочному pod-у, не допуская автоматической реконструкции остановленной реплики. В обычных условиях контроллер StatefulSet следит за числом реплик и незамедлительно восстанавливает отсутствующий pod. Рассмотрим несколько способов, позволяющих реализовать «паузу» для конкретного pod-а с сохранением данных и – что немаловажно – с возможностью возврата в строй без серьезных хлопот.
Цели «паузы»:
– Остановить нужный pod, чтобы он не рестартовал сам себя.
– Сохранить его PVC и данные для отладки.
– Возможность подключения тома к debug-pod для анализа.
– Восстановление pod-а в составе StatefulSet после завершения работ.
Подходы, описанные ниже, актуальны для Kubernetes версии 1.21+ и основаны на стандартных механизмах, которые мы адаптируем под нужды отладки.
Подход 1: Масштабирование StatefulSet (scale down/up)
Данный подход позволяет временно «заморозить» конкретный pod для отладки без риска потерять данные, хранящиеся в PVC. Если, например, в кластере запущено три реплики с именами app-0
, app-1
и app-2
, самый простой способ – уменьшить число реплик на единицу. При этом контроллер StatefulSet автоматически удалит последний pod (в нашем случае – app-2
), а связанный PVC останется привязанным к приложению, что гарантирует сохранность всех данных, необходимых для отладки. По окончании диагностики можно вернуть исходное количество реплик, и контроллер вновь создаст отсутствующий pod, примонтировав ранее сохранённое хранилище.
Сначала необходимо удостовериться в текущем состоянии StatefulSet и убедиться, что число реплик соответствует ожиданиям. Для этого можно выполнить команду:
kubectl get statefulset <имя> -o yaml
Эта команда выведет подробное описание StatefulSet, позволяя увидеть текущее количество реплик и другие параметры. После этого, чтобы временно удалить pod, достаточно уменьшить число реплик. Например, если изначально их три, нужно уменьшить до двух:
kubectl scale statefulset <имя-statefulset> --replicas=2
Контроллер StatefulSet автоматически определит, что необходимо удалить последний pod (в данном случае – app-2
), а привязанный к нему PVC не будет удалён, что гарантирует сохранность данных.
После масштабирования удобно проверить, что pod действительно удалён. Список активных pod-ов можно получить командой:
kubectl get pods -l app=<метка StatefulSet>
Если pod app-2
отсутствует, а команда для просмотра PVC:
kubectl get pvc
показывает, что PVC, соответствующий app-2
, по-прежнему имеет статус Bound, значит, хранилище в безопасности и готово для дальнейших действий.
Далее для проведения отладки создаётся отдельный debug-pod, который монтирует ранее сохранённый PVC. Пример YAML-манифеста debug-pod-а:
apiVersion: v1
kind: Pod
metadata:
name: debug-pod
spec:
containers:
- name: debugger
image: busybox:latest
command: ["sh", "-c", "sleep 3600"]
volumeMounts:
- name: data
mountPath: /data
volumes:
- name: data
persistentVolumeClaim:
claimName: <имя-PVC-от-app-2>
Этот pod создаётся командой kubectl apply -f <имя-файла>.yaml
и запускается с примонтированным томом. Подключиться к debug-pod-у можно с помощью:
kubectl exec -it debug-pod -- sh
Далее, в интерактивной оболочке можно исследовать содержимое тома.
По окончании отладки debug-pod удаляется, чтобы освободить ресурсы:
kubectl delete pod debug-pod
После чего возвращается исходное состояние StatefulSet – количество реплик увеличивается до первоначального числа. Для этого выполним:
kubectl scale statefulset <имя-statefulset> --replicas=3
Контроллер StatefulSet создаст новый pod с именем app-2
, который автоматически примонтирует ранее сохранённый PVC, и приложение продолжит работу с сохранёнными данными.
Плюсы: Простота реализации и минимум манипуляций.
Ограничение: Подходит, только если нужный pod – последний по порядку.
Подход 2: «Осиротение» pod-ов через orphan-удаление StatefulSet
Если pod находится не на конце списка (например, в наборе из 5 реплик требуется отладить app-2
), стандартный метод не подойдет – он удалит последний pod, а не тот, который нам нужен. Вместо этого можно временно удалить объект StatefulSet с использованием флага --cascade=orphan
. Таким образом, удалится только сам контроллер, а все pod-ы останутся в кластере, став «сиротами», то есть без контроля. После этого можно спокойно удалить целевой pod, не опасаясь за потерю его PVC, так как хранилище останется привязанным. После "дебага" можно восстановить исходное состояние – применив ранее сохранённый манифест StatefulSet, контроллер снова возьмёт под своё управление оставшиеся pod-ы и создаст отсутствующий app-2
, автоматически примонтировав его PVC.
Прежде всего, сохраняем текущий манифест StatefulSet, чтобы впоследствии его можно было восстановить. Это делается командой:
kubectl get statefulset <имя> -o yaml > statefulset-backup.yaml
Затем удаляем объект StatefulSet с флагом --cascade=orphan
, чтобы все pod-ы остались в кластере, а контроллер перестал за ними следить:
kubectl delete statefulset <имя-statefulset> --cascade=orphan
После этого можно удалить конкретный pod, который необходимо для отладки:
kubectl delete pod app-2
При этом удалится только выбранный pod, а его PVC останется нетронутым, поскольку хранилище не удаляется вместе с pod-ом. Теперь можно запустить debug-pod, который примонтирует этот PVC, аналогично описанному в первом подходе.
Ну и далее возвращаем StatefulSet в исходное состояние, применив сохранённый ранее манифест:
kubectl apply -f statefulset-backup.yaml
Контроллер вновь «адоптирует» оставшиеся pod-ы и пересоздаст отсутствующий app-2
, примонтировав его PVC, что позволит вернуться к нормальной работе приложения.
Плюсы: Позволяет избирательно приостановить любое нужное приложение без влияния на соседей.
Ограничения: Контроллер временно отсутствует, поэтому при внезапных ошибках другие pod-ы не будут автоматически восстановлены. Работать лучше в запланированное maintenance-время.
Подход 3: Патчинг StatefulSet – nodeSelector как замораживатель
Если хочется сохранить работу всего StatefulSet, но при этом «заморозить» конкретный pod для отладки, можно изменить шаблон его описания, добавив условие, которое невозможно выполнить. Например, добавить в спецификацию pod-а параметр nodeSelector
с меткой, которой нет ни на одном узле (например, debug-pause
). В результате попытка создать или восстановить этот pod приведет к тому, что Kubernetes оставит его в состоянии Pending, поскольку ни один из узлов не удовлетворяет новому требованию.
Для начала рекомендуется опционально изменить стратегию обновления StatefulSet на OnDelete
. Это гарантирует, что внесенные изменения в шаблон не приведут к автоматическому обновлению всех pod-ов, а отразятся лишь на вновь создаваемых экземплярах. Сделать это можно следующей командой:
kubectl patch statefulset <имя> -p '{"spec":{"updateStrategy":{"type":"OnDelete"}}}'
После этого в шаблон pod-а вносят требование с использованием nodeSelector
. Пример патча, который добавляет в спецификацию требование к узлам с меткой node=debug-pause
, выглядит следующим образом:
kubectl patch statefulset <имя> -p '{
"spec": {
"template": {
"spec": {
"nodeSelector": {"node": "debug-pause"}
}
}
}
}'
Как только шаблон изменен, необходимо удалить целевой pod (например, app-2
). Контроллер StatefulSet, обнаружив отсутствие pod-а, попытается создать новый экземпляр, однако из-за невозможности найти узел с требуемой меткой новый pod останется в состоянии Pending:
kubectl delete pod app-2
Теперь, когда целевой pod фактически «заморожен» (находится в ожидании назначения узла), можно запустить debug-pod. Работает это по аналогии с предыдущим подходом: debug-pod запускается по заранее подготовленному YAML-манифесту, после чего к нему подключаются интерактивной командой, например, kubectl exec -it debug-pod -- sh
.
После завершения отладки необходимо вернуть StatefulSet в нормальное состояние. Для этого из шаблона следует убрать добавленное условие – удалить nodeSelector. Это можно сделать следующим патчем:
kubectl patch statefulset <имя> -p '{
"spec": {
"template": {
"spec": {
"nodeSelector": null
}
}
}
}'
После удаления условия целевой pod, находящийся в состоянии Pending, больше не будет «зависать» из-за невозможности найти узел. Выполним:
kubectl delete pod app-2
Контроллер StatefulSet создаст новый pod без недостижимого условия, и он сразу получит доступ к уже существующему PVC с данными.
Плюсы: Позволяет не трогать контроллер StatefulSet, минимально влияет на работу остальных pod-ов.
Ограничения: Метод требует аккуратности и уверенного владения патчингом шаблона, иначе «заморозка» может затянуться надолго.
Нюансы подключения к PVC
Во всех трёх подходах отладка реализуется через отдельный debug-pod, к которому примонтируется PVC остановленной реплики. Важно помнить: если том имеет режим доступа ReadWriteOnce, его может использовать одновременно только один pod. Поэтому необходимо убедиться, что исходный pod реально не активен, иначе debug-pod застрянет в ожидании освобождения тома.
TL;DR
- Scale down/up: Идеален, когда требуется остановить последнюю реплику.
- Orphan deletion: Универсальный способ изъять любой pod, сохранив PVC, за счёт временного удаления контроллера.
- Патчинг через nodeSelector: Тонкое решение для заморозки конкретной реплики, не затрагивая работу остальных pod-ов.
Выбор метода зависит от ситуации и допустимых изменений в кластере. Главное – верно организовать возвращение pod-а в штатное состояние. И, конечно же, сохранить данные.