Learning Platform
Глоссарий Troubleshooting
Урок 14.02 · 20 мин
Продвинутый
QoSGuaranteedBurstableBestEffortevictionoom_score

QoS classes: Guaranteed, Burstable, BestEffort

QoS class — это метка, которую Kubernetes ставит на Pod автоматически, исходя из того, как заданы requests и limits. Вручную выставить QoS нельзя (поле status.qosClass, read-only). Эта метка определяет две вещи: порядок eviction Pods под memory pressure на node и значения oom_score_adj для процессов в container, то есть кого первым убьёт kernel OOM killer. На CKAD QoS — частая теоретическая тема, и одновременно фундамент production-устойчивости.


Приоритеты и nice: как влиять на планировщик из user-space

Три класса

Правила назначения QoS class
GuaranteedДля КАЖДОГО container в Pod выполнено: requests == limits для CPU И для memory. Если limits заданы, но requests опущены — API server defaulting (или LimitRanger) проставит requests = limits, и Pod всё равно станет Guaranteed.
BurstableХотя бы один container имеет request или limit на CPU или memory, но условие Guaranteed не выполняется. Самый частый класс на практике.
BestEffortНи один container в Pod не имеет ни request, ни limit ни на CPU, ни на memory. Pod вообще никак не описал свои потребности.

Примеры:

# Guaranteed: request == limit для обоих ресурсов
resources:
  requests:
    cpu: 500m
    memory: 512Mi
  limits:
    cpu: 500m
    memory: 512Mi

# Также Guaranteed: только limits, kubelet проставит requests=limits
resources:
  limits:
    cpu: 500m
    memory: 512Mi

# Burstable: request < limit хотя бы для одного ресурса
resources:
  requests:
    cpu: 100m
    memory: 128Mi
  limits:
    cpu: 500m
    memory: 256Mi

# Также Burstable: указан только request
resources:
  requests:
    cpu: 100m

# BestEffort: пусто
# (resources не указан вообще, или resources: {})
NOTE

QoS определяется на уровне Pod, но проверка идёт по каждому container. Если в Pod два container, один Guaranteed и один BestEffort — итоговый Pod = Burstable (потому что Guaranteed требует, чтобы ВСЕ container были Guaranteed). Подмешать BestEffort container в Guaranteed Pod нельзя случайно — поднимется до Burstable.

Посмотреть QoS:

kubectl get pod <name> -o jsonpath='{.status.qosClass}'
kubectl describe pod <name> | grep "QoS Class"

Eviction order под memory pressure

Kubelet на каждом node постоянно мониторит сигналы (memory.available, nodefs.available, imagefs.available). Когда memory.available падает ниже eviction threshold (по умолчанию около 100Mi), kubelet сам инициирует eviction Pods, чтобы освободить ресурсы и не дать упасть критичным компонентам node (kubelet, container runtime, kernel).

Eviction priority под memory pressure
1. BestEffortPods вообще без requests/memory limits. Их kubelet считает наименее ценными — они и так не заявляли о потребностях. Убиваются первыми, выбираются по usage (наибольший usage первым).
2. Burstable, превысившие requestBurstable Pod, у которого текущее использование memory выше его request, считается 'overcommitting'. kubelet выбирает victim среди таких Pods, сортируя по (usage - request) — чем сильнее превысил, тем раньше умрёт.
3. Burstable в пределах requestЕсли Burstable Pods не превышают свой request, они тоже могут попасть под eviction, но только если выше уже всё съели.
4. GuaranteedGuaranteed Pods kubelet старается не трогать. Их evict-ит только если ситуация совсем критичная — node под memory pressure и других кандидатов нет.

Eviction kubelet делает SIGTERM → grace period → SIGKILL. Pod помечается как Failed с reason Evicted. По restartPolicy controller (Deployment/StatefulSet/etc.) пересоздаст реплику — но на другом node, не на том же.

WARNING

Eviction отличается от OOMKill. Eviction — это решение kubelet (workload terminate gracefully where possible, status=Failed Evicted). OOMKill — решение kernel (мгновенный SIGKILL процессу, container exit 137). Eviction срабатывает раньше, на уровне node pressure; OOMKill — на уровне cgroup конкретного container.


OOM score и приоритет внутри cgroup

Внутри cgroup memory limit (когда process в одном Pod упёрся в собственный limit) Linux OOM killer выбирает victim среди процессов этой cgroup, и тоже использует приоритеты. Они задаются через oom_score_adj — число от -1000 (никогда не убивать) до 1000 (убить в первую очередь).

Kubelet проставляет oom_score_adj процессам Pod исходя из QoS class:

Guaranteed:     -997    (защищён)
BestEffort:     1000    (убивать первым)
Burstable:     рассчитывается формулой
  oom_score_adj = max(2, min(999, 1000 - (1000 * memory_request_bytes / node_memory_bytes)))

Burstable формула: чем больший memory request относительно ёмкости node, тем меньше oom_score_adj, тем менее охотно kernel выберет process. То есть Burstable с большим request защищён почти как Guaranteed.

