Requests и limits: CPU и память
Чем requests отличаются от limits, откуда берутся единицы m и Mi, почему CPU троттлится, а память убивает Pod.
requests и limits: для чего
У каждого контейнера в Pod можно объявить два числа на каждый ресурс (CPU и память):
requests: сколько контейнер заранее просит. Используется scheduler-ом при выборе ноды.limits: потолок, который контейнер не может превысить во время работы.
containers:
- name: app
image: busybox:1.36
resources:
requests:
cpu: 100m
memory: 128Mi
limits:
cpu: 500m
memory: 256Mi
Без requests scheduler считает контейнер «нулевым» и кладёт на ноду с любым свободным местом. Без limits контейнер может съесть всю оперативку ноды и убить системные демоны.
| Поле | Кто читает | Когда действует | Что при нарушении |
|---|---|---|---|
requests | kube-scheduler | один раз, при размещении | ни одна нода не вмещает, Pod в Pending |
limits | kubelet и ядро 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
Память мерится в байтах. Суффиксы:
Ki: 1024 байтMi: 1024 × 1024 (~1 МБ)Gi: 1024 × 1024 × 1024 (~1 ГБ)
Без 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 превысил limit: Linux CFS throttle-ит процесс. Программа
бежит медленнее, но живёт. В метриках видно как
container_cpu_cfs_throttled_seconds_total > 0. - Memory превысил limit: Linux OOM-killer убивает процесс. Pod
переходит в
OOMKilled, restartPolicy решает, рестартовать ли.
CPU throttle часто пропускают: приложение жалуется на медленные ответы, а разработчик уверен, что лимит «с запасом». Лекарство простое: смотреть метрики throttle, не только usage.
Глубже: QoS-классы
Ядро темы - requests и limits выше. QoS-классы надстроены над ними, их можно прочитать позже.
По комбинации requests/limits Kubernetes присваивает Pod-у QoS-класс, который влияет на порядок eviction-а при нехватке памяти:
- Guaranteed: у всех контейнеров
requests == limitsна оба ресурса. Eviction-ятся последними. - Burstable: есть
requests, ноlimitsлибо нет, либо они большеrequests. Средний приоритет. - BestEffort: ни requests, ни limits. Первые в очереди на eviction, когда ноде плохо.
Чтобы у продового приложения была защита от eviction-а, делают QoS
Guaranteed. Для batch-job-ов часто хватает Burstable.
Что часто ломается
- Pod в
PendingсоInsufficient cpu. Уменьшай request или добавь ноду. - Pod регулярно
OOMKilled. Подними memory limit или найди утечку. Никогда не «выключай OOM» через игнорирование лимита. - Ставят
limits.memory == requests.memoryбез запаса. Любой пик убивает pod. Делай limit чуть выше requests.
Связанные команды
# Что объявлено в 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 # проверить текущий шаг
Коротко
requests- число для шедулера,limits- потолок во время работы.- CPU считают в millicpu:
1000mэто одно ядро. Память вMi/Gi, без суффикса число читается как байты. - Шедулер сравнивает сумму requests на ноде с
Allocatable. Не влезло- Pod в
PendingсInsufficient cpu/memory.
- Pod в
- Превышение CPU limit даёт throttling, превышение memory limit даёт
OOMKilled. - Комбинация requests и limits определяет QoS-класс, а он - очередь на eviction при нехватке памяти.
Прошли материал до конца? Отметьте урок пройденным.
Прогресс сохранён в этом браузере
Почистишь историю или откроешь с телефона, и отметки пропадут. Оставь почту, они переедут в аккаунт. Пароль придумывать не нужно.
Готово. теперь в аккаунте: прогресс виден с любого устройства. Мой прогресс