Имеется Kubernetes кластер, работает на AWS Elastic Kubernetes Service.
В кластере запущено приложение, которое в целом работает без проблем, но система мониторинга периодически сообщает, что:
Проверяем поды:
[simterm]
$ kk -n eks-prod-1-web-projectname-admin-backend-ns get pod NAME READY STATUS RESTARTS AGE bttrm-web-projectname-admin-backend-64648597fc-9j29n 1/1 Running 0 43m bttrm-web-projectname-admin-backend-64648597fc-kptjj 1/1 Running 0 43m bttrm-web-projectname-admin-backend-7f4b5bdb4c-wlbjf 0/1 Evicted 0 12d bttrm-web-projectname-admin-backend-8478d778f9-5mrnc 0/1 Evicted 0 15d
[/simterm]
Два пода оказались в Evicted статусе — начинаем разбираться.
Содержание
Kubernetes requests
и limits
В Kubernetes мы имеем возможность ограничить используемые контейнерами ресурсы двумя способами — requests
и limits
:
resources: requests: cpu: 100m memory: 100Mi limits: cpu: 100m memory: 100Mi
Тут:
requests
: используется Kubernetes Scheduler при выборе рабочей ноды, на которой будет запущен под — тут задаётся минимальное значение памяти и/или ЦПУ, которые должны быть доступны на ноде для запуска подаlimits
: такой себе «hard-limit» — максимальное значение ресурсов, которые доступны контейнеру для использования
Kubernetes pods QoS classes
Документация — Configure Quality of Service for Pods.
Поды в Kubernetes могут попадать под один из трёх классов Quality of Service (QoS):
- Guaranteed: поды, для которых указаны реквесты и лимиты для всех контейнеров, и для всех они одинаковы
- Burstable: не гарантированные поды, для которых задан реквест как минимум на CPU или память для одного из контейнеров
- Best effort: поды без реквестов и лимитов вообще
Проверить QoS класс пода можно через describe pod -o yaml
:
[simterm]
$ kubectl get pod appname-54545944b4-h8gmv -o yaml ... resources: requests: cpu: 100m ... qosClass: Burstable ...
[/simterm]
Тут задан requests
только на CPU, и под попадает под QoS class Burstable.
А теперь посмотрим — как QoS класс влияет на жизнь подов.
Node tolerations
Когда WorkerNode в кластере исчерпытывает доступные ей ресурсы — память, ЦПУ, диск и т.д. — scheduler кластера во-первых перестаёт добавлять новые поды на эту ноду.
Кроме того, Kubernetes к такой ноде добавляет аннотации, указывающие на проблему с нодой, например — node.kubernetes.io/memory-pressure
.
См. полный список в Taint based Evictions.
В таком случае, kubelet
на этой ноде начинает убивать запущенные контейнеры, а их поды отмечает как Failed.
Например, при заполнении диска — kubelet
будет удалять в первую очередь неиспользуемые поды и их контейнеры, затем — удалять образы с дисков.
Если этого недостаточно — начинает «выселять» (собственно — Evict) пользовательские поды в следующем порядке:
- Best Effort — поды без реквестов и лимитов вообще
- Burstable — поды, использующие больше ресурсов, чем задано в их реквестах
- Burstable — поды, использующие меньше ресурсов, чем задано в их реквестах
Guaranteed поды в целом не должны затрагиваться, но в случае, если системе на ноде потребуется больше ресурсов, чем доступно — то kubelet
начнёт вытеснять с этой ноды даже те поды, которые попадают под Guaranteed QoS класс.
Увидеть, почему именно под был вынеснен с ноды можно в событиях этого пода.
Например, последние поды у нас сейчас:
[simterm]
$ kk -n eks-prod-1-web-projectname-admin-backend-ns get pod NAME READY STATUS RESTARTS AGE ... bttrm-web-projectname-admin-backend-7f4b5bdb4c-wlbjf 0/1 Evicted 0 14d bttrm-web-projectname-admin-backend-8478d778f9-5mrnc 0/1 Evicted 0 17d
[/simterm]
И события пода:
[simterm]
$ kk -n eks-prod-1-web-projectname-admin-backend-ns get pod bttrm-web-projectname-admin-backend-7f4b5bdb4c-wlbjf -o yaml ... status: message: 'The node was low on resource: memory. Container bttrm-web-projectname-admin-backend was using 1014836Ki, which exceeds its request of 300Mi. ' phase: Failed reason: Evicted
[/simterm]
Тут Kubernetes нам явно сообщает, что «Container was using 1014836Ki, which exceeds its request of 300Mi«.
Тут Ki == kibibyte (1024 байта, 8192 бита), а Mi — mebibyte, т.е. 1024 kibibytes, или 1048576 байт.
Проверяем — переведём 1014836Ki в мегабайты:
[simterm]
$ echo 1014836/1024 | bc 991
[/simterm]
Почти гиг памяти.
И ещё раз посмотрим на ресурсы, заданные в Deployment этого пода:
[simterm]
$ kk -n eks-prod-1-web-projectname-admin-backend-ns get deploy bttrm-web-projectname-admin-backend -o yaml ... resources: limits: memory: 3Gi requests: cpu: 100m memory: 300Mi ...
[/simterm]
Т.к. в лимитах во-первых не задан лимит на ЦПУ, во-вторых — лимиты и реквесты не одинаковы — то он попадает под Burstable QoS класс, плюс на этой ноде кроме подов этого деплоймента других подов не было — и Kubernetes его прибил.
Посмотрим историю статусов пода (см. Kubernetes: мониторинг кластера с Prometheus Operator):
Жил-был под на рабочей ноде 10.4.44.215 в статусе Running, а потом бац — и в 19:40 убился.
Посмотрим на состояние ноды 10.4.44.215 в это время:
Нода исчерпала доступную память (AWS EC2 t3.medium, 4GB RAM), и kubelet
начал вытеснять поды.
И глянем ресурсы, которые под потреблял в этом время:
Собственно, видим, что на под прилетело немного входящего трафика — и он активно начал отжирать память.
Память на ноде закончилась — и kubelet
этот под прибил.
Дальше уже вопрос к разработчикам — то ли это expected, и надо переселять этот проект на рабочие ноды, у которых больше доступной памяти, и увеличивать подам реквесты и лимиты — либо у них где-то memory leak, и тогда они уже сами будут это фиксить.
Ссылки по теме
- Resource Quotas
- Configure Quality of Service for Pods
- Assign Memory Resources to Containers and Pods
- Understanding resource limits in kubernetes: memory