Поды и контроллеры Kubernetes
Поднял в кластере рядом два вида нагрузки. Под тут - это запущенный экземпляр приложения, а реплика - один из нескольких одинаковых таких экземпляров. У обычного развёртывания поды получили имена stateless-799d7679bb-czgb6 и stateless-799d7679bb-s2qwl - случайные хвосты. У набора с состоянием имена оказались stateful-0 и stateful-1. Удалил stateful-0 - вернулся под с ТЕМ ЖЕ именем. Удалил под развёртывания - пришёл новый со случайным именем.
Стержень: вид нагрузки выбирается по тому, взаимозаменяемы ли экземпляры и нужен ли им личный диск и постоянное имя.
// Формулировки: «чем StatefulSet отличается от Deployment?», «когда нужен DaemonSet?», «как запускать разовые задачи?»
Взаимозаменяемые и именованные
Развёртывание рассчитано на одинаковые взаимозаменяемые экземпляры. Имена случайны, порядок запуска не гарантирован, поды поднимаются все разом. Это подходит приложениям без собственного состояния: любой экземпляр обслужит любой запрос.
Набор с состоянием даёт три вещи, которых нет у развёртывания. Стабильные имена по номерам, за счёт чего у каждого экземпляра есть постоянный сетевой адрес по имени. Личный том - хранилище данных снаружи пода, - который переживает пересоздание и подключается обратно к экземпляру с тем же номером. И порядок: поды поднимаются по очереди с нулевого, а гасятся в обратном порядке.
// Нужно это ровно там, где экземпляры НЕ взаимозаменяемы: базы с репликацией, очереди, кластеры с выбором главного. Для обычного веб-сервиса набор с состоянием - лишняя сложность: он медленнее обновляется и болезненнее переживает падение узла.
- развёртывание
- одинаковые взаимозаменяемые поды со случайными именами
- набор с состоянием
- стабильные имена по номерам, личные тома, строгий порядок запуска
По одному на узел и разовые задачи
Набор по узлам ставит ровно один под на каждый узел кластера и сам добавляет его на новые узлы. Это форма для инфраструктурных вещей: сборщик логов, агент мониторинга, сетевой компонент. Число реплик тут не задают - оно равно числу узлов по определению.
Разовая задача запускает под и следит, чтобы он ДОШЁЛ ДО КОНЦА успешно. Отличие от развёртывания принципиальное: там под, завершившийся с нулевым кодом, будет перезапущен, потому что желаемое состояние - «работает». В разовой задаче успешное завершение и есть цель. Ей задают число попыток при неудаче и предел времени.
// Задача по расписанию - та же разовая, но запускаемая по календарю. Две настройки, которые спасают от боли: политика на случай, когда предыдущий запуск ещё идёт (пропустить, запустить рядом или заменить), и предел времени выполнения. Без них повторяется классика планировщика: задача накладывается сама на себя, пока не кончатся ресурсы.
- набор по узлам
- ровно один под на каждом узле; для агентов и сборщиков
- разовая задача
- под должен успешно завершиться; завершение - это цель, а не сбой
Обновление и откат
При обновлении развёртывание не гасит всё разом. Оно создаёт новый набор реплик и переводит поды по чуть-чуть. Две настройки задают темп: сколько подов можно поднять СВЕРХ нормы и сколько можно держать недоступными. По умолчанию у меня стояло по 25% на обе.
Замерил, как это выглядит на четырёх репликах. Через 2 секунды после команды: готово 3 из 4, актуальной версии 2, доступно 3. Через 8 секунд: актуальных уже 4, доступно 3. Через 10 секунд: 4 из 4 готовы, все актуальные. Доступность ни разу не опустилась ниже трёх из четырёх - именно это и обещают настройки.
// Откат работает потому, что старый набор реплик не удаляется, а остаётся с нулём подов. Команда отката просто наполняет его обратно. История версий хранится, у меня после одного обновления в ней было две записи. ⚠Но обещание «без простоя» держится только при работающей пробе готовности - это регулярный запрос, по ответу на который кластер решает, можно ли пускать на под трафик. Без неё кластер считает под готовым сразу после старта процесса и переводит на него трафик раньше, чем приложение способно отвечать.
- максимум сверх нормы
- сколько подов разрешено поднять поверх заданного числа во время обновления
- максимум недоступных
- сколько подов разрешено держать недоступными во время обновления
Как отвечать: «Чем StatefulSet отличается от Deployment и когда он нужен?»
Тремя вещами, и все три я проверял на стенде. Стабильные имена: у развёртывания поды получили случайные хвосты, а у набора с состоянием - номера, ноль и один. Устойчивость этих имён: я удалил нулевой под, и вернулся под ровно с тем же именем, тогда как у развёртывания взамен удалённого пришёл под с новым именем. Плюс личный том, который переживает пересоздание и подключается обратно к экземпляру с тем же номером, и строгий порядок запуска и остановки. Нужно это там, где экземпляры не взаимозаменяемы: базы с репликацией, очереди, кластеры с выбором главного узла. Для обычного веб-сервиса это лишняя сложность - обновляется медленнее и хуже переживает падение узла, а взамен даёт свойства, которые сервису без состояния не нужны.
Ответ называет ровно три отличия с проверенными примерами и заканчивается тем, когда этого делать НЕ надо. Второе не менее важно: набор с состоянием часто ставят по привычке.
На чём валятся
- −Берут набор с состоянием для сервиса без состояния и получают медленное обновление.
- −Ждут от развёртывания стабильных имён подов.
- −Запускают разовые задачи развёртыванием: завершившийся под будет перезапущен.
- −Не ставят задаче по расписанию предел времени и политику наложения запусков.
- −Обещают обновление без простоя, не настроив пробу готовности.
Проверьте себя
Пять вопросов из банка по этой подтеме. Всего их 7, остальные разбираются в тренажёре.
- Реплик 4, maxSurge: 1, maxUnavailable: 0. Что это даёт при обновлении?A)Обновление идёт разом: все поды пересоздаются параллельноB)В момент выкатки живёт максимум 5 подов и не меньше 4 готовыхC)Один под простаивает, зато выкатка идёт вдвое быстрееD)Старые поды остаются, пока не пройдёт ручное подтверждение
показать ответ и разбор
+B)В момент выкатки живёт максимум 5 подов и не меньше 4 готовых// разбор: maxSurge — сколько подов сверх желаемого числа разрешено поднять, maxUnavailable — сколько готовых можно потерять. Нули по недоступности означают: сначала поднимаем новый под и ждём readiness, только потом гасим старый. Ёмкость под нагрузкой не проседает, но нужна свободная квота на ноде и выкатка идёт медленнее. Обратная настройка (surge 0, unavailable 1) экономит ресурсы ценой просадки.
- Под db-1 из StatefulSet удалили вручную. Что будет дальше?A)Вернётся под с тем же именем и тем же PVCB)Контроллер создаст под db-3, старый том останется висетьC)Реплика не восстановится, пока не пересоздать весь StatefulSetD)Под вернётся, но с новым пустым томом из шаблона
показать ответ и разбор
+A)Вернётся под с тем же именем и тем же PVC// разбор: Идентичность в StatefulSet держится на порядковом индексе: контроллер поднимет ровно db-1 и подключит его прежний PersistentVolumeClaim — данные на месте. PVC живут отдельно от подов и не удаляются вместе с ними (а по умолчанию остаются и после удаления самого StatefulSet). Отсюда типовая уборка после экспериментов: удалить набор и отдельно почистить тома.
- Разработчик создал голый Pod (не Deployment). Нода перезагрузилась — пода больше нет. Почему Deployment вёл бы себя иначе?A)Голый Pod без Deployment запускать в кластере вообще запрещеноB)Deployment хранит под на диске, поэтому тот переживает ребутC)Pod без Deployment не получает IP и потому и исчезаетD)Голый Pod никто не пересоздаёт; Deployment держит число реплик
показать ответ и разбор
+D)Голый Pod никто не пересоздаёт; Deployment держит число реплик// разбор: Голый Pod не управляется контроллером: если его нода умирает или его удаляют, пересоздавать некому. Deployment (через свой ReplicaSet) постоянно сводит фактическое число реплик к желаемому — умер под/нода, планируется замена на другой ноде. Это и есть самолечение, нужное сервисам. Голые поды — для разовых/отладочных задач.
- Нужно, чтобы агент сбора логов работал ровно по одному экземпляру на каждой ноде, включая новые. Какой контроллер?A)Deployment с числом реплик, равным текущему числу нодB)DaemonSet: по одному поду на ноду, на новых — автоматическиC)StatefulSet тоже ставит по поду на каждую нодуD)CronJob, который периодически раскладывает поды по нодам
показать ответ и разбор
+B)DaemonSet: по одному поду на ноду, на новых — автоматически// разбор: DaemonSet запускает ровно один под на каждой (матчащей) ноде и автоматически добавляет его на любую новую ноду — то, что нужно для узловых агентов (сбор логов/метрик, CNI, storage). Deployment с replicas=N не гарантирует один-на-ноду (планировщик может сложить их вместе) и не реагирует на смену числа нод; StatefulSet — про стабильную идентичность и хранилище, а не про раскладку по нодам.
- Ночной CronJob иногда идёт дольше часа, и запуски начинают наслаиваться, плодя параллельные джобы. Чем это управлять?A)concurrencyPolicy: Forbid/Replace — не поверх идущегоB)Уменьшить schedule, чтобы запуски шли реже одного в часC)Выставить backoffLimit: 0 — тогда параллель и пропадётD)Поднять activeDeadlineSeconds, чтобы джоба падала быстрее
показать ответ и разбор
+A)concurrencyPolicy: Forbid/Replace — не поверх идущего// разбор: Наслоение запусков CronJob регулирует concurrencyPolicy: Allow (по умолчанию) разрешает параллель; Forbid пропускает новый запуск, если предыдущий ещё идёт; Replace убивает текущий и стартует свежий. Для долгих джоб, которым нельзя пересекаться, — Forbid или Replace. startingDeadlineSeconds отвечает за пропущенные запуски, backoffLimit/activeDeadlineSeconds — за ретраи и таймаут, а не за конкуренцию.
дальше
Теорию прочитали. Навык ставится повторением
В Сеньорчике эта подтема идёт в ежедневных сессиях: движок возвращает её, пока ответы не станут уверенными, и ведёт прогресс отдельно по каждой подтеме. Теория внутри тоже бесплатна, лимит только на количество вопросов в день.