Alesha

Проектирование кластеров Kubernetes — выбор размера рабочих узлов

Оригинальная статья: August 2023

Перевод: November 2024


Проектирование кластеров Kubernetes — выбор размера рабочих узлов


TL;DR: Что лучше - кластер Kubernetes с меньшим количеством крупных узлов или множеством мелких? В этой статье рассматриваются плюсы и минусы обоих подходов.

Когда вы создаете кластер Kubernetes, один из первых вопросов может быть: “Какой тип рабочих узлов следует использовать и сколько их нужно?”

Если вы создаете локальный кластер, стоит ли заказывать несколько мощных серверов последнего поколения или использовать десяток старых машин, которые есть в вашем дата-центре?

Или если вы используете управляемый сервис Kubernetes, такой как Google Kubernetes Engine (GKE), следует ли использовать восемь экземпляров n1-standard-1 или два n1-standard-4 для достижения желаемой вычислительной мощности?

Содержание

Емкость кластера

В общем случае, кластер Kubernetes можно рассматривать как абстракцию набора отдельных узлов в виде большого “супер-узла”.

Общая вычислительная мощность этого супер-узла (CPU и память) является суммой мощностей всех составляющих его узлов.

Существует несколько способов достичь этого.

Например, предположим, вам нужен кластер с общей мощностью 8 ядер CPU и 32 ГБ оперативной памяти.

Вот только два из возможных способов спроектировать ваш кластер:

Маленькие и большие узлы в кластере Kubernetes

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

Какой вариант лучше?

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

Зарезервированные ресурсы в рабочих узлах Kubernetes

Каждый рабочий узел в кластере Kubernetes является вычислительной единицей, на которой запущен kubelet — агент Kubernetes.

Kubelet — это бинарный файл, который подключается к плоскости управления и поддерживает текущее состояние узла синхронизированным с состоянием кластера.

Например, когда планировщик Kubernetes назначает под определенному узлу, он не отправляет сообщение напрямую в kubelet.

Вместо этого он создает объект Binding и сохраняет его в etcd.

Kubelet регулярно проверяет состояние кластера, и как только он замечает новый под, назначенный его узлу, он приступает к загрузке спецификации пода и его созданию.

Kubelet обычно развертывается как служба SystemD и работает как часть операционной системы.

Kubelet, SystemD и операционной системе нужны ресурсы, такие как CPU и память, для правильной работы.

Следовательно, не все ресурсы ваших рабочих узлов доступны для запуска подов.

Ресурсы CPU и памяти обычно распределяются следующим образом:

  1. Операционная система
  2. Kubelet
  3. Поды
  4. Порог вытеснения

Распределение ресурсов в узле Kubernetes

Вы можете задаться вопросом, какие ресурсы назначаются каждому из них.

Хотя эти значения можно настраивать, в большинстве случаев CPU резервируется со следующими распределениями:

  • 6% первого ядра
  • 1% следующего ядра (до 2 ядер)
  • 0.5% следующих двух ядер (до 4)
  • 0.25% любых ядер свыше четырех

Для памяти это может выглядеть так:

  • 255 MiB памяти для машин с менее чем 1 ГБ
  • 25% первых 4ГБ памяти
  • 20% следующих 4ГБ памяти (до 8ГБ)
  • 10% следующих 8ГБ памяти (до 16ГБ)
  • 6% следующих 112ГБ памяти (до 128ГБ)
  • 2% любой памяти свыше 128ГБ

Наконец, порог вытеснения обычно составляет 100МБ.

Что такое порог вытеснения?

Это порог использования памяти — если узел пересекает этот порог, kubelet начинает вытеснять поды, поскольку в текущем узле недостаточно памяти.

Давайте рассмотрим пример.