Это объясняет, почему сам факт указать memory request снижает шанс быть убитым — даже без limit. Pod, скромно попросивший 1Gi на node с 32Gi, получит oom_score_adj ≈ 970 (плохо, но не худший), а BestEffort — гарантированно 1000.


CPU manager policy и static CPU pinning

С cpuManagerPolicy: static (включается на kubelet) Guaranteed Pod с integer CPU request получает эксклюзивно закреплённые vCPUs (cpuset). Это убирает context switching между ядрами, выгодно для latency-критичных приложений: low-latency trading, video processing, real-time inference.

Условия для pinning:

# Guaranteed, request=limit=integer для CPU
resources:
  requests:
    cpu: 2          # ровно 2 ядра, integer, НЕ 2000m в строке (тоже OK, но integer)
    memory: 4Gi
  limits:
    cpu: 2
    memory: 4Gi

С cpu: 500m или cpu: "1.5" static policy не сработает — нужны integer. Burstable и BestEffort всегда работают в shared CPU pool (SharedCPUs).

Это редко требуется на CKAD, но в production знание помогает: «почему мы видим cache line thrashing на high-frequency сервисе» → потому что Pod Burstable, и kernel перемещает task между ядрами.


Killer момент: BestEffort — приговор в production

# Pod без resources — BestEffort
apiVersion: v1
kind: Pod
metadata:
  name: yolo
spec:
  containers:
  - name: app
    image: my-app
    # никаких resources

Что произойдёт с этим Pod в плохой день:

  • Scheduler посадит его на любой node с самым большим запасом — но запас фейковый, scheduler не знает, сколько Pod реально использует.
  • При первом скачке нагрузки на node — Pod первый в очереди на eviction.
  • При cgroup memory pressure где-то рядом — Pod первый в очереди на OOM kill (oom_score_adj=1000).
  • Даже если node нормальный, при system OOM Pod умрёт раньше любого Pod, у которого хотя бы заявлен memory request.

В production BestEffort приемлем только для нагрузок типа «curl debug Pod», «короткий job», «опциональная вспомогательная задача, которую можно потерять». Никогда не для приложений с пользовательским трафиком.

DANGER

Никогда не выкатывайте Deployment в production без хотя бы memory request. Без request Pod становится BestEffort, и при первой же memory pressure он умрёт раньше всех, не получив shutdown gracefully — пользователи увидят 500/504 без возможности retry на старом instance. Минимальный production-контракт: указать оба request, и memory limit (≈ 1.5–2x request).


Проверка знанийKnowledge check
Pod с двумя containers: первый имеет requests==limits для CPU и memory, второй — только memory request. Какой QoS у Pod?
ОтветAnswer
Burstable. Guaranteed требует, чтобы ВСЕ container в Pod удовлетворяли условию requests==limits для обоих ресурсов. Здесь второй container не выполняет это условие (нет CPU request, request != limit для memory) — поэтому Pod опускается до Burstable. QoS определяется по самому слабому container.
Проверка знанийKnowledge check
На node memory pressure. Какой Pod kubelet evict-нет первым?
ОтветAnswer
BestEffort. Eviction order под memory pressure: BestEffort → Burstable превысившие свои requests → остальные Burstable → Guaranteed. Внутри одного класса — по usage (наибольший usage первым). Логика: BestEffort не заявил потребностей и поэтому считается expendable; Burstable превысивший request нарушил свой 'контракт' и тоже expendable до тех, кто остался в рамках.
Проверка знанийKnowledge check
Внутри Guaranteed Pod с integer CPU request у вас включён cpuManagerPolicy=static. Pod получает 2 эксклюзивных vCPU. Что произойдёт с Burstable Pods на этом же node?
ОтветAnswer
Burstable и BestEffort Pods работают в shared CPU pool — то есть на всех остальных vCPU node, кроме закреплённых за Guaranteed. У них нет access к pinned 2 vCPU. Если Guaranteed Pod не использует свои ядра, они простаивают — Burstable не может их позаимствовать в этом режиме. Это компромисс static policy: гарантия latency для одних в обмен на потерю гибкости для других.
Проверка знанийKnowledge check
У Burstable Pod на node 32Gi выставлен memory request 8Gi. Какой примерно oom_score_adj?
ОтветAnswer
Около 750. Формула: oom_score_adj = 1000 - (1000 * 8 / 32) = 1000 - 250 = 750. Это значит, kernel считает этот Pod 'умеренно ценным': хуже Guaranteed (-997), но сильно лучше BestEffort (1000). Чем больший запрос Pod заявил относительно ёмкости node, тем ниже число и тем безопаснее Pod при cgroup OOM.

Проверьте понимание

Результат: 0 из 0
Концептуальный
Вопрос 1 из 5. Pod с одним container: requests cpu=500m memory=512Mi, limits cpu=500m memory=512Mi. Какой QoS class?

Закончили урок?

Отметьте его как пройденный, чтобы отслеживать свой прогресс

Войдите чтобы оценить урок

Прогресс модуля
0 из 5