Управление ресурсами в Kubernetes немного напоминает зефирный тест, который иногда выходит из-под контроля. Если тщательно не лимитировать, сколько ресурсов может потреблять контейнер, он пойдёт вразнос, примерно как малыш, способный слопать большую пачку Skittles за один присест.

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

Вот почему настолько важно правильно настроить в Kubernetes лимиты и работу с запросами. Понимая, какова роль запросов и лимитов при управлении ресурсами и производительностью в Kubernetes, а также умея настраивать и/или задавать запросы и лимиты, вы гарантируете, что на обработку каждой рабочей нагрузки будет выделено ровно столько ресурсов, сколько нужно — ни больше, ни меньше.

Далее в этой статье подробно рассказано, как в Kubernetes организована работа с запросами и лимитами, как они используются для управления ресурсами. В любой организации чрезвычайно важно управление ресурсами в Kubernetes и роль такого управления. Разберём управление ресурсами в Kubernetes и начнём с самых азов.

Kubernetes нужен, прежде всего, для оркестрации рабочих нагрузок в масштабах кластера серверов. По умолчанию Kubernetes неоткуда узнать, сколько ядер ЦП, памяти или других ресурсов требуется на обработку каждой рабочей нагрузки. В той же степени Kubernetes не пытается контролировать, к какому количеству ресурсов открыт доступ для каждой рабочей нагрузки. Если на том узле, где расположен под, хватает процессорной мощности и памяти, то Kubernetes не станет вмешиваться и позволит поду потреблять любые ресурсы, которые тому потребуются.

Такой подход к правлению ресурсами вполне нормален, пока хватает мощности ЦП и памяти, чтобы все поды работали как задумано. Но могут возникать проблемы, если какой-то под или контейнер пытается пожирать ресурсы, либо общий объём доступных ресурсов снижается из-за каких-то проблем, например, из-за вывода узлов из кластера. В рабочих нагрузках могут начинаться ошибки, либо на обработку этих нагрузок станет уходить слишком много времени. Из-за нехватки процессорной мощности начинается троттлинг процессора. Аналогично, если существует дефицит памяти, то некоторые рабочие нагрузки отрубаются командой OOMKill. Вдобавок, по мере того, как узлы один за другим исчерпывают скудные ресурсы, Kubernetes то и дело пытается перебрасывать поды с одного узла на другой, из-за чего может нарушаться доступность приложения.

К счастью, в Kubernetes предусмотрены специальные фичи – запросы и лимиты – в помощь администратору, чтобы преодолевать подобные проблемы и гарантировать оптимальное распределение ресурсов в кластере.

❯ Что такое запрос в Kubernetes?

В Kubernetes запрос — это минимальное количество ресурсов, которое Kubernetes предоставляет конкретному контейнеру.

Например, если в пересчёте на один контейнер указать для запросов к памяти значение 128 мегабайт, а для ЦП запрос в 750 миллиядер (подробнее о том, что такое миллиядро – чуть ниже), то Kubernetes гарантирует, что у контейнера всегда будет такой необходимый минимум процессорных мощностей и памяти.

Конечно, контейнер может употребить и больше ресурсов – вплоть до любых границ, которые вы установите. Как именно – объяснено ниже. Запрос к памяти или к ЦП просто задаёт абсолютный минимум доступности ресурсов.

Запросы определяются в разделе resources спецификации контейнера. Например:

resources:
	requests:
    	memory: "128Mi"
    	cpu: "750m"

Здесь мы запрашиваем 128 мегабайт памяти и 750 миллиядер ЦП.

❯ В каких единицах измеряются запросы: миллиядра ЦП

Измерять запросы к памяти в мегабайтах достаточно просто, а вот концепция миллиядер известна не так широко.

В Kubernetes миллиядро ЦП – это одна тысячная той единицы, которая называется «модуль ЦП». Модуль ЦП соответствует одному физическому или виртуальному ядру ЦП.

Точный объём вычислительной мощности, выражаемый в этих единицах, может варьироваться, так как у разных ЦП отличается мощность. Поэтому 1000 миллиядер ЦП на одном узле может давать больше, а может меньше, чем 1000 миллиядер на другом узле, если только сами процессоры на этих узлах не идентичны. Тем не менее, по миллиядрам можно приблизительно посчитать и сравнить процессорную мощность, а затем нужно присваивать нужную процессорную мощность разным рабочим нагрузкам.

