Вся соль Kubernetes в том, что это — зверь. А управление этим зверем, особенно когда речь идет о критичных к производительности нагрузках — представьте себе фермы для обучения ML, системы сверхнизкой задержки для трейдинга или базы данных, которые буквально дышат — всегда было тонким танцем. Хочется предсказуемой производительности, что часто означает выделение эксклюзивных, выровненных по NUMA ресурсов для вашего главного события. Но pod’ы — это уже не просто одинокие контейнеры. Это целые экосистемы. В них есть сайдкары для логирования, мониторинга, service mesh, приема данных — весь набор.
Исторически, чтобы получить эти чистые, эксклюзивные ресурсы для основного приложения, приходилось идти ва-банк. Каждый контейнер в pod’е получал свой выделенный, целочисленный кусок CPU. Расточительно? Безусловно. Особенно для крошечного экспортера метрик, который едва ли потребляет процессорное время. Если же вы отказывались от такого строгого выделения, вы теряли драгоценный класс QoS (Quality of Service) Guaranteed для pod’а, а вместе с ним — всякую надежду на стабильную, первоклассную производительность. Настоящий выбор Золушки для тех, кто запускает требовательные приложения.
Новая надежда: появляются менеджеры ресурсов на уровне Pod’а
Kubernetes v1.36, однако, сдвигает иглу с альфа-введением Pod-Level Resource Managers. Это не просто косметическое изменение; это фундаментальный архитектурный сдвиг, расширяющий возможности менеджеров топологии, CPU и памяти kubelet. Главная новость? Теперь они поддерживают спецификации ресурсов на уровне pod’а прямо в .spec.resources. Мы переходим от строго модели выделения на контейнер к явно pod-ориентированной.
Включение этих новых feature gates (PodLevelResourceManagers и PodLevelResources) позволяет kubelet оркестрировать гибридные модели выделения ресурсов. Это означает, что вы наконец-то можете получить NUMA-выравнивание и эксклюзивное выделение ресурсов для вашего звездного исполнителя, не тратя процессорные ядра попусту на его менее требовательных компаньонов. Гибкость встречается с эффективностью, наконец-то.
Сценарии из реальной жизни: где шина встречается с NUMA
Прелесть этого нового подхода раскрывается в практических сценариях использования, сильно зависящих от области применения Topology Manager. Возьмем pod с базой данных, чувствительной к задержкам, включая основной контейнер, локальный экспортер метрик и сайдкар-агент резервного копирования.
Если вы настраиваете Topology Manager с областью видимости pod, kubelet выполняет единое NUMA-выравнивание на основе всего пула ресурсов pod’а. Критический контейнер базы данных получает свои эксклюзивные куски CPU и памяти из этого конкретного NUMA-узла. Что остается? Это формирует общий пул pod’а. Здесь обитают ваш экспортер метрик и агент резервного копирования. Они делят ресурсы между собой, да, но, что критически важно, они изолированы от выделенных кусков базы данных и остальной части узла. Это огромный плюс: вы можете размещать вспомогательные контейнеры на том же NUMA-узле, не тратя выделенные ядра.
При настройке с областью видимости Topology Manager
podkubelet выполняет единое NUMA-выравнивание на основе общего бюджета pod’а. Контейнер базы данных получает эксклюзивные куски CPU и памяти из этого NUMA-узла. Оставшиеся ресурсы из бюджета pod’а формируют новый общий пул pod’а.
Вот как это может выглядеть в YAML:
apiVersion: v1
kind: Pod
metadata:
name: tightly-coupled-database
spec:
# Ресурсы на уровне Pod'а устанавливают общий бюджет и размер NUMA-выравнивания.
resources:
requests:
cpu: "8"
memory: "16Gi"
limits:
cpu: "8"
memory: "16Gi"
initContainers:
- name: metrics-exporter
image: metrics-exporter:v1
restartPolicy: Always
- name: backup-agent
image: backup-agent:v1
restartPolicy: Always
containers:
- name: database
image: database:v1
# Этот контейнер Guaranteed получает эксклюзивный кусок в 6 CPU из бюджета pod'а.
# Оставшиеся 2 CPU и 4Gi памяти формируют общий пул pod'а для сайдкаров.
resources:
requests:
cpu: "6"
memory: "12Gi"
limits:
cpu: "6"
memory: "12Gi"
Альтернативно, рассмотрим ML-рабочую нагрузку с инфраструктурными сайдкарами. Здесь, скорее всего, вы будете использовать область видимости Topology Manager container. Kubelet оценивает каждый контейнер отдельно. ML-контейнер для обучения, жаждущий максимальной производительности, получает свои эксклюзивные, NUMA-выровненные CPU и память. Сайдкар service mesh? Ему не нужно такое специализированное лечение; он счастливо работает в общем пуле узла. Общее потребление ресурсов по-прежнему ограничено общими лимитами pod’а, но вы разумно применяете эти эксклюзивные, NUMA-выровненные ресурсы только там, где они действительно необходимы.
apiVersion: v1
kind: Pod
metadata:
name: ml-workload
spec:
# Ресурсы на уровне Pod'а устанавливают общее ограничение бюджета.
resources:
requests:
cpu: "4"
memory: "8Gi"
limits:
cpu: "4"
memory: "8Gi"
initContainers:
- name: service-mesh-sidecar
image: service-mesh:v1
restartPolicy: Always
containers:
- name: ml-training
image: ml-training:v1
# При области видимости 'container' этот контейнер Guaranteed получает эксклюзивные,
# NUMA-выровненные ресурсы, в то время как сайдкар работает в общем пуле узла.
resources:
requests:
cpu: "3"
memory: "6Gi"
limits:
cpu: "3"
memory: "6Gi"
Квоты CPU и искусство изоляции
Изоляция — ключ к успеху, и она обрабатывается по-разному для этих смешанных рабочих нагрузок. Для контейнеров, получающих эксклюзивные куски CPU, принудительное применение квот CFS (Completely Fair Scheduler) CPU отключается на уровне контейнера. Это означает, что они работают без ограничений, обычно налагаемых планировщиком CFS ядра, гарантируя, что они могут работать без помех.
Для контейнеров, которые не получают эксклюзивных кусков CPU, они становятся частью общего пула — либо общего пула pod’а, либо общего пула всего узла. К этим контейнерам применяется принудительное применение квот CFS. Они делят ресурсы в соответствии с политикой планирования CFS. Это тонкая система, разработанная для предотвращения того, чтобы вышедшие из-под контроля процессы голодали жизненно важные, при этом предоставляя выделенные полосы для приложений с высокими ставками.
Скрытый сдвиг: от контейнеров к Pod’ам
Этот переход к управлению ресурсами на уровне pod’а — больше, чем просто оптимизация; это философский сдвиг внутри Kubernetes. Годами основной единицей планирования и выделения ресурсов был контейнер. Эта новая функция признает, что современные приложения часто распределены внутри pod’а, и управление этими распределенными компонентами как единым целым имеет решающее значение для производительности и эффективности. Это попытка относиться к pod’у меньше как к набору независимых процессов, а больше как к единому, хотя и сложному, экземпляру приложения.
Это открывает интересные возможности для того, как мы проектируем и развертываем высокопроизводительные приложения. Вместо того чтобы тщательно рассчитывать потребности отдельных контейнеров в ресурсах и бороться с последствиями QoS, мы теперь можем определить общий профиль ресурсов pod’а, а затем интеллектуально делегировать в рамках этого бюджета. Это упрощает операции и, что более важно, позволяет более плотно использовать ресурсы без снижения производительности. Это победа для FinOps-команд и для инженеров, стремящихся достичь этих неуловимых метрик производительности.
Что это значит для разработчиков и операторов
Для разработчиков это более интуитивный способ определения требований к ресурсам для сложных pod’ов. Вы можете четко разграничить свои критически важные компоненты и их вспомогательный состав. Для операторов это означает более эффективное использование узлов и потенциально лучшие сбережения, поскольку вы больше не будете избыточно выделять ресурсы для сайдкаров только для поддержания QoS основного приложения. Это значительный шаг к тому, чтобы сделать Kubernetes более жизнеспособной платформой для самых требовательных рабочих нагрузок.
Конечно, это альфа. Ожидайте шероховатостей, изменений и, естественно, больше вопросов по мере созревания. Но направление ясно: Kubernetes становится умнее в плане распределения конечных ресурсов аппаратного обеспечения, и это развитие стоит того, чтобы за ним следить.