Kubernetes: Evicted поды и Quality of Service для подов

Автор: | 10/03/2020
 

Имеется Kubernetes кластер, работает на AWS Elastic Kubernetes Service.

В кластере запущено приложение, которое в целом работает без проблем, но система мониторинга периодически сообщает, что:

Проверяем поды:

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

Два пода оказались в 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:

kubectl get pod appname-54545944b4-h8gmv -o yaml
...
   resources:
     requests:
      cpu: 100m
...
qosClass: Burstable
...

Тут задан 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 класс.

Увидеть, почему именно под был вынеснен с ноды можно в событиях этого пода.

Например, последние поды у нас сейчас:

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

И события пода:

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

Тут Kubernetes нам явно сообщает, что «Container was using 1014836Ki, which exceeds its request of 300Mi«.

Тут Ki == kibibyte (1024 байта, 8192 бита), а Mi — mebibyte, т.е. 1024 kibibytes, или 1048576 байт.

Проверяем — переведём 1014836Ki в мегабайты:

echo 1014836/1024 | bc
991

Почти гиг памяти.

И ещё раз посмотрим на ресурсы, заданные в Deployment этого пода:

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
...

Т.к. в лимитах во-первых не задан лимит на ЦПУ, во-вторых — лимиты и реквесты не одинаковы — то он попадает под 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, и тогда они уже сами будут это фиксить.

Ссылки по теме