Для экземпляра с 8ГБ и 2 vCPU доступные ресурсы распределяются следующим образом:

  1. 70m vCPU и 1.8ГБ для kubelet и операционной системы (они обычно объединены)
  2. 100МБ для порога вытеснения
  3. Оставшиеся 6.1ГБ памяти и 1930 миллиядер могут использоваться подами

Только 75% от общего объема памяти используется для запуска рабочих нагрузок.

Распределение ресурсов в узле Kubernetes с 2 vCPU и 8ГБ памяти

Но это еще не все.

Вашему узлу может потребоваться запускать поды на каждом узле (например, DaemonSets) для правильной работы, и они тоже потребляют память и CPU.

Примеры включают Kube-proxy, агент логирования, такой как Fluentd или Fluent Bit, NodeLocal DNSCache или CSI драйвер.

Это фиксированная стоимость, которую вы должны платить независимо от размера узла.

Распределение ресурсов в узле Kubernetes с DaemonSets

Имея это в виду, давайте рассмотрим плюсы и минусы двух противоположных направлений: “несколько крупных узлов” и “много мелких узлов”.

Обратите внимание, что “узлы” в этой статье всегда относятся к рабочим узлам. Выбор количества и размера узлов плоскости управления — это совершенно другая тема.

Распределение ресурсов и эффективность рабочих узлов

Ресурсы, зарезервированные kubelet, уменьшаются с увеличением размера экземпляров.

Давайте рассмотрим два крайних сценария.

Вы хотите развернуть семь реплик приложения с запросами 0.3 vCPU и 2ГБ памяти.

  1. В первом сценарии вы выделяете один рабочий узел для развертывания всех реплик.
  2. Во втором сценарии вы развертываете по одной реплике на узел.

Для простоты мы будем предполагать, что на этих узлах не запущены DaemonSets.

Общие ресурсы, необходимые для семи реплик, составляют 2.1 vCPU и 14ГБ памяти (т.е. 7 x 300m = 2.1 vCPU и 7 x 2ГБ = 14ГБ).

Может ли экземпляр с 4 vCPU и 16ГБ запустить эти рабочие нагрузки?

Давайте посчитаем зарезервированный CPU:

6% первого ядра           = 60m +
1% второго ядра          = 10m +
0.5% оставшихся ядер     = 10m
---------------------------------
всего                    = 80m

Доступный CPU для запуска подов составляет 3.9 vCPU (т.е. 4000m - 80m) — более чем достаточно.

Давайте проверим память, зарезервированную для kubelet:

25% первых 4ГБ памяти         = 1ГБ
20% следующих 4ГБ памяти      = 0.8ГБ
10% следующих 8ГБ памяти      = 0.8ГБ
--------------------------------------
всего                         = 2.8ГБ

Общая доступная память для подов составляет 16ГБ - (2.8ГБ + 0.1ГБ) — где 0.1ГБ учитывает 100МБ порога вытеснения.

Наконец, поды могут потреблять до 13.1ГБ памяти.

Распределение ресурсов в узле Kubernetes с 2 vCPU и 16ГБ памяти

К сожалению, этого недостаточно (т.е. 7 реплик требуют 14ГБ памяти, но у вас есть только 13.1ГБ), и вам следует выделить вычислительную единицу с большим объемом памяти для развертывания рабочих нагрузок.

Если вы используете облачного провайдера, следующее доступное увеличение для вычислительной единицы составляет 4 vCPU и 32ГБ памяти.

Узел с 2 vCPU и 16ГБ памяти недостаточен для запуска семи реплик

Отлично!

Давайте посмотрим на другой сценарий, где мы пытаемся найти наименьший экземпляр, который мог бы вместить одну реплику с запросами, равными 0.3 vCPU и 2ГБ памяти.

Давайте попробуем экземпляр типа 1 vCPU и 4ГБ памяти.

Общий зарезервированный CPU составляет 6% или 60m, а доступный CPU для подов составляет 940m.

Поскольку приложению требуется только 300m CPU, этого достаточно.

Зарезервированная память для kubelet составляет 25% или 1ГБ плюс дополнительные 0.1ГБ порога вытеснения.

Общая доступная память для подов составляет 2.9ГБ; поскольку приложению требуется только 2ГБ, этого значения достаточно.

Отлично!

Распределение ресурсов в узле Kubernetes с 2 vCPU и 16ГБ памяти

Давайте сравним две конфигурации.

Общие ресурсы для первого кластера — это всего один узел — 4vCPU и 32 ГБ.

Второй кластер имеет семь экземпляров с 1 vCPU и 4ГБ памяти (всего 7 vCPU и 28 ГБ памяти).

В первом примере 2.9ГБ памяти и 80m CPU зарезервированы для Kubernetes.

Во втором 7.7ГБ (1.1ГБ x 7 экземпляров) памяти и 360m CPU (60m x 7 экземпляров) зарезервированы.

Вы уже можете заметить, как ресурсы используются более эффективно при выделении более крупных узлов.

Сравнение распределения ресурсов между кластером с одним узлом и кластером с несколькими узлами

Но это еще не все.

В более крупном экземпляре все еще есть место для запуска дополнительных реплик — но сколько?

  • Зарезервированная память составляет 3.66ГБ (3.56ГБ kubelet + 0.1ГБ порог вытеснения), и общая доступная память для подов составляет 28.44ГБ.
  • Зарезервированный CPU по-прежнему составляет 80m, и поды могут использовать 3920m.

На этом этапе вы можете найти максимальное количество реплик для памяти и CPU с помощью следующего деления:

Всего CPU   3920 /
CPU пода     300
------------------
Макс подов   13.1

Вы можете повторить расчет для памяти:

Всего памяти  28.44 /
Память пода     2
---------------------
Макс подов     14.22

Приведенные выше числа показывают, что вы исчерпаете CPU раньше памяти и можете разместить до 13 подов в рабочем узле с 4 vCPU и 32ГБ.

Расчет емкости подов для рабочего узла с 2 vCPU и 32ГБ

А что со вторым сценарием?

Есть ли возможность для масштабирования?

Не совсем.

Хотя у экземпляров все еще есть больше CPU, у них остается только 0.9ГБ памяти после развертывания первого пода.

Расчет емкости подов для рабочего узла с 1 vCPU и 4ГБ

В заключение, более крупный узел не только лучше использует ресурсы, но и может минимизировать фрагментацию ресурсов и повысить эффективность.

Означает ли это, что вы всегда должны выделять более крупные экземпляры?

Давайте рассмотрим другой крайний сценарий: что происходит, когда узел неожиданно теряется?

Отказоустойчивость и репликация

Небольшое количество узлов может ограничить эффективную степень репликации ваших приложений.

Например, если у вас есть высокодоступное приложение, состоящее из 5 реплик, но только два узла, то эффективная степень репликации снижается до 2.

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

Фактор репликации для кластера с двумя узлами и пятью репликами равен двум

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

Таким образом, вам может потребоваться определенное минимальное количество узлов в вашем кластере, если у вас есть требования к высокой доступности.

Фактор репликации для кластера с пятью узлами и пятью репликами равен пяти

Вы также должны учитывать размер узла.

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

Если узел меньше и размещает только несколько рабочих нагрузок, планировщик переназначает только небольшое количество подов.

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

И в зависимости от вашей настройки это может привести к дальнейшим задержкам.

Давайте рассмотрим почему.

Шаги масштабирования и время подготовки

Вы можете масштабировать приложения, развернутые в Kubernetes, используя комбинацию горизонтального масштабирования (т.е. увеличение количества реплик) и автомасштабирования кластера (т.е. увеличение количества узлов).

Предполагая, что у вас есть кластер на полной мощности, как размер узла влияет на ваше автомасштабирование?

Во-первых, вы должны знать, что Cluster Autoscaler не смотрит на доступную память или CPU при запуске автомасштабирования.

