← Kubernetes: планирование
7 мин · средне · Урок 1 из 7

Requests и limits: CPU и память

Чем requests отличаются от limits, откуда берутся единицы m и Mi, почему CPU троттлится, а память убивает Pod.

requests и limits: для чего

У каждого контейнера в Pod можно объявить два числа на каждый ресурс (CPU и память):

containers:
  - name: app
    image: busybox:1.36
    resources:
      requests:
        cpu: 100m
        memory: 128Mi
      limits:
        cpu: 500m
        memory: 256Mi

Без requests scheduler считает контейнер «нулевым» и кладёт на ноду с любым свободным местом. Без limits контейнер может съесть всю оперативку ноды и убить системные демоны.

ПолеКто читаетКогда действуетЧто при нарушении
requestskube-schedulerодин раз, при размещениини одна нода не вмещает, Pod в Pending
limitskubelet и ядро Linuxвсё время работыCPU троттлится, память даёт OOMKilled

Единицы CPU: m

CPU мерится в millicpu (m). 1000m == 1 CPU == 1 ядро на ноде. Дроби: 500m это половина ядра, 100m десятая. Можно писать и числом: cpu: 1 это 1000m.

Под капотом kubelet кладёт это в CFS-квоты Linux. limit 500m значит «за 100 мс реального времени контейнер получит максимум 50 мс CPU». Если приложение хочет больше, оно throttling-ится, но не убивается.

requests для CPU это вес в cpu.shares cgroup. На свободной ноде контейнер может получить больше своего request, если есть пустые циклы. Под нагрузкой делёж идёт пропорционально requests.

Единицы памяти: Mi, Gi

Память мерится в байтах. Суффиксы:

Без i это степени 10 (1M = 1000000, 1G = 1000000000). Почти всегда в манифестах ставят с i: 128Mi, 1Gi. На память requests это число для scheduler, limits это жёсткая OOMKill-граница. Превысил memory limit, процесс убивается ядром с OOMKilled. Никакого throttling, как у CPU.

Числа без суффикса читаются как байты: memory: 128 это 128 байт, а не 128 мегабайт. Контейнер падает на старте с непонятным сообщением.

Как scheduler выбирает ноду

Шедулер смотрит на сумму requests всех уже запущенных Pod на ноде и сравнивает с Allocatable ноды. Если pod просит больше, чем осталось, scheduler пропускает эту ноду.

Это значит: Pod в статусе Pending со Insufficient cpu или Insufficient memory означает «ни одна нода не может вместить мой request». Решения два: уменьшить requests или добавить нод. В кластере из одной ноды второго варианта нет, остаётся только урезать requests.

CPU throttle vs Memory OOMKill

Это две разные истории, важно их не путать:

CPU throttle часто пропускают: приложение жалуется на медленные ответы, а разработчик уверен, что лимит «с запасом». Лекарство простое: смотреть метрики throttle, не только usage.

Глубже: QoS-классы

Ядро темы - requests и limits выше. QoS-классы надстроены над ними, их можно прочитать позже.

По комбинации requests/limits Kubernetes присваивает Pod-у QoS-класс, который влияет на порядок eviction-а при нехватке памяти:

Чтобы у продового приложения была защита от eviction-а, делают QoS Guaranteed. Для batch-job-ов часто хватает Burstable.

Что часто ломается

Связанные команды

# Что объявлено в spec
kubectl get pod app -o jsonpath='{.spec.containers[0].resources}' | jq

# Что реально потребляется (нужен metrics-server)
kubectl top pod app

# Сколько ресурсов на ноде осталось
kubectl describe node <name> | grep -A5 Allocated

# Схема полей
kubectl explain pod.spec.containers.resources
kubectl explain pod.spec.containers.resources.requests

Домашка

Теорию закрепляет задание на своём кластере: 1 шаг с проверкой. Кластер поднимается по треку Kubernetes локально, задания лежат в репозитории k8s-practice.

./run.sh start k8s-resource-requirements     # развернуть стартовое состояние
./run.sh verify k8s-resource-requirements    # проверить текущий шаг

Коротко

Прошли материал до конца? Отметьте урок пройденным.