❯ Как запросы на ЦП и память влияют на размещение подов: искусство планирования

Когда Kubernetes назначает поды (то есть, определяет, на каком узле должен располагаться данный под), он учитывает, сколько запросов было определено для данного пода. Он не присвоит под узлу, если на узле и так мало ресурсов, и ими явно не удовлетворить запросы пода.

Если в распоряжении нет подов, которые соответствовали бы выданным запросам, то Kubernetes вообще не будет назначать под. Под застрянет в ожидающем состоянии до тех пор, пока не откроется узел с достаточным количеством ресурсов (либо пока вы не измените запросы ресурсов для данного пода).

Таким образом, балансировка запросов в сочетании с назначением подов по такому принципу – это в своём роде искусство. Необходимо не попадать в ситуации, в которых установленные вами запросы окажутся чрезмерно высокими. Тогда поды либо вообще не удастся распланировать, либо они будут неэффективно расходовать ресурсы имеющихся узлов.

Допустим, у вас 10 узлов, на каждый из которых приходится по одному модулю ЦП, и по этим узлам вам требуется распределить 10 подов. Если для каждого из узлов вы установите запрос в 550 миллиядер ЦП, то на узле можно будет разместить более одного пода в каждый момент времени. В таком случае вы, скорее всего, недоиспользуете ваши узлы, поскольку некоторые узлы могут функционировать, и при этом примерно половина ресурсов у них будет оставаться не выделенной – если поды не потребляют существенно больше ресурсов, чем им отведено. (Это упрощённый сценарий, поскольку в реальной ситуации у вас вряд ли будет один ЦП на узел, но идею вы поняли).

❯ Практические соображения о том, как эффективно устанавливать значения для запросов

Чтобы не возникало различных проблематичных ситуаций при работе планировщика, администраторы Kubernetes должны расставлять значения запросов, руководствуясь примерно следующими рекомендованными практиками:

  • Запросы должны формулироваться, исходя из фактического использования ресурсов: чтобы вам не приходилось гадать, сколько ресурсов вам потребуется на обслуживание данной рабочей нагрузки, пользуйтесь предусмотренными в Kubernetes инструментами мониторинга, и с их помощью отслеживайте, как используются ресурсы в конкретном окружении. Даже если такие наблюдения ведутся в среде разработки/тестирования, важно собирать метрики Kubernetes. Например, следить, как потребляется ЦП и память – тогда вы легко выясните, сколько ресурсов использует данная рабочая нагрузка при эксплуатации. Возможно, эти данные не совпадут с вашими ожиданиями.

  • Устанавливая запросы, учитывайте, для чего именно предназначена каждая рабочая нагрузка. Для критически важных нагрузок (например, для приложений, работающих в продакшене) может потребоваться более агрессивный подход к запросам, чем для менее важных рабочих нагрузок.

  • Соотносите запросы с тем, сколько ресурсов доступно на данном узле: учитывайте, каким количеством ресурсов обладает каждый узел, и как запросы повлияют на то, сколько подов можно будет разместить на каждом узле. Опять же, решительно избегайте таких сценариев, в которых узлы недоиспользуются, так как из-за запросов вы не сможете эффективно распределять поды по этим узлам.

  • Учитывайте, что мощность ЦП может варьироваться: поскольку значение 1 модуля ЦП может отличаться от узла к узлу, при назначении запросов держите в уме фактическую процессорную мощность, которой располагаете на каждом из ваших узлов. Также помните, что можете воспользоваться DaemonSets, если хотите принудительно заставить под работать именно на конкретном узле, поскольку ЦП на этом узле обладает нужными вам характеристиками.

❯ Что такое лимит в Kubernetes?

Теперь, разобравшись, как в Kubernetes устроены запросы, перейдём к изучению их альтер эго: лимитам Kubernetes.

Лимит в Kubernetes – это максимальный объём ресурсов конкретного типа, которые может потребить контейнер. Например, если установить лимит ЦП в 1500 миллиядер, а лимит памяти в 1024 мегабайт на контейнер, то Kubernetes не позволит контейнеру потребить ресурсы сверх этого предела.

Ресурсы, как и запросы, определяются в спецификации контейнера. Например:

resources:
	limits:
    	memory: "1024Mi"
    	cpu: "1500m"

❯ Как лимиты памяти и процессорной мощности предотвращают сверхпотребление ресурсов и помогают поддерживать кластер в стабильном состоянии

