В Kubernetes не так-то просто что-то удалить вы уверены, что
удалили объект, но оказывается, что он все еще присутствует в
кластере. Вы, конечно, можете выполнять команду kubectl
delete
в повседневных операциях и надеяться на лучшее, но
знание принципов работы delete
команд в Kubernetes
поможет вам понять, почему некоторые объекты остаются после
удаления.
В этой статье мы рассмотрим:
- Какие свойства ресурса влияют на удаление.
- Как финализаторы и ссылки на родителя-владельца управляют удалением объектов.
- Как можно использовать
propagationPolicy
, чтобы изменить порядок удаления. - Как работает удаление, с примерами.
Для простоты во всех примерах мы используем ConfigMaps и базовые команды kubectl. Мы увидим, как работают эти команды, и обсудим результаты их использования на практике.
Базовая команда
delete
В Kubernetes есть несколько команд для создания, чтения,
обновления и удаления объектов. Здесь мы поговорим о четырех
командах kubectl
: create
,
get
, patch
и delete
.
Примеры базовой команды kubectl delete
:
kubectl create configmap mymapconfigmap/mymap created
kubectl get configmap/mymapNAME DATA AGEmymap 0 12s
kubectl delete configmap/mymapconfigmap "mymap" deleted
kubectl get configmap/mymapError from server (NotFound): configmaps "mymap" not found
Перед командой стоит знак $
, далее следует ее
результат. Как видите, мы начали с kubectl create configmap
mymap
, чтобы создать пустой configmap mymap
.
Теперь нужно выполнить get
и убедиться, что он
существует. Затем мы удалили configmap. Если снова сделать
get
, получим ошибку HTTP 404, потому что configmap не
найден.
У команды delete
очень простая диаграмма
состояний:
Диаграмма состояний для delete
Операция незамысловатая, но ей могут помешать некоторые факторы, включая финализаторы и ссылки на родителя-владельца.
Как работают финализаторы
Чтобы понять, как удаляются ресурсы в Kubernetes и почему они иногда все-таки не удаляются, полезно разобраться с финализаторами.
Финализаторы это ключи в манифесте ресурса, отвечающие за операции, которые надо выполнить до удаления объекта. Они контролируют сборку мусора для ресурсов и сообщают контроллерам, какие операции очистки нужно выполнить до удаления ресурса. Они не обязательно обозначают код, который нужно выполнить. По сути, это просто списки ключей, вроде аннотаций. Как и аннотациями, ими также можно управлять.
Скорее всего, вы встречали следующие финализаторы:
kubernetes.io/pv-protection
kubernetes.io/pvc-protection
Они используются для постоянных томов и не дают случайно удалить их. Точно так же некоторые финализаторы могут использоваться для предотвращения удаления любого ресурса.
Ниже приводится configmap без свойств, но с финализатором:
cat <<EOF | kubectl create -f -apiVersion: v1kind: ConfigMapmetadata: name: mymap finalizers: - kubernetesEOF
Контроллер ресурса configmap не понимает, что делать с ключом
финализатора kubernetes
. Такие финализаторы для
configmap я называю мертвыми, и обычно они используются с
неймспейсами. Вот что происходит при попытке удалить configmap:
kubectl delete configmap/mymap &configmap "mymap" deletedjobs[1]+ Running kubectl delete configmap/mymap
Kubernetes сообщает, что объект удален, но на самом деле, в
традиционном понимании, никуда он не удален. Скорее он находится в
процессе удаления. Если мы снова выполним get
, то
узнаем, что объект изменен и теперь содержит временную метку
удаления deletionTimestamp
.
kubectl get configmap/mymap -o yamlapiVersion: v1kind: ConfigMapmetadata: creationTimestamp: "2020-10-22T21:30:18Z" deletionGracePeriodSeconds: 0 deletionTimestamp: "2020-10-22T21:30:34Z" finalizers: - kubernetes name: mymap namespace: default resourceVersion: "311456" selfLink: /api/v1/namespaces/default/configmaps/mymap uid: 93a37fed-23e3-45e8-b6ee-b2521db81638
Если в двух словах, объект был изменен, но не удален. Все потому, что Kubernetes увидел, что у объекта есть финализаторы, и поместил его в состояние read-only. Временная метка удаления показывает, что объект можно только читать а еще из него можно удалить апдейты ключа финализатора. Другими словами, удаление будет завершено, только когда мы удалим из объекта финализатор.
Удалить финализаторы можно с помощью команды patch
.
Фоновое удаление завершится, и объект будет удален. Если мы опять
выполним get
configmap, то ничего не увидим.
kubectl patch configmap/mymap \ --type json \ --patch='[ { "op": "remove", "path": "/metadata/finalizers" } ]'configmap/mymap patched[1]+ Done kubectl delete configmap/mymapkubectl get configmap/mymap -o yamlError from server (NotFound): configmaps "mymap" not found
Диаграмма состояния для финализации выглядит так:
Диаграмма состояний для финализации
Если мы попытаемся удалить объект с финализатором, он останется на этапе финализации, пока контроллер не удалит ключи финализаторов или финализаторы не будут удалены через Kubectl. Когда список финализаторов будет пуст, Kubernetes поместит его в очередь на удаление из реестра.
Ссылки на владельца
Ссылки на владельца (owner references) описывают отношения между группами объектов. Это свойства ресурсов, которые указывают, как они связаны друг с другом, чтобы можно было удалять целые деревья ресурсов.
Правила финализатора обрабатываются, если есть ссылки на владельца. Ссылки на владельца состоят из имени и UID. Они связывают ресурсы в одном неймспейсе и им нужен UID. Ссылки на владельца для подов обычно указывают на replica set, которому они принадлежат. Если удалить deploy или stateful set, дочерние replica set и pod тоже удалятся.
Посмотрим на примере, как работают ссылки на владельца. В первом примере мы сначала создаем родительский объект, а потом дочерний. В результате получаем простейший configmap со ссылками на владельца, то есть родителя:
cat <<EOF | kubectl create -f -apiVersion: v1kind: ConfigMapmetadata: name: mymap-parentEOFCM_UID=$(kubectl get configmap mymap-parent -o jsonpath="{.metadata.uid}")cat <<EOF | kubectl create -f -apiVersion: v1kind: ConfigMapmetadata: name: mymap-child ownerReferences: - apiVersion: v1 kind: ConfigMap name: mymap-parent uid: $CM_UIDEOF
Если мы удалим дочерний объект, в котором есть ссылка на владельца, родитель никуда не денется:
kubectl get configmapNAME DATA AGEmymap-child 0 12m4smymap-parent 0 12m4skubectl delete configmap/mymap-childconfigmap "mymap-child" deletedkubectl get configmapNAME DATA AGEmymap-parent 0 12m10s
В следующем случае мы воссоздали configmap из примера выше. Если
мы удалим родителя, а не дочерний объект, в котором есть ссылка на
него, а потом сделаем get
для configmap, то увидим,
что все configmap пропали:
kubectl get configmapNAME DATA AGEmymap-child 0 10m2smymap-parent 0 10m2skubectl delete configmap/mymap-parentconfigmap "mymap-parent" deletedkubectl get configmapNo resources found in default namespace.
Получается, что если в дочернем объекте есть ссылка на родителя,
он автоматически удаляется при удалении родителя. Это называется
cascade
. По умолчанию cascade имеет значение
true
, но можно указать --cascade=false в kubectl
delete
, чтобы удалить родительский объект, не трогая
дочерние.
В следующем примере у нас есть родительский и дочерний объекты, а еще ссылки на владельца. Если мы удалим родителя, указав --cascade=false, дочерние объекты сохранятся:
kubectl get configmapNAME DATA AGEmymap-child 0 13m8smymap-parent 0 13m8skubectl delete --cascade=false configmap/mymap-parentconfigmap "mymap-parent" deletedkubectl get configmapNAME DATA AGEmymap-child 0 13m21s
Параметр --cascade указывает на политику распространения в API, которая позволяет менять порядок объектов удаления в дереве. В следующем примере используется отправка запроса к API через curl для удаления с фоновой политикой распространения (propagation policy):
kubectl proxy --port=8080 &Starting to serve on 127.0.0.1:8080curl -X DELETE \ localhost:8080/api/v1/namespaces/default/configmaps/mymap-parent \ -d '{ "kind":"DeleteOptions", "apiVersion":"v1", "propagationPolicy":"Background" }' \ -H "Content-Type: application/json"{ "kind": "Status", "apiVersion": "v1", "metadata": {}, "status": "Success", "details": { ... }}
Политику распространения нельзя указать в командной строке с
помощью kubectl только при прямом вызове API. Просто создайте
прокси, чтобы иметь доступ к серверу API со своего компьютера, и
выполните команду curl
с одним URL, чтобы послать
команду delete
.
Есть три варианта политики распространения:
Foreground
: дочерние объекты удаляются до родительского (обратный порядок).Background
: родительский объект удаляется до дочерних (прямой порядок).Orphan
: ссылки на владельца игнорируются.
Если вы удаляете объект, в котором есть ссылки на владельца, то финализаторы также будут учитываться. В результаты деревья объектов могут сохраняться, и удаление будет частичным. В этом случае изучите имеющиеся ссылки на владельца и финализаторы, чтобы понять, что происходит.
Принудительное удаление неймспейса
В некоторых случаях для удаления неймспейсов может потребоваться
принудительная финализация: если вы удалили неймспейс и очистили
все объекты в нем, но неймспейс по-прежнему существует, его можно
удалить принудительно, обновив его подресурс,
finalize
. Так контроллер неймспейса поймет, что нужно
удалить финализатор и выполнить очистку:
cat <<EOF | curl -X PUT \ localhost:8080/api/v1/namespaces/test/finalize \ -H "Content-Type: application/json" \ --data-binary @-{ "kind": "Namespace", "apiVersion": "v1", "metadata": { "name": "test" }, "spec": { "finalizers": null }}EOF
Действовать нужно осторожно, иначе неймспейс будет удален, а осиротевшие объекты останутся, и Kubernetes запутается. В таком случае нужно заново создать неймспейс вручную, и тогда оставшиеся объекты снова в нем появятся, чтобы их можно было очистить и восстановить.
Итоги
Как показывают примеры, финализаторы могут помешать удалению ресурсов в Kubernetes, особенно если речь о родительских и дочерних объектах. Обычно финализаторы добавляются в код не просто так, поэтому нужно все проверить, прежде чем удалять их вручную. С помощью ссылок на владельца можно указывать и удалять деревья ресурсов, хотя и в этом случае будут учитываться финализаторы. Наконец, можно использовать политику распространения, чтобы указывать порядок удаления в прямых вызовах API и контролировать процесс удаления объектов. Теперь вы чуть больше знаете о том, как работает удаление в Kubernetes, и можете поэкспериментировать самостоятельно в продакшен-кластере.
Видео от автора: