Scheduling vSphere Pods
Это перевод статьи Frank Denneman Scheduling vSphere Pods. Публикуется с разрешения автора.
В предыдущей статье “Первоначальное размещение vSphere Pod” было рассмотрено внутреннее устройство vSphere Pod, чтобы показать, что он представляет собой комбинацию специализированной виртуальной машины и группы контейнеров. Платформы Kubernetes и vSphere содержат богатый набор элементов управления, политик и функций управления ресурсами, которые контролируют и распределяют ресурсы между рабочими нагрузками. Обе системы управления используют похожие названия подсистем и это вызывает ложное ощущение унификации с точки зрения управления vSphere Pod. В этой серии статей рассматривается наложение различных подсистем управления и то, как новая платформа vSphere 7 позволяет разработчикам использовать знакомые средства управления Kubernetes, в то время как администраторам vSphere — продолжать использовать знакомые средства управления vSphere.
Workload Deployment Process
Обе Control plane реализуют похожий процесс для развертывания рабочих нагрузок, планировщик должен запустить рабочую нагрузку на источнике вычислительных ресурсов (Worker node или ESXi хост), Control plane выбирает место запуска на основе критериев, представленных заказчиком ресурсов (Pod манифест или конфигурация ВМ). Control plane проверяет, кто из Worker node или ESXi хостов имеет достаточно ресурсов, и если ресурсов достаточно, то соответствует ли он критериям, перечисленным в Pod манифесте или конфигурации ВМ. Инструкция отправляется исполнителю рабочей нагрузки, чтобы инициировать процесс включения рабочей нагрузки.
Разница между процессами развертывания контейнеров и виртуальных машин заключается в их размере. Виртуальная машина определяется ее виртуальным оборудованием, и эта конфигурация действует как граница для гостевой ОС и ее процессов (следовательно, виртуальная машина изолирована и не может выйти за пределы, выделенных ресурсов). Контейнер является контролируемым процессом и использует служебную ОС для управления распределением ресурсов, для этого процесса не может быть задана прямая аппаратная конфигурация. И это различие создает несколько интересных проблем, когда вы хотите запустить контейнер на гипервизоре. Гипервизору требуются понимание рабочих нагрузок для определения их аппаратной конфигурации. Чтобы его планировщик мог управлять рабочими нагрузками и управлять распределением ресурсов между ними. Как оценить рабочую нагрузку, которая не дает вам абсолютно никаких намеков на планируемое потребление ресурсов? Вы можете выбрать произвольную конфигурацию оборудования, но тогда вы можете поломать намерение разработчика, если он или она хочет, чтобы приложение могло “взорваться” и временно использовать больше ресурсов, чем это необходимо обычно. Вы же не хотите диктовать новую парадигму управления ресурсами разработчикам, чтобы им пришлось изменить свои текущие методы развертывания рабочих нагрузок, вы же хотите иметь возможность принимать новые рабочие нагрузки с наименьшим количеством ручного труда. Совместная работа двух Control plane — это не только процесс решения всех этих проблем, но еще и возможность улучшить пользовательский опыт потребителей ресурсов. В этой статье рассматривается различие в поведении планировщиков ресурсов и то, как Request, Limits и политики QoS Kubernetes влияют на размер vSphere Pod. Она начинается с представления конструкций Kubernetes и планировщика Kubernetes для предоставления достаточного количества вводной информации, чтобы потом разобраться, как это все влияет на размер vSphere Pod и его размещение.
Управление вычислительными ресурсами для контейнеров
В Kubernetes вы можете указать, сколько ресурсов может потреблять контейнер (Limits) и сколько Worker node должен выделять контейнеру (Request). Это похоже на Reservation и Limit в vSphere. Подобно vSphere, Kubernetes выбирает Worker node (так в Kubernetes называется хост, который будет выполнять рабочую нагрузку), основываясь на Request (Reservation) контейнера. В vSphere атомарным элементом для назначения Reservation, Shares и Limit является виртуальная машина, а в Kubernetes — контейнер. Звучит это просто, но есть небольшая загвоздка.
Контейнер не разворачивается непосредственно на Worker node Kubernetes, он заключен в более высокоуровневую конструкцию называемую Pod. Причиной существования Pod является соображение, что каждый отдельный процесс должен запускаться в своем контейнере. А если приложение состоит из нескольких процессов, то было бы здорово если бы существовала бы группа контейнеров, вы же не хотите управлять процессами независимо, а хотите управлять самим приложением целиком, отсюда и вытекает существование Pod. Так и в чем же подвох? Несмотря на то, что вы развертываете Pod, вы должны задать необходимые ресурсы для каждого контейнера отдельно, а не для всего Pod. Поскольку Pod является атомарной единицей для планировщика, Request всех контейнеров внутри Pod суммируются, и результат используется для выбора Worker node. После размещения Pod планировщик ресурсов Worker node должен уже позаботиться о каждом Request контейнера и ограничить его индивидуально. Но это тема для будущей статьи. Давайте внимательнее посмотрим на Pod манифест.
Сайзинг контейнера versus сайзинг виртуальной машины
В примере Pod манифест содержит описание двух контейнеров, для каждого из которых заданы Requests и Limits как для CPU, так и для памяти. Ограничения по процессору могут быть заданы несколькими различными способами. 1 соответствует 1 процессору, что совпадает с одним hyperthread процессора Intel. Если вам кажется, что это слишком много, вы можете использовать меньшие единицы измерения, millicpu или десятичные дроби. Таким образом, 0.5 означает половину hyperthread или 500 millicpu. Для опытного администратора vSphere это может выглядеть странно, вместо того, чтобы иметь дело с пользователями, которые требуют 64 ядра, мы пытаемся разделить атомы. С памятью проще, вы можете задать требования к памяти в виде простых целых чисел (126754378954) или целых чисел с фиксированной запятой, используя суффиксы 64MiB (2²⁶ bytes). Документация Kubernetes поможет вам в этом разобраться лучше. В примере Pod Request задан в 128 Mi памяти и 500 m процессорных ресурсов.
Планировщик Kubernetes
В процессе первоначального размещения контейнера планировщик должен сначала проверить “совместимость”. После того, как планировщик Kubernetes проверил совместимость, правила Affinity и Anti-affinity, он проверяет доступные ресурсы хоста, чтобы понять, может ли он удовлетворить запросы Pod (128 Mi и 500 m CPU). Если точнее, он проверяет “node allocatable”. Это количество ресурсов, доступных для использования Pod. Дополнительно Kubernetes резервирует некоторые ресурсы хоста, чтобы гарантировать, что системные демоны смогут работать без риска нехватки ресурсов. Доступные у хоста ресурсы делятся на две части, распределенные и нераспределенные (allocated и unallocated). Общее количество выделенных ресурсов является суммой всех Request всех активных контейнеров на Worker node. В результате Kubernetes сопоставляет Request, указанный в Pod манифесте, и нераспределенные ресурсы, доступные на каждой Worker node в кластере. Хост с наибольшим количеством нераспределенных ресурсов будет выбран для запуска Pod. Уточню, первоначальное размещение Kubernetes не учитывает фактическое потребление ресурсов.
Как показано на диаграмме, планировщик должен разместить нагрузку на хосте B. Kubernetes Control plane проверяет все Worker node, выбирает хосты, подходящие для запуска Pod, а затем выбирает хост на основе сконфигурированной prioritization function. Наиболее часто используемой является опция “LeastRequestedPriority”, которая отдает предпочтение Worker node с меньшим количеством уже запрошенных ресурсов. Поскольку Worker node B имеет наименьшее количество зарезервированных ресурсов, планировщик считает этот узел наилучшим кандидатом для выполнения рабочей нагрузки.
Планировщик vSphere
DRS имеет более сложную модель планирования ресурсов. Метод, используемый Kubernetes, более или менее соответствует модели vSphere Admission control. Планировщик Kubernetes содержит больше нюансов и функций, чем я только что описал в параграфах выше. Он проверяет, сообщает ли хост о нехватке памяти, позволяя исключить его из списка доступных хостов (CheckNodeMemoryPressure) и prioritization function пока в бета-версии, и общий взгляд только на зарезервированную и незарезервированную память можно считать немного грубым. vSphere имеет три Admission control, которые работают вместе для обеспечения непрерывности и доступности ресурсов. Планировщик DRS соизмеряет доступность ресурсов хоста с запросами на ресурсы рабочих нагрузок. Reservation, Shares и Limit и фактическое потребление ресурсов рабочей нагрузкой используется для выбора соответствующего хоста. Тут вы можете поспорить, рабочая нагрузка, которая должна быть размещена, еще не использует никаких ресурсов, так как же это работает?
Во время первоначального размещения DRS рассматривает сконфигурированный размер как фактическое потребление, что является наихудшим сценарием. Таким образом, виртуальная машина с 4 vCPU и 16 GB запрашивает до еще включения 4 vCPU и 16 GB плюс накладные расходы на работу виртуальной машины (VM overhead). Тем не менее, если Reservation 8 ГБ, то запрашивается 4 vCPU, 8 GB + VM overhead. На хосте должно быть не менее 8 GB (+ VM overhead) свободных ресурсов доступных для размещения рабочей нагрузки. Чем это отличается от Kubernetes? Ключевое — это принятие во внимание будущего состояния рабочей нагрузки. DRS понимает фактическое потребление ресурсов (и запрошенное) всех других активных рабочих нагрузок, работающих на разных хостах. И, таким образом, DRS имеет точное представление о способности (и возможности) рабочей нагрузки выполнять на каждом хосте. Запрос ресурсов указывает количество ресурсов, которые рабочая нагрузка имеет право использовать, но DRS также может и прогнозировать потребление рабочей нагрузки. Вы бы предпочли разместить новую нагрузку на “забито” рабочими нагрузками хосте или на менее загруженном?
В примере ниже имется три хоста, каждый с 100 GB памяти. На хосте A активна рабочая нагрузка, которая зарезервировала 60 GB памяти. 40 GB памяти не зарезервировано. Хост B и хост C имеют активные рабочие нагрузки, которые зарезервировали 40 GB памяти. 60 GB памяти не зарезервированы. Появляется новая рабочая нагрузка с зарезервированными 35 GB. Планировщик Kubernetes решил бы, что оба хоста одинаково хороши для размещения нагрузки. Однако DRS знает об реальном использовании ресурсов. На хосте B Active Usage 70 GB, а на хосте C — 45 GB. Поскольку использование ресурса хоста B приближается к его полной емкости, DRS выбирает хост C в качестве хоста назначения для первоначального размещения.
Учитывая активное использование ресурсов другими активными потребителями, будь то ВМ или контейнерами в vSphere Pod, получается создать платформу, лучше удовлетворяющую запросы в ресурсах для всех рабочих нагрузок. Если Pod сконфигурирован с Quality of Service, допускающим рост потребления ресурсов (Limit превышает Request), разработчик заявляет, что рабочая нагрузка должна быть в состоянии потреблять больше ресурсов, если доступно. При таком более интеллектуальном начальном размещении (учитывающем реальное потребление), вероятность того, что эта возможность будет реализована значительно увеличивается.
Управление вычислительными ресурсами для vSphere Pod
vSphere Pod представляет собой комбинацию контейнеров и ВМ, обе Control plane взаимодействуют с различными компонентами vSphere Pod. И понятно, что параметры контейнеров влияют на параметры ВМ, например, на их размер. Размер ВМ определяется аппаратной конфигурацией. По сути, виртуальная машина представляет собой виртуализированное оборудование, и планировщик должен понимать границы виртуальной машины, чтобы правильно размещать ее и управлять ей. Эта конфигурация действует как граница, в пределах которой живет гостевая ОС. Гостевая ОС не может смотреть за пределы ВМ. Для ВМ не существует понятия “outer-space”, а для контейнеров существует. Контейнер не является жесткой или ограниченной конструкцией. Это группа процессов, которые изолированы от остальных и заперты для “сдерживания” потребления ресурсов с помощью control groups. Можете сравнить это с процессом или приложением на компьютере с Windows. Когда вы запускаете или настраиваете приложение, вы не определяете, сколько процессоров или памяти может потреблять это конкретное приложение. Вы надеетесь, что оно не будет вести себя как Галактус (более известный как Google Chrome) и что оно просто не израсходует все ваши ресурсы. Это означает, что процесс в Windows может видеть все ресурсы, которые содержит хост (например, ноутбук или виртуальная машина). То же самое относится и к контейнерам Linux. Контейнер может видеть все ресурсы, доступные для его хоста, но настройка Limits ограничивает его потребление. А это означает, что если ограничение не установлено, контейнер должен иметь возможность потреблять столько, сколько может предоставить Worker node. Таким образом размер контейнера будет равен размеру Worker node. Если этот контейнер должен работать внутри vSphere Pod, то vSphere Pod должен иметь размер целого ESXi хоста. Хотя мы все любим наших monster-VM, мы не должны так делать. Особенно, когда потребление ресурсов большинства контейнеров граничат с расщеплением атома и не требуют Worker node размером с планету.
Классы QoS Kubernetes и их влияние на размер vSphere Pod
Очень интересным поведением Kubernetes является неявное определение классов качества обслуживания (QoS) исходя из сочетания Limits и Request в Pod манифесте. Как видно из введения в эту статью, Pod манифест содержит Limits и Request для каждого контейнера. Тем не менее, эти параметры не являются обязательными. Основываясь на сочетании Limits и Request, Kubernetes автоматически назначает класс QoS контейнерам внутри модуля. А класс Qos определяет поведение процесса высвобождения ресурсов. Разработчик, хорошо разбирающийся в диалекте Kubernetes, понимает это поведение и соответствующим образом настраивает Pod манифест. Давайте рассмотрим три класса QoS, чтобы лучше понять разработчиков.
Существует три класса QoS, класс BestEffort, класс Burstable и класс Guaranteed. Если никакие Limits и Request не установлены для всех контейнеров в Pod манифесте назначается класс BestEffort. Это означает, что все контейнеры в этом Pod могут потреблять столько ресурсов, сколько они хотят, но они также являются первыми претендентами, которые будут “выселены”, если произойдет нехватка ресурсов. Если все контейнеры в Pod манифесте содержат Request и для CPU и для памяти и этот Request равен Limits, то Kubernetes назначит этому Pod Guaranteed класс QoS. Guaranteed pod — это последние кандидаты, которые будут “ущемлены” в ресурсах в случае нехватки ресурсов. Любая другая мыслимая комбинация Request и Limits процессора и памяти гарантирует, что Kubernetes назначит класс Burstable. Важно не нарушать ожидаемое поведение выдачи и возврата ресурсов, Request и Limits, указанные в Pod манифесте, используются в для определения размера vSphere Pod чтобы обеспечить ожидаемое поведение.
Если для контейнера не установлен Limits, как vSphere должна это интерпретировать чтобы определить размера vSphere Pod? Для предотвращения создания vSphere Pod размером в целый хост введен размер контейнера по умолчанию. Который устанавливается на каждый контейнер. Если быть точным, если создать самый простой Pod с одним контейнером и без параметров Request /Limits , то vSphere Pod получит 1 vCPU и 512 MB. По умолчанию контейнер получает 0.5 ядра, но если в Pod только один контейнер, то мы округляем до 1 vCPU. Почему размер выбирается не на основе Pod? Просто из соображений масштабируемости — размер Pod может увеличиваться в зависимости от количества BestEffort контейнеров внутри. Если задан Request или Limits, превышающее размер по умолчанию, этот показатель используется для определения размера контейнера. Если Pod манифест содержит несколько контейнеров, добавляется наибольшая из метрик каждого контейнера, и результат сложения используется в качестве размера vSphere Pod. Например, Pod содержит два контейнера, каждый с Request и Limits, превышающими размер контейнера по умолчанию. CPU Limits превышает размер CPU Request, в результате vSphere использует сумму обоих CPU Limits и добавляет небольшие запасы для компонентов, отвечающих за жизненный цикл Pod, конфигурацию Pod и взаимодействие vSpherelet. Аналогичный расчет будет сделан и для памяти.
Первоначальное размещение контейнеров в vSphere Pod на vSphere с Kubernetes
Когда разработчик отправляет Pod манифест в Kubernetes Control plane, Kube-Scheduler должен найти соответствующий Worker node. На диалекте Kubernetes Worker node, который отвечает требованиям ресурсов Pod, называется Feasible node. Чтобы определить, какие хосты являются Feasible node, Kube-Scheduler отфильтрует хосты, у которых недостаточно нераспределенных ресурсов, необходимых для удовлетворения запроса ресурсов описанного в Pod манифесте. Вторым шагом в процессе работы Kube-Scheduler, является оценка каждого хоста в списке на основе дополнительных требований, таких как Affinity и Labels. Ранжированный список отправляется в Pacific Scheduler Extension, которое, в свою очередь, отправляет его на vSphere API сервер, который перенаправляет его в vSphere DRS службу. DRS определяет, какой хост лучше всего соответствует требованиям к ресурсам, и является наиболее подходящим кандидатом для обеспечения того, чтобы vSphere Pod достиг наивысшего показателя счастья (получая ресурсы, на которые имеет право vSphere Pod). vSphere Pod LifeCycle Controller гарантирует, что vSpherelet на выбранном хосте будет создан и внедряет ядро Photon Linux в vSphere Pod. vSpherelet запускает контейнер. (cм. Первоначальное размещение vSphere Pod где детально рассмотрен процесс создания vSphere Pod).
Освобождение ресурсов
В дополнение к определению размера vSphere Pod, vSphere использует значения Request, перечисленные в Pod манифесте, для применения параметров выделения ресурсов vSphere, чтобы гарантировать доступность запрошенных ресурсов. Может быть зазор между установленным резервированием и размером vSphere Pod. Подобно поведению виртуальной машины, ресурсы доступны, пока нет конфликта ресурсов. Когда происходит конфликт ресурсов, инициируется высвобождение ресурсов. В случае vSphere Pod используется широкий спектр методов консолидации vSphere, но когда дело доходит до выселения модуля, vSphere позволяет Kubernetes выполнять грязную работу. На самом деле, это связано с внутренним управлением событиями в Kubernetes и более детальном представлении об использовании ресурсов.
Namespaces
В Kubernetes имеется высокоуровневая конструкция для управления и контроля Pod, эта конструкция называется Namespace. vSphere 7 с Kubernetes предоставляет аналогичную конструкцию на уровне vSphere, Supervisor namespace. Пул ресурсов vSphere используется для управления вычислительными ресурсами Supervisor namespace. Namespace может быть сконфигурировано с необязательным диапазоном ограничения, который определяет Request и Limits по умолчанию для контейнеров, влияя на размеры vSphere Pod и поведение высвобождения ресурсов. Supervisor namespace — обширная тема, и поэтому больше информации о Namespace будет в следующей статье этой серии.