Важность лимитов памяти и процессорной мощности заключается в том, что они не дают контейнеру потребить настолько много ресурсов, что их перестанет хватать на поддержку других контейнеров. Как было указано выше, в результате возможны ситуации с отказом или недостаточной производительностью приложений из-за нехватки необходимых ресурсов. Кроме того, из-за этого может быть инициировано нежелательное перераспределение подов, которые совместно используют узел в обделённом ресурсами контейнере.

Разумеется, вы не собираетесь занижать лимиты памяти и ЦП настолько, чтобы лишить контейнер даже того минимума ресурсов, которые необходимы ему для нормальной работы. Вы должны стремиться соблюдать здоровый баланс, при котором контейнер сохранит доступ к разумному объёму ресурсов, но в то же время высвободить достаточное количество ресурсов на обслуживание других рабочих нагрузок.

❯ Умный подход к лимитированию: стратегии оптимизации использования ресурсов

Следующие стратегии помогут вам выставить правильно подобранные лимиты:

  • Решайте, каким рабочим нагрузкам отдать приоритет: как и в случае с запросами на память и процессорную мощность, для некоторых рабочих нагрузок могут понадобиться одни лимиты, для других — иные, в зависимости от важности этих нагрузок. Целесообразно устанавливать высокие лимиты для наиболее критичных нагрузок и сравнительно низкие – для тех сервисов, отказ которых в принципе можно перетерпеть.

  • Учитывайте масштабируемость кластера: если в вашем кластере включены функции автомасштабирования, позволяющие быстро добавлять узлы в случаях, когда начинает просматриваться общая стеснённость в ресурсах, то вы не столь рискуете при установке высоких лимитов, чем в случае, когда общая ресурсоёмкость кластера фиксирована и масштабированию не поддаётся.

  • Присваивайте диапазоны лимитовДиапазоны лимитов — это фича Kubernetes, позволяющая ограничивать уровни использования ресурсов «по пространствам имён» (а не «по контейнерам»). Диапазоны лимитов могут послужить страховкой, помогающей не переусердствовать с лимитами для отдельных контейнеров, поскольку контейнерам не будет позволено потреблять память или процессорную мощность сверх того лимита, который установлен для данного пространства имён, даже если общие лимиты ресурсов для контейнеров выше.

❯ Ключевые отличия между запросами и лимитами ресурсов в Kubernetes

Запросы и лимиты на память и процессорную мощность в Kubernetes похожи в том, что и те, и другие помогут вам эффективно управлять ресурсами. Но очевидная разница между ними такова: запросы на ресурсы помогают установить тот минимум ресурсов, который будет доступен приложению, тогда как лимиты, напротив, контролируют максимум ресурсов.

Также важно отметить, что не обязательно использовать запросы и лимиты ресурсов в сочетании друг с другом. Их можно задействовать независимо, например, определяя для контейнера запрос на память или на процессорную мощность, но не определяя соответствующего лимита. Поступать так целесообразно в сценариях, когда вы хотите гарантировать, что критически важная рабочая нагрузка всегда будет обеспечена необходимым минимумом ресурсов, но сверхпотребление ресурсов в вашем случае – не проблема.

❯ Почему существенны как запросы, так и лимиты: гармоничный баланс

При всём вышесказанном, обычно лучше задавать как запросы, так и лимиты. Даже если маловероятно, что данная рабочая нагрузка будет потреблять неоправданно много ресурсов, всегда могут возникнуть непредусмотренные всплески расхода ресурсов — например, из-за утечек памяти. Лимиты – это предохранитель, помогающий предотвращать проблемы со стабильностью или производительностью кластеров.

Аналогично, самостоятельно устанавливать запросы – это, как правило, умный ход, поскольку так вы сами исключаете недостаток ресурсов и гарантируете хотя бы минимальную производительность. Иногда доступные контейнерам ресурсы могут неожиданно сокращаться из-за отказа узлов, поэтому, правильно применяя запросы, можно обеспечить наиболее эффективное переназначение контейнеров.

❯ Распространённые проблемы, возникающие из-за неправильно сконфигурированных запросов и лимитов

Если неправильно сконфигурировать в Kubernetes запросы и лимиты, то могут возникать разнообразные проблемы. Наиболее распространённые из них перечислены ниже.

Многие из этих проблем могут быть обусловлены более глубокими причинами, нежели просто неверно выставленные настройки запросов и лимитов к ЦП и памяти. Например, есть много причин, по которым узел может перестать отвечать – например, возникла проблема с сетевым соединением или отказ операционной системы на данном узле. Но зачастую, даже если корень проблемы не связан с запросами или лимитами к памяти или ЦП, именно корректировка запросов и лимитов помогает сгладить нежелательные последствия.

❯ Как устанавливать запросы и лимиты в Kubernetes: наилучшие практики

Далее перечислены наилучшие практики, рекомендуемые при работе с запросами и лимитами в Kubernetes. Они помогут избежать проблем наподобие тех, что описаны выше:

  • Соотносите настройки с приоритетом тех или иных рабочих нагрузок: как уже упоминалось выше, как правило, бывает целесообразно выделять больше ресурсов на наиболее важные рабочие нагрузки, а менее приоритетные поды снабжать ресурсами более сдержанно.

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

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

  • Стратегически пользуйтесь автомасштабированием: возможности автомасштабирования (доступные в определённых дистрибутивах и сервисах Kubernetes) подразумевают автоматическое добавление узлов в кластеры. Тем самым увеличивается объём доступных ресурсов. Автомасштабирование помогает защититься от отказов, возникающих из-за сверхпотребления ресурсов, так что почему бы не взять эту возможность на вооружение при обслуживании критически важных нагрузок. Правда, поскольку из-за добавления узлов возрастает цена эксплуатации Kubernetes, не рассматривайте автомасштабирование как альтернативу борьбе с потреблением ресурсов. Лучше обходиться эффективным контролем лимитов.

❯ Запросы и лимиты как средство, чтобы выжать дополнительную пользу из Kubernetes

Работая с Kubernetes, вы не обязаны конфигурировать запросы и лимиты, точно как не обязаны контролировать, сколько шоколадок съедает ваш ребёнок. Но запросы и лимиты – это удобный инструмент, помогающий поддерживать здоровый уровень потребления ресурсов. Так любая нагрузка, которой требуются ресурсы, сможет справиться с поставленной задачей, но при этом не будет и ненасытных задач, из-за которых могут возникнуть проблемы в масштабах всего кластера.

? Читайте также:


Новости, обзоры продуктов и конкурсы от команды Timeweb.Cloud - в нашем Telegram-канале

Перейти ↩



Комментарии (7)


  1. NightTiger
    10.08.2024 08:23
    +4

    Стоит отметить, что все чаще практикуется подход, где устанавливаются реквесты по CPU, RAM, устанавливаются лимиты по RAM, но не устанавливаются лимиты CPU, позволяя временный Overcommit ресурсов если они есть на машине, пока не отработает автоскейлер дальше. А приложения лучше успевают пройти пик нагрузок без тротлинга со стороны ядра.


    1. NightTiger
      10.08.2024 08:23

      Ну и еще стоит отметить разные типы QoS если заданы ресурсы и лимиты и то, как это влияет на класс пода и его приоритет при вытеснении (Eviction) с машины.


  1. Evgenym
    10.08.2024 08:23

    Я пришел к такой практике:

    1. Выставляю для памяти одинаковые значения для реквеста и лимита

    2. Для CPU выставляю только значение реквеста

    Данные для реквестов и лимитов выставляю спустя некоторое время на основе анализа потребления ресурсов из Prometheus за какое-то время.

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


    1. SlavikF
      10.08.2024 08:23

      А в чём преимущество ставить "для памяти одинаковые значения для реквеста и лимита"?


      1. Evgenym
        10.08.2024 08:23

        Согласно документации поду гарантированно выдаются ресурсы из реквеста. Если я знаю, что мое приложение со всеми пиками ест Х памяти, то и имеет смысл отдать ему столько, ну, плюс небольшой запас на всякий случай накинуть.


  1. Derfirm
    10.08.2024 08:23
    +3

    Здравствуйте, спасибо за статью и классные примеры.

    Хотел бы заметить что MiB это мебибайты, которые более "честные" в расчетах тех же дисков, так как 1KiB равен 1024 байтам, но обычный 1KB это 1000 байт.


  1. Sigest
    10.08.2024 08:23

    А как связаны DaemonSet и желание повесить под на определенную ноду? DaemonSet отскейлит под на все ноды. А чтобы под работал на ноде с определенными ресурсами, то как вариант можно использовать affinity или node selector