Другими словами, полное использование кластера не запускает Cluster Autoscaler.

Вместо этого Cluster Autoscaler создает больше узлов, когда под не может быть запланирован из-за нехватки ресурсов.

В этот момент автомасштабирование вызывает API облачного провайдера для выделения дополнительных узлов для этого кластера.

К сожалению, выделение узлов обычно происходит медленно.

Это может занять несколько минут для выделения новой виртуальной машины.

Меняется ли время выделения для больших или меньших узлов?

Нет, оно обычно постоянно независимо от размера экземпляра.

Кроме того, автомасштабирование кластера не ограничивается добавлением одного узла за раз; оно может добавлять несколько одновременно.

Давайте рассмотрим пример.

Есть два кластера:

  1. Первый имеет один узел с 4 vCPU и 32ГБ.
  2. Второй имеет тринадцать узлов с 1 vCPU и 4ГБ.

В кластере развернуто приложение с 0.3 vCPU и 2ГБ памяти и масштабировано до 13 реплик.

Обе конфигурации работают на полной мощности — у них не осталось свободного места для подов.

Два кластера с: один под на узел и все поды в одном узле

Что происходит, когда развертывание масштабируется до 15 реплик (т.е. еще два)?

В обоих кластерах Cluster Autoscaler обнаруживает, что дополнительные поды не могут быть запланированы из-за нехватки ресурсов и выделяет:

  • Дополнительный узел 4 vCPU и 32ГБ для первого кластера.
  • Два узла 1 vCPU и 4ГБ для второго кластера.

Поскольку нет разницы во времени между выделением больших или малых экземпляров, узлы будут доступны одновременно в обоих сценариях.

Ожидающие поды запускают автомасштабирование независимо от их размера

Однако можете ли вы заметить другое различие?

В первом кластере есть место еще для 11 подов, поскольку общая емкость составляет 13.

Вместо этого второй кластер все еще работает на максимуме.

Можно утверждать, что меньшие приращения более эффективны и дешевле, потому что вы добавляете только то, что нужно.

Шаги автомасштабирования в больших и малых узлах

Но давайте посмотрим, что происходит, когда вы снова масштабируете развертывание — на этот раз до 17 реплик (т.е. еще два).

  • Первый кластер создает два дополнительных пода в существующем узле.
  • Второй кластер работает на полной мощности. Поды находятся в состоянии Pending, и запускается Cluster Autoscaler. Наконец, выделяются еще два рабочих узла.

Компромиссы для шагов автомасштабирования в узлах Kubernetes

В первом кластере масштабирование происходит почти мгновенно.

Во втором вы должны ждать, пока узлы будут выделены, прежде чем поды смогут обслуживать запросы.

Другими словами, масштабирование быстрее в первом случае и занимает больше времени во втором.

В общем, поскольку время выделения составляет несколько минут, вы должны тщательно подумать о том, чтобы запускать Cluster Autoscaler экономно, чтобы не увеличивать время запуска подов.

Другими словами, вы можете получить более быстрое масштабирование с более крупными узлами, если вы не против (потенциально) неполного использования ресурсов.

Но на этом не заканчивается.

Загрузка образов контейнеров также влияет на то, как быстро вы можете масштабировать ваши рабочие нагрузки — и это связано с количеством узлов в кластере.

Загрузка образов контейнеров

Когда под создается в Kubernetes, его определение сохраняется в etcd.

Задача kubelet — обнаружить, что под назначен его узлу, и создать его.

Kubelet будет:

После этих шагов под жив, и kubelet может перейти к проверке проб живости и готовности и обновить плоскость управления состоянием нового пода.

Kubelet и интерфейсы CRI, CSI и CNI

Важно заметить, что когда CRI создает контейнер в поде, он должен сначала загрузить образ контейнера.

Это если только образ контейнера еще не кэширован на текущем узле.

Давайте посмотрим, как это влияет на масштабирование с двумя кластерами:

  1. Первый имеет один узел с 4 vCPU и 32ГБ.
  2. Второй имеет тринадцать узлов с 1 vCPU и 4ГБ.

Давайте развернем 13 реплик приложения с 0.3 vCPU и 2ГБ памяти.

Приложение использует контейнерный образ на основе OpenJDK и весит 1ГБ (только базовый образ весит 775МБ).

Что происходит с двумя кластерами?

  • В первом кластере Container Runtime загружает образ один раз и запускает 13 реплик.
  • Во втором кластере каждый Container Runtime загружает и запускает образ.

В первом сценарии загружается только 1ГБ.

Container runtime загружает образ контейнера один раз и запускает 13 реплик

Однако во втором сценарии вы загружаете 13ГБ образов контейнеров.

Поскольку загрузка занимает время, второй кластер медленнее создает реплики, чем первый

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

Каждый из 13 контейнерных сред выполнения загружает один образ

Важно отметить, что эта проблема усугубляется с Cluster Autoscaler.

Если у вас небольшие узлы:

  • Cluster Autoscaler выделяет несколько узлов одновременно.
  • Как только они готовы, каждый начинает загружать контейнерный образ.
  • Наконец, создается под.

Когда вы выделяете более крупные узлы, образ, вероятно, уже кэширован на узле, и под может запуститься немедленно.

Так значит, всегда следует выделять более крупные узлы?

Не обязательно.

Вы можете смягчить проблему загрузки одного и того же контейнерного образа узлами с помощью прокси реестра контейнеров.

В этом случае образ все еще загружается, но из локального реестра в текущей сети.

Или вы можете прогреть кэш для узлов с помощью таких инструментов, как spegel.

С Spegel узлы являются пирами, которые могут анонсировать и делиться слоями контейнерных образов.

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

Но пропускная способность контейнеров - не единственная пропускная способность, которую нужно держать под контролем.

Kubelet и масштабирование API Kubernetes

Kubelet спроектирован для получения информации из плоскости управления.

Так что через регулярные интервалы kubelet отправляет запрос к API Kubernetes для проверки состояния кластера.

Но разве плоскость управления не отправляет инструкции в kubelet?

Модель получения проще масштабируется, потому что:

  • Плоскости управления не нужно отправлять сообщения каждому рабочему узлу.
  • Узлы могут независимо опрашивать API-сервер в своем собственном темпе.
  • Плоскости управления не нужно держать соединения с kubelet открытыми.

Обратите внимание, что есть заметные исключения. Команды такие как kubectl logs и kubectl exec требуют, чтобы плоскость управления подключалась к kubelet (т.е. модель push).

Но kubelet не только запрашивает информацию.

Он также сообщает информацию обратно мастеру.

Например, kubelet сообщает о состоянии узла кластеру каждые десять секунд.

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

И kubelet поддерживает плоскость управления в курсе метрик контейнеров.

Другими словами, несколько запросов в обоих направлениях (т.е. от и к плоскости управления) делаются kubelet к плоскости управления для правильного функционирования узла.

В Kubernetes 1.26 и ранее, kubelet мог делать до 5 запросов в секунду для этого (это было ослаблено в Kubernetes >1.27).

Итак, предполагая, что ваш kubelet работает на полной мощности (т.е. 5 запросов в секунду), что происходит, когда вы запускаете несколько меньших узлов против одного большого узла?

Давайте посмотрим на наши два кластера:

  1. Первый имеет один узел с 4 vCPU и 32ГБ.
  2. Второй имеет тринадцать узлов с 1 vCPU и 4ГБ.

Первый генерирует 5 запросов в секунду.

Один kubelet делает 5 запросов в секунду

Второй 65 запросов в секунду (т.е. 13 x 5).

13 kubelet делают по 5 запросов в секунду каждый

Вы должны масштабировать ваш API-сервер для обработки более частых запросов, когда вы запускаете кластеры со многими меньшими узлами.

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

Ограничения узлов и кластера

Есть ли ограничение на количество узлов, которое может иметь кластер Kubernetes?

Kubernetes спроектирован для поддержки до 5000 узлов.

Однако это не жесткое ограничение, как продемонстрировала команда Google, позволив вам запускать кластеры GKE с 15,000 узлами.

Для ��ольшинства сл��чаев использования 5000 узлов - это уже большое число и может не быть фактором, который мог бы повлиять на ваше решение в пользу более крупных или меньших узлов.

Вместо этого, максимальное количество подов, которое вы можете запустить в узле, может заставить вас пересмотреть архитектуру вашего кластера.

Итак, сколько Подов вы можете запустить в узле Kubernetes?

Большинство облачных провайдеров позволяют вам запускать от 110 до 250 подов на узел.

Если вы настраиваете кластер самостоятельно, по умолчанию это 110.

В большинстве случаев это число не является ограничением kubelet, а склонностью облачного провайдера к риску двойного назначения IP-адресов.

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

В большинстве случаев каждому рабочему узлу назначается подсеть с 256 адресами (например, 10.0.1.0/24).

Каждому рабочему узлу назначена подсеть

Из них два зарезервированы и вы можете использовать 254 для запуска ваших Подов.

Рассмотрим сценарий, где у вас 254 пода в одном узле.

Вы создаете еще один под, но исчерпали доступные IP-адреса, и он остается в состоянии pending.

Чтобы исправить проблему, вы решаете уменьшить количество реплик до 253.

Создается ли ожидающий под в кластере?

Вероятно, нет.

Когда вы удаляете под, его состояние меняется на “Terminating”.

Kubelet отправляет SIGTERM в Под (а также вызывает хук жизненного цикла preStop, если он присутствует) и ждет, пока контейнеры корректно завершат работу.

Если контейнеры не завершаются в течение 30 секунд, kubelet отправляет сигнал SIGKILL контейнеру и принудительно завершает процесс.

В течение этого периода Под все еще не освободил IP-адрес, и трафик все еще может достигать его.

Когда под наконец удаляется, IP-адрес освобождается.

Под освобождает IP-адрес

В этот момент ожидающий под может быть создан, и ему назначается тот же IP-адрес, что и у последнего.

Это хорошая идея?

Ну, других IP-адресов нет — так что у вас нет выбора.

Каковы последствия?

Помните, когда мы упоминали, что под должен корректно завершить работу и обработать все ожидающие запросы?

Ну, если под завершается резко (т.е. без корректного завершения) и IP-адрес немедленно назначается другому поду, все существующие приложения и компоненты kubernetes могут все еще не знать об изменении.

В результате, некоторый существующий трафик может быть ошибочно отправлен новому Поду, потому что у него тот же IP-адрес, что и у старого.

Чтобы избежать этой проблемы, вы можете иметь меньше назначенных IP-адресов (например, 110) и использовать оставшиеся как буфер.

Таким образом, вы можете быть разумно уверены, что тот же IP-адрес не используется немедленно повторно.

Хранилище

Вычислительные единицы имеют ограничения на количество дисков, которые могут быть подключены.

Например, Standard_D2_v5 с 2 vCPU и 8ГБ памяти может иметь до 4 дисков данных, подключенных в Azure.

Если вы хотите развернуть StatefulSet на рабочем узле, который использует тип экземпляра Standard_D2_v5, вы не сможете создать более четырех реплик.

Это потому, что каждая реплика в StatefulSet имеет подключенный диск.

Как только вы создаете пятую, Под останется в состоянии pending, потому что Persistent Volume Claim не может быть привязан к Persistent Volume.

И почему нет?

Потому что каждый Persistent Volume - это подключенный диск, вы можете иметь только 4 для этого экземпляра.

Итак, каковы ваши варианты?

Вы можете выделить более крупный экземпляр.

Или вы могли бы повторно использовать тот же диск с разным полем subPath.

Давайте посмотрим на пример.

Следующий persistent volume требует диск с 16ГБ пространства:

pvc.yaml

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: shared
spec:
  storageClassName: default
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 16Gi

Если вы отправите этот ресурс в кластер, вы увидите, что создается Persistent Volume и привязывается к нему.

Существует отношение один-к-одному между Persistent Volume и Persistent Volume Claims, поэтому вы не сможете иметь больше Persistent Volume Claims для использования того же диска.

Если вы хотите использовать claim в ваших подах, вы можете сделать это так:

deployment-1.yaml

apiVersion: apps/v1
kind: Deployment
metadata:
  name: app1
spec:
  selector:
    matchLabels:
      name: app1
  template:
    metadata:
      labels:
        name: app1
    spec:
      volumes:
        - name: pv-storage
          persistentVolumeClaim:
            claimName: shared
      containers:
        - name: main
          image: busybox
          volumeMounts:
            - mountPath: '/data'
              name: pv-storage

У вас мог бы быть другой deployment, использующий тот же Persistent Volume Claim:

deployment-2.yaml

apiVersion: apps/v1
kind: Deployment
metadata:
  name: app2
spec:
  selector:
    matchLabels:
      name: app2
  template:
    metadata:
      labels:
        name: app2
    spec:
      volumes:
        - name: pv-storage
          persistentVolumeClaim:
            claimName: shared
      containers:
        - name: main
          image: busybox
          volumeMounts:
            - mountPath: '/data'
              name: pv-storage

Однако с этой конфигурацией оба пода будут записывать свои данные в одну и ту же папку.

Вы могли бы заставить их работать в поддиректориях с помощью subPath, чтобы обойти проблему.

deployment-2.yaml

apiVersion: apps/v1
kind: Deployment
metadata:
  name: app2
spec:
  selector:
    matchLabels:
      name: app2
  template:
    metadata:
      labels:
        name: app2
    spec:
      volumes:
        - name: pv-storage
          persistentVolumeClaim:
            claimName: shared
      containers:
        - name: main
          image: busybox
          volumeMounts:
            - mountPath: '/data'
              name: pv-storage
              subPath: app2

Deployments будут записывать свои данные по следующим путям:

  • /data/app1 для первого deployment и
  • /data/app2 для второго.

Этот обходной путь не является идеальным решением и имеет несколько ограничений:

  • Все deployments должны помнить использовать subPath.
  • Если вам нужно записывать в том, вы должны выбрать том Read-Write-Many, который может быть доступен с нескольких узлов. Такие обычно дороги в предоставлении.

Также тот же обходной путь не будет работать с StatefulSet, поскольку он создаст новый Persistent Volume Claim (и Persistent Volume) для каждой реплики.

Выводы и заключение

Итак, следует ли использовать несколько крупных узлов или много мелких узлов в вашем кластере?

Это зависит от ситуации.

Что вообще считать маленьким или большим?

Все сводится к рабочим нагрузкам, которые вы развертываете в вашем кластере.

Например, если вашему приложению требуется 10 ГБ памяти, запуск экземпляра с 16ГБ памяти равносилен “запуску меньшего узла”.

Тот же экземпляр с приложением, которому требуется только 64МБ памяти, может считаться “большим”, поскольку вы можете поместить несколько таких приложений.

А как насчет смеси рабочих нагрузок с разными требованиями к ресурсам?

В Kubernetes нет правила, что все ваши узлы должны иметь одинаковый размер.

Ничто не мешает вам использовать смесь узлов разных размеров в вашем кластере.

Это может позволить вам найти компромисс между плюсами и минусами обоих подходов.

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

Калькулятор экземпляров Kubernetes позволяет вам исследовать лучший тип экземпляра для данной рабочей нагрузки.

Обязательно попробуйте его.