План Очередь планировщика. Кто последний в очереди? Scheduler framework и его компоненты. Informer
Продолжение предыдущей частиКогда я начал погружаться в k8s и успешно сдал k8s CKA, я подумал что k8s scheduler работает как описано в части 1 и больше мне знать не обязательно, чтобы понимать как действительно работает k8s и почему поды планируются именно таким образом. Оказалось, что я упустил важный пласт знаний, который скрывается отчасти в документации, отчасти разбросан по англоязычным статьям в www. Сегодня мы наверстаем упущенное вместе.
Итоги предыдущей части:
k8s Scheduler непрерывно следит за появлением новых подов и нод, и если есть поды(в специальной очереди), которые не закреплены за нодой, то scheduler пытается закрепить их за правильно подобранной нодой.
Тезисно, k8s нужен для:
Kubernetes в корне своей идеологии использует модульную архитектуру для увелечения гибкости, поддерживаемости и уменьшения сложности кода. k8s Scheduler - не исключение.
Kubernetes использует в своей имплементации Scheduler framework(был придуман как раз для эффективной имплементации k8s планировщика). Это расширяемая архитектура, представленная в Kubernetes, которая позволяет разработчикам настраивать планировщик через написание и включение собственных плагинов. Этот фреймворк состоит из набора точек расширения (extension points), которые позволяют вмешиваться в процесс планирования на различных этапах:
Схема Scheduler framework с официального сайта:
На схеме видно, что зеленым цветом отмечены блоки, которые можно самостоятельно расширять и менять. Желтым цветом отмечены элементы, которые недоступны для модификации. В официальной документации k8s очень не плохо расписано, за что отвечает каждый элемент, однако, чтобы раскрыть все взаимосвязи между объектами, одной документации недостаточно. Именно по этой причине я подготовил расширенную схему ниже и эту статью.
PreEnqueueЭто первый плагин планировщика, который вызывается в начале каждого цикла планирования. Его целью является фильтрация подов, которые должны быть запланированы. Например, если у POD'а не указаны требования к ресурсам, то этот POD не будет запланирован. Дальнейшее планирование POD'а возможно, только при условии, что PreEnqueue вернет success в результате своих проверок.
Комментарий
Это некоторая оптимизация, которая позволяет проводить базовые проверки для подов перед помещением объекта POD'а в основную активную очередь. Эта оптимизация нужна для экономии ресурсов, т.е. если POD не удовлетворяет базовым проверкам, то и в очереди ему делать нечего.
Sort(QueueSort)QueueSort — это функция (или точка расширения в планировщике Kubernetes), которая определяет, как поды должны быть упорядочены в ActiveQ. Сортировка очереди важна, потому что порядок, в котором поды рассматриваются для планирования, может повлиять на общую производительность и распределение ресурсов в кластере. Например, вы можете захотеть, чтобы более критичные поды или поды, ожидающие больше всего времени, были рассмотрены для планирования в первую очередь.
Комментарий
QueueSort не хранит поды; он лишь определяет логику их сортировки в рамках очереди. Очереди подов хранятся в ActiveQ.
Scheduler QueueВ Kubernetes планировщик (kube-scheduler) использует несколько очередей для управления подами, ожидающими назначения на узел. Каждая очередь представляет собой список подов, ожидающих назначения на узел. Всего имеется 3 типа очереди: ActiveQ, UnschedulableQ и BackoffQ. Подробнее тут.
Active QueueЭто активная очередь планирования, в которой хранятся все поды, ожидающие назначения на ноду. Поды в ActiveQ ожидают обработки планировщиком, чтобы быть проверенными по соответствию требованиям, ресурсам нод и так далее, перед тем как будут назначены на подходящую ноду для запуска.
Комментарий
Поды в очереди ActiveQ могут быть как новые, так и перепланированные. Перепланированные(не смог подобрать более подходящего слова для это сущности) поды - это поды, которые уже были запланированы, но по каким-либо причинам были перепланированы. Например, если нода, на которой был запланирован под, упала, то этот POD будет перепланирован на другую здоровую ноду. Кроме этого, QueueSort
UnschedulableQueue (unschedulableQ)UnschedulableQueue предназначена для хранения подов, ожидающих каких-либо событий, в результате которых эти поды могли бы перейти в activeQ.
Например, эта очередь используется для подов которые планировщик не смог разместить на доступных узлах кластера из-за различных ограничений, таких как недостаток ресурсов, требования к affinity, taints и tolerations и другие пользовательские ограничения. Поды в этой очереди считаются "unschedulable" (не готовые к процессу планирования или нераспределяемыми).
Функциональность:
Комментарий
UnschedulableQueue - это ещё 1 способ для оптимизации процесса планирования, который позволяет избежать ненужных циклов планирования подов в условиях, когда их невозможно распределить, и одновременно обеспечивает механизм для их переоценки при изменении условий кластера.
PodBackoffQueue - для обработки и управления повторными попытками планирования подов, которые не удалось запланировать в предыдущих попытках.
Эта очередь имеет механизм экспоненциальной задержки между повторными попытками, чтобы предотвратить чрезмерную нагрузку на планировщик и ресурсы кластера.
Функциональность:
Комментарий про экспоненциальную задержку
Эти механизмы помогают убедиться, что поды, которые по тем или иным причинам временно не могли быть запланированы, не остаются без внимания и имеют возможность быть запланированными, когда условия в кластере меняются.
flushUnschedulableQLeftoverЭтот механизм отвечает за перемещение подов из UnschedulableQueue (очередь нераспределяемых подов) обратно в активную очередь (ActiveQ).
Этот механизм управляет подами в BackoffQueue (очередь отката), которая содержит поды, временно отложенные из-за предыдущих неудачных попыток планирования.
Schedule Pipeline - представляет собой центральную логику планировщика Kubernetes. Schedule Pipeline - это цепочка шагов и проверок, после которых pod должен быть назначен на одну из нод. Schedule Pipeline разделен на 3 потока. Про потоки писал ранее тут.
PreFilterЭтот этап процесса планирования выполняется до основных этапов фильтрации и выбора узла и служит для выполнения быстрых предварительных проверок, чтобы определить, соответствует ли POD базовым критериям для планирования.
Основная функция PreFilter
В случае неудачи pod помещается в UnschedulableQueue, где он ожидает, пока не изменится состояние кластера, что может позволить ему быть запланированным.
FilterFilter является ключевой точкой расширения (extension point) в Scheduling Framework. Он отвечает за фильтрацию узлов, которые не могут принять pod, и выбор узла, который может принять pod.
Процесс фильтрации
Комментарий о расширяемости
т.к. этот плагин (extension point) помечен зеленым(как и другие подобные на схеме), то разработчики разработчики могут создавать и внедрять свои собственные фильтры в рамках Scheduling Framework. Это позволяет адаптировать процесс планирования к специфическим требованиям и условиям кластера.
PreScoreЭтот этап предназначен для подготовки необходимых данных или выполнения предварительных расчетов, которые будут использоваться на этапе ранжирования узлов.
Это еще 1 плагин призванный оптимизировать процесс, его назначение очень похоже на PreFilter по сути.
Пояснение
Цель этапа Score заключается в оценке и ранжировании узлов, которые успешно прошли этап фильтрации, чтобы определить, какой из них лучше всего подходит для размещения данного POD'а.
Каждому узлу присваивается оценка на основе различных критериев, таких как доступные ресурсы, близость к необходимым сервисам, требования к affinity/anti-affinity и другие факторы. Эти оценки затем используются для ранжирования узлов, чтобы определить, какой из них лучше всего подходит для размещения POD'а.
Пояснение о метриках и оценках
Цель этапа Reserve заключается в резервировании необходимых ресурсов на узле для POD'а, который планируется к запуску. Это может включать, например, выделение определенного объема памяти или CPU. Этап Reserve гарантирует, что все ресурсы, необходимые для POD'а, будут доступны на выбранном узле перед его окончательным назначением.
Это необходимо для того, чтобы другие поды не могли занять эти ресурсы(предотвращение ситуации race condition). Этот плагин реализует так же метод UnReserve.
UnReserveЕсли на каком-либо позднем этапе планирования (например, на этапе Permit или Bind) возникают проблемы и POD не может быть успешно размещен на узле, UnReserve используется для отката действий, выполненных на этапе Reserve. UnReserve освобождение ресурсы, делает отмену изменений, сделанных на узле в процессе резервирования, что позволяет этим ресурсам быть доступными для других подов.
Другими словами: Этот этап гарантирует, что ресурсы узлов не остаются беспричинно заблокированными в случае неудачи планирования, повышая тем самым общую эффективность и надежность кластера.
Это метод часть плагина Reserve. Этот метод может быть вызван не зависимо от очереди выполнения из любых других плагинов(после этапа Reserve).
PermitЭтот этап позволяет планировщику синхронизировать свои действия с внешними системами, прежде чем разместить pod на узле.
Основная задача: решить, следует ли разрешить или отложить размещение пода (pod) на узле (node), основываясь на текущем состоянии кластера или внешних факторах.
Этап Permit работает в своем отдельном потоке, что означает, что он выполняется параллельно с основным(main) потоком планировщика. Это подчеркивает асинхронный и неблокирующий характер этой фазы.
Permit может сделать одну из 3-х вещей:
Пояснение:
Все семейство плагинов Bind работает в отдельном потоке, как изображено на схеме.
Простой пример - PreBind может убедиться в доступности внешнего сетевого хранилища перед привязкой.
BindНа этом этапе планировщик фактически привязывает POD к узлу. Это может включать в себя различные действия, такие как резервирование ресурсов, настройка сети, запуск контейнеров и т.д.
Процесс привязки:
Informer в Kubernetes представляет собой мощный инструмент для наблюдения за ресурсами, кэширования их состояния и уведомления об их изменениях, что критически важно для реактивности и эффективности работы различных компонентов системы.
При запуске планировщик Kubernetes извлекает данные, необходимые для планирования, с сервера API Kubernetes через informer с помощью API List и Watch. Эти данные содержат информацию о модулях, узлах, PV, PVC, etc...
Роль и функционал Informer'а
P.S.: Статья вышла больше, чем я ожидал, поэтому сюда не включен разбор конкретных плагинов и пример написания собственного планировщика (если будет запрос в комментариях, то эта статья появится на хабре).
Пожалуйста, не стесняйтесь указывать на неточности и ошибки в комментариях, я буду исправлять и дополнять статью. K8S достаточно сложная тема, информацию о ней пришлось собирать из разных источников.
Полезные ссылки: