Новая формула CPU weight в Kubernetes и cgroup v2

Предыстория

Kubernetes изначально проектировался с расчётом на cgroup v1, где значение CPU shares (приоритет процессора) для контейнера вычислялось из запрошенного количества процессорного времени по следующей формуле:

Заметим, что значение 1024 в этой формуле — это значение cpu.shares по умолчанию в cgroup v1, и оно никак не связано с милликорами. Например, контейнер, запрашивающий 1 CPU (1000m), получал cpu.shares = 1000 × 1024 / 1000 = 1024, а контейнер, запрашивающий 100m, — cpu.shares = 100 × 1024 / 1000 = 102.

Со временем cgroup v1 начали вытеснять cgroup v2. В cgroup v2 понятие CPU shares (диапазон значений: от 2 до 262144, то есть от 2¹ до 2¹⁸) было заменено понятием CPU weight (вес процессора; диапазон значений: от 1 до 10000, то есть от 10⁰ до 10⁴).

При переходе на cgroup v2 в рамках KEP-2254 была введена формула преобразования CPU shares из cgroup v1 в CPU weight для cgroup v2. Она выглядела так: cpu.weight = (1 + ((cpu.shares - 2) * 9999) / 262142)

Эта формула линейно отображает значения из диапазона [2¹, 2¹⁸] в диапазон [10⁰, 10⁴].

График линейного преобразования CPU shares в CPU weight

Подход прост, но линейное отображение порождает ряд серьёзных проблем, влияющих и на производительность, и на гранулярность конфигурирования.

Проблемы прежней формулы преобразования

Действующая формула преобразования создаёт две ключевые проблемы.

1. Снижение приоритета относительно рабочих нагрузок вне Kubernetes

В cgroup v1 значение CPU shares по умолчанию равно 1024, то есть контейнер, запрашивающий 1 CPU, имеет такой же приоритет, что и системные процессы за пределами Kubernetes. Однако в cgroup v2 значение CPU weight по умолчанию равно 100, а текущая формула переводит 1 CPU (1000m) лишь в ≈39 — это менее 40% от значения по умолчанию.

Пример:

  • Контейнер запрашивает 1 CPU (1000m)

  • cgroup v1: cpu.shares = 1024 (равно значению по умолчанию)

  • cgroup v2 (текущая формула): cpu.weight = 39 (значительно ниже значения по умолчанию 100)

Это означает, что после перехода на cgroup v2 рабочие нагрузки Kubernetes (или OCI) фактически теряют часть приоритета по процессору относительно процессов за пределами Kubernetes. Проблема может оказаться критической для инсталляций с большим числом системных демонов, работающих вне Kubernetes и рассчитывающих на то, что его рабочие нагрузки будут иметь приоритет, — особенно в условиях нехватки ресурсов.

2. Неприемлемая гранулярность

При небольших запросах CPU текущая формула выдаёт очень низкие значения, что не даёт создавать внутри контейнеров суб-cgroups для тонкого распределения ресурсов (в будущем это станет значительно проще — подробнее см. KEP #5474).

Пример:

  • Контейнер запрашивает 100m CPU

  • cgroup v1: cpu.shares = 102

  • cgroup v2 (текущая формула): cpu.weight = 4 (слишком мало для настройки суб-cgroup)

В cgroup v1 запрос 100m CPU, дававший 102 CPU shares, был вполне управляемым: внутри основного контейнера можно было создавать суб-cgroups и гибко распределять приоритеты процессора между группами процессов. В cgroup v2 же значение 4 практически невозможно распределить между суб-cgroups из-за недостаточной гранулярности.

С учётом планов поддержки доступных для записи cgroups в непривилегированных контейнерах этот вопрос становится ещё более актуальным.

Новая формула преобразования

Описание

Новая формула сложнее, но гораздо точнее отображает CPU shares cgroup v1 в CPU weight cgroup v2.

В её основе лежит квадратичная функция, проходящая через следующие опорные точки:

  • (2, 1): минимальные значения обоих диапазонов.

  • (1024, 100): значения по умолчанию для обоих диапазонов.

  • (262144, 10000): максимальные значения обоих диапазонов.

Визуально новая функция выглядит следующим образом:

График новой формулы преобразования CPU shares в CPU weight

А вот увеличенный фрагмент наиболее важной части:

Увеличенный график новой формулы преобразования в ключевой области

Новая формула «близка к линейной», однако тщательно спроектирована так, чтобы отображение диапазонов проходило ровно через три перечисленные выше опорные точки.

Как это решает выявленные проблемы

Восстановление приоритетов:

  • Контейнер, запрашивающий 1 CPU (1000m), теперь получит cpu.weight = 102. Это значение близко к стандартному значению cgroup v2, равному 100, и восстанавливает правильное соотношение приоритетов между рабочими нагрузками Kubernetes и системными процессами.

Улучшенная гранулярность:

  • Контейнер, запрашивающий 100m CPU, получит cpu.weight = 17 (см. пример). Это обеспечивает более точное распределение ресурсов внутри контейнеров.

Применение и интеграция

Изменение реализовано на уровне OCI, то есть не в самом Kubernetes. Поэтому переход на новую формулу преобразования целиком зависит от того, когда её поддержат OCI-среды выполнения (runtime).

Например:

  • runc: новая формула включена начиная с версии 1.3.2.

  • crun: новая формула включена начиная с версии 1.23.

Влияние на существующие развёртывания

Важно: часть пользователей может столкнуться с проблемами, если их инструменты опираются на прежнюю линейную формулу преобразования. Приложениям и системам мониторинга, которые самостоятельно вычисляют ожидаемые значения CPU weight на основе старой формулы, может потребоваться обновление. Это касается прежде всего следующих случаев:

  • Инструменты для управления ресурсами, прогнозирующие значения CPU weight.

  • Системы мониторинга, проверяющие или ожидающие конкретные значения weight.

  • Приложения, программно устанавливающие или проверяющие значения CPU weight.

Также учитывайте, что обратное преобразование cpu.weight в милликоры не всегда даёт исходное точное значение. Потеря точности возникает по двум причинам: перевод милликоров в cpu.shares сопровождается целочисленным усечением (например, 100m даёт 102 shares, а не 102,4), а само отображение shares в weight является отображением «многие к одному» (например, значения от 90m до 109m включительно — все отображаются в cpu.weight = 17). Инструментам, которым нужны точные значения запрошенных ресурсов CPU, следует читать их непосредственно из спецификации пода, а не вычислять из параметров cgroup.

Проект Kubernetes рекомендует протестировать новую формулу преобразования в непроизводственных средах перед обновлением OCI-рантаймов, чтобы убедиться в совместимости с существующими инструментами.

Где можно узнать больше?

Для тех, кто хочет глубже изучить это улучшение:

Как принять участие?

Если вас интересует работа над функциями уровня узла (node-level features) в Kubernetes, присоединяйтесь к Kubernetes Node Special Interest Group. Мы всегда рады новым участникам и свежим взглядам на задачи управления ресурсами.

© 2026 meganuke