Освоение запросов и лимитов ресурсов Kubernetes для максимальной производительности
Узнайте о ключевых различиях между запросами и лимитами ресурсов Kubernetes для CPU и памяти. Это руководство объясняет, как эти настройки определяют классы качества обслуживания (QoS) (Guaranteed, Burstable, BestEffort), предотвращают нестабильность узлов и оптимизируют эффективность планирования кластера. Включает практические примеры YAML и лучшие практики для настройки производительности.
Освоение запросов и лимитов ресурсов Kubernetes для максимальной производительности
Запросы и лимиты ресурсов Kubernetes выглядят просто в YAML, но они формируют почти все поведение кластера на второй день: где размещаются поды, какие рабочие нагрузки вытесняются первыми, получает ли приложение троттлинг CPU под нагрузкой и сколько свободной емкости, по вашему мнению, у вас есть. Неправильная настройка может сделать здоровое приложение неработоспособным. Отсутствующая настройка может заставить планировщик упаковывать поды на узел до тех пор, пока первый всплеск трафика не превратится в инцидент с шумным соседом.
Часть, которая сбивает с толку команды, заключается в том, что запросы и лимиты используются разными системами. Запросы — это в основном обещание планировщика. Лимиты — это граница принудительного применения. Относиться к ним как к одному и тому же приводит к странным результатам, особенно с CPU.
Понимание основных концепций: запросы против лимитов
В Kubernetes контейнер может определить ожидаемое потребление ресурсов с помощью resources.requests и resources.limits. Технически они не являются обязательными, если только в вашем кластере не используются политики, такие как LimitRange или средства контроля допуска, но производственные рабочие нагрузки обычно должны определять как минимум запросы для CPU и памяти. Без запросов у планировщика мало полезной информации, и под попадает в более слабую позицию качества обслуживания.
1. Запросы ресурсов (requests)
Запросы представляют собой количество ресурсов, которое контейнер гарантированно получит при планировании. Это минимальное количество ресурсов, которое kube-scheduler использует при принятии решения о размещении пода на узле.
- Планирование: Узел должен иметь достаточное количество доступных распределяемых ресурсов, удовлетворяющих сумме всех запросов подов, прежде чем на него можно будет запланировать новый под.
- Приоритет выполнения: Запросы влияют на доли CPU и решения о вытеснении памяти. Это не магическая резервация, предотвращающая любое замедление, но они дают Kubernetes и ядру лучшую информацию во время конкуренции.
2. Лимиты ресурсов (limits)
Лимиты определяют максимальное количество ресурсов, которое контейнер может потреблять. Превышение этих лимитов приводит к определенным, заданным поведениям для CPU и памяти.
- Лимиты CPU: Если контейнер пытается использовать больше CPU, чем его лимит, cgroups ядра Linux будут троттлить его использование, предотвращая потребление дальнейших циклов.
- Лимиты памяти: Если контейнер превышает свой лимит памяти, ядро может завершить процесс в контейнере. Kubernetes сообщает об этом как
OOMKilled, когда это является зарегистрированной причиной завершения.
Поведение CPU против памяти
Крайне важно понимать качественную разницу в том, как Kubernetes обеспечивает соблюдение границ CPU и памяти:
| Ресурс | Поведение при превышении лимита | Механизм принуждения |
|---|---|---|
| CPU | Троттлинг (замедление) | cgroups (управление пропускной способностью CPU) |
| Память | Завершение (OOMKill) | Убийца OOM ядра |
Практическое предостережение: Лимиты CPU могут защитить узел от вышедшего из-под контроля процесса, но они также могут создавать проблемы с задержкой, если установлены слишком низко. Многие команды платформы последовательно устанавливают лимиты памяти и более избирательно подходят к лимитам CPU для сервисов, чувствительных к задержке, в зависимости от их толерантности к риску и политики кластера.
Определение ресурсов в спецификациях подов
Ресурсы определяются в блоке spec.containers[*].resources. Количества указываются с использованием стандартных суффиксов Kubernetes (например, m для милли-CPU, Mi для мебибайт).
Определения единиц CPU
1единица CPU равна 1 полному ядру (или vCPU у облачных провайдеров).1000m(миллиядро) равно 1 единице CPU.
Определения единиц памяти
Mi(Мебибайты) илиGi(Гибибайты) являются распространенными.1024Mi=1Gi.
Пример конфигурации YAML
Рассмотрим контейнер, которому требуется гарантированный минимум 500m CPU и 256Mi памяти, но который никогда не должен превышать 1 CPU и 512Mi:
resources:
requests:
memory: "256Mi"
cpu: "500m"
limits:
memory: "512Mi"
cpu: "1"
Числа должны основываться на наблюдаемом поведении, а не на догадках. Для небольшого HTTP API начальный запрос может основываться на нормальном использовании p50 или p90 во время делового трафика, а затем корректироваться после нагрузочного тестирования. Для JVM-сервиса память должна включать кучу, метапространство, нативную память, стеки потоков, прямые буферы и накладные расходы сайдкара. Для пакетного задания пиковая память во время обработки самого большого входного файла может быть важнее средней памяти.
Классы качества обслуживания (QoS)
Соотношение между запросами и лимитами определяет класс качества обслуживания (QoS), назначаемый поду. Этот класс определяет приоритет пода, когда ресурсы становятся дефицитными и узлу необходимо освободить память (вытеснение).
Kubernetes определяет три класса QoS:
1. Guaranteed (Гарантированный)
Определение: Все контейнеры в поде должны иметь идентичные, ненулевые запросы и лимиты для обоих ресурсов CPU и памяти.
- Преимущество: Эти поды вытесняются в последнюю очередь при нехватке ресурсов, обеспечивая максимальную стабильность.
- Вариант использования: Критические системные компоненты или базы данных, требующие строгой изоляции производительности.
2. Burstable (Пульсирующий)
Определение: По крайней мере один контейнер в поде имеет определенные запросы, но либо запросы и лимиты не равны для всех контейнеров, либо некоторые ресурсы не ограничены (хотя установка лимитов настоятельно рекомендуется).
- Преимущество: Позволяет контейнерам выходить за пределы своих запросов, используя неиспользованную емкость узла, вплоть до определенных лимитов.
- Приоритет вытеснения: Вытесняются перед подами BestEffort, но после подов Guaranteed.
- Вариант использования: Большинство стандартных приложений без сохранения состояния, где допустимы небольшие колебания задержки.
3. BestEffort (Максимальное усилие)
Определение: Под не имеет определенных запросов или лимитов ни для одного контейнера.
- Преимущество: Нет, кроме простоты.
- Риск: Эти поды являются первыми кандидатами на вытеснение, когда узел испытывает нехватку памяти. Они также могут плохо конкурировать за CPU, поскольку не было объявлено ни одного запроса.
- Вариант использования: Некритичные пакетные задания или агенты ведения журнала, которые можно легко перезапустить.
Практические стратегии оптимизации
Эффективное управление ресурсами требует измерения, итераций и тщательного планирования.
Стратегия 1: Измеряйте и устанавливайте запросы точно
Запросы должны отражать количество ресурсов, необходимое приложению для приемлемой работы большую часть времени. Если вы установите запросы слишком высоко, вы тратите впустую емкость кластера, потому что планировщик считает эту емкость уже занятой. Если вы установите их слишком низко, планировщик может разместить слишком много подов на узле, и рабочая нагрузка может с большей вероятностью пострадать во время конкуренции.
Используйте инструменты мониторинга, такие как Prometheus и Grafana, чтобы сравнивать значения запросов с реальным использованием. Распространенная отправная точка — посмотреть на несколько дней нормального трафика, игнорировать очевидные разовые инциденты и установить запросы около устойчивого процентиля, а не самого высокого единичного пика. Точный процентиль — это политический выбор; главное — использовать данные и пересматривать их.
Например, если сервис обычно использует 180m CPU, достигает пика около 450m во время прогрева развертывания и имеет редкие скачки до 900m во время известной пакетной задачи, установка запроса 900m может тратить емкость весь день. Установка запроса 50m может сделать под дешевым для планирования, но нестабильным при конкуренции. Запрос в диапазоне нормального устойчивого использования плюс отдельная обработка для пакетного пути часто являются лучшим решением.
Стратегия 2: Определите консервативные лимиты
Лимиты действуют как граница безопасности, но они не бесплатны. Для памяти лимит, немного превышающий измеренное пиковое использование, может предотвратить потребление одним контейнером всего узла. Для CPU лимит предотвращает использование неограниченного CPU вышедшим из-под контроля процессом, но агрессивные лимиты могут троттлить сервис, даже если на узле есть простаивающие ядра.
Предупреждение о лимитах CPU: Установка лимитов CPU ниже фактического спроса может вызвать заметную задержку из-за троттлинга. QoS Burstable является разумным выбором для многих сервисов без сохранения состояния, в то время как QoS Guaranteed лучше подходит для рабочих нагрузок, где компромисс изоляции является преднамеренным.
Стратегия 3: Использование вертикального автомасштабирования подов (VPA)
Ручная настройка ресурсов сложна и отнимает много времени. Вертикальный автомасштабировщик подов (VPA) отслеживает использование во время выполнения и может рекомендовать или обновлять запросы ресурсов в зависимости от своего режима. Во многих настройках VPA сначала используется в режиме рекомендаций, чтобы команды могли просмотреть предлагаемые запросы перед разрешением автоматических обновлений.
Будьте осторожны при сочетании VPA с горизонтальным автомасштабированием подов (HPA). HPA часто масштабируется на основе использования относительно запросов, поэтому изменение запросов может изменить поведение масштабирования. Это может работать хорошо, но должно быть тщательно протестировано.
Стратегия 4: Квоты ресурсов для пространств имен
Чтобы предотвратить захват ресурсов между командами или средами, администраторы должны использовать квоты ресурсов на уровне пространства имен. ResourceQuota накладывает агрегированные ограничения на общее количество запросов/лимитов CPU и памяти, которые могут существовать в этом пространстве имен, обеспечивая справедливость.
Пример квоты пространства имен
apiVersion: v1
kind: ResourceQuota
metadata:
name: compute-quota
namespace: development
spec:
hard:
requests.cpu: "10"
limits.memory: "20Gi"
Это гарантирует, что общий запрошенный CPU во всех подах в пространстве имен development не может превышать 10 ядер, а общие лимиты памяти не могут превышать 20Gi.
Как неправильные настройки проявляются в реальных кластерах
Ошибки с ресурсами редко объявляют себя как «ошибки с ресурсами». Обычно они выглядят как инциденты приложений.
Если лимиты CPU слишком низкие, вы можете увидеть высокую задержку запросов, в то время как графики использования CPU выглядят ограниченными. Контейнер хочет больше CPU, но троттлинг cgroups сдерживает его. В настройках на основе Prometheus такие метрики, как container_cpu_cfs_throttled_periods_total и container_cpu_cfs_periods_total, могут помочь показать, является ли троттлинг частью истории.
Если лимиты памяти слишком низкие, под может перезапуститься с Reason: OOMKilled. Журналы приложения могут внезапно обрываться, потому что процесс не получил корректного завершения. kubectl describe pod обычно говорит правду быстрее, чем журнал приложения в этом случае.
Если запросы слишком высоки, поды могут находиться в состоянии Pending, даже если панели мониторинга показывают низкое среднее использование узла. Планировщик не размещает поды на основе среднего фактического использования; он сравнивает запрошенные ресурсы с распределяемыми ресурсами. Вот почему кластер может выглядеть недоиспользованным и все равно отказывать новому поду.
Если запросы отсутствуют, рабочая нагрузка может выглядеть нормально в спокойные периоды, а затем стать первой, кто пострадает, когда узел занят. Это может быть приемлемо для одноразовых заданий, но это плохое значение по умолчанию для сервисов, ориентированных на пользователя.
Более безопасный рабочий процесс настройки
Практический цикл настройки выглядит следующим образом:
- Начните с явных запросов CPU и памяти для каждого производственного контейнера, включая сайдкары.
- Установите лимиты памяти на основе наблюдаемого пика плюс запас, затем следите за перезапусками OOMKilled.
- Решите, требуются ли лимиты CPU по политике или риску рабочей нагрузки. Если вы их используете, отслеживайте троттлинг.
- Сравнивайте запрошенные CPU и память с фактическим использованием еженедельно или ежемесячно, особенно после крупных релизов.
- Относитесь к оптимизации размеров как к управлению изменениями. Более низкий запрос может увеличить плотность упаковки, но также может изменить поведение при сбоях во время нагрузки на узел.
Сайдкары заслуживают внимания. Прокси сервисной сетки, сборщик журналов или агент безопасности могут потреблять достаточно CPU или памяти, чтобы изменить реальный след пода. Если настраивается только основной контейнер приложения, под все равно может быть неправильно представлен планировщику.
Пример: Исправление всплеска задержки, вызванного троттлингом CPU
Представьте контейнер API с такой конфигурацией:
resources:
requests:
cpu: "100m"
memory: "256Mi"
limits:
cpu: "200m"
memory: "512Mi"
Во время распродажи задержка возрастает. На узле все еще есть простаивающий CPU, но контейнер ограничен 200m. Увеличение реплик может помочь, но каждый под по-прежнему индивидуально троттлится. Лучшим исправлением может быть увеличение или удаление лимита CPU, увеличение запроса в соответствии с нормальным устойчивым спросом и использование HPA, чтобы сервис масштабировался до того, как задержка станет ужасной.
Важный урок заключается в том, что низкое использование CPU на графике не всегда означает, что приложение простаивает. Это может означать, что приложению не разрешено использовать больше.
Финальная проверка
Запросы сообщают Kubernetes, как размещать и расставлять приоритеты пода. Лимиты сообщают ядру, где его остановить. Хорошие значения основаны на реальных метриках, нагрузочных тестах и четком решении о том, что важнее для каждой рабочей нагрузки: плотность, изоляция, задержка или стоимость. Пересматривайте значения после изменений трафика, изменений зависимостей и обновлений среды выполнения. Устаревшие настройки ресурсов — один из самых тихих способов, которыми кластер дрейфует к плохой производительности.