Архитектура кластера Kubernetes
Кластер - это группа машин, которые работают как одна и сами раскладывают по себе приложения; каждая машина в нём называется узлом, а запущенное приложение живёт в поде. Поднял настоящий кластер и удалил из него под руками. Через 2125 миллисекунд рабочих подов снова стало два - никто ничего не запускал, кластер сам вернул описанное состояние. Имя у нового пода при этом другое: старый ушёл навсегда, вместо него появился новый.
Стержень: кластер это не запускалка контейнеров, а цикл сверки - он непрерывно сравнивает «что описано» с «что есть» и устраняет разницу.
// Формулировки: «из чего состоит кластер?», «что происходит после kubectl apply?», «зачем нужен под, если есть контейнер?»
Цикл сверки вместо команд
Вы не говорите кластеру «запусти контейнер». Вы записываете желаемое состояние: должно быть два экземпляра такого-то образа (образ - это неизменяемая заготовка приложения со всем нужным внутри). Кластер принимает эту запись, хранит её и дальше сам следит, чтобы реальность ей соответствовала.
Отсюда и мой замер. Я удалил под, и через две секунды его место занял новый - потому что в записи по-прежнему стоит «два экземпляра», а по факту остался один. Ровно так же кластер поступает при падении узла, при вылете процесса, при любом расхождении. Никакой автоматики поверх писать не надо, это и есть его основная работа.
// Отсюда важное следствие для проектирования: поды одноразовы. Имя нового пода в моём замере отличалось от старого, адрес тоже сменился. Значит, нельзя ни привязываться к имени пода, ни хранить внутри него данные, ни считать его адрес постоянным.
- желаемое состояние
- запись о том, что должно быть; кластер приводит реальность к ней
- цикл сверки
- непрерывное сравнение описанного с фактическим и устранение разницы
- одноразовый под
- его в любой момент могут заменить новым с другим именем и адресом
Из чего кластер состоит
Управляющая часть отвечает за решения. Точка входа принимает все запросы, проверяет права и записывает изменения в хранилище - именно с ней разговаривает командная строка и все компоненты. Хранилище держит всё желаемое состояние кластера; потеря его означает потерю кластера. Планировщик решает, на какой узел поставить новый под. Набор контроллеров следит, чтобы фактическое совпадало с желаемым, - это те самые циклы сверки.
Рабочая часть исполняет. На каждом узле живёт агент, который получает задание и запускает контейнеры через среду выполнения, а также гоняет проверки состояния и докладывает результат наверх. Рядом - сетевая часть, которая раздаёт адреса и правила.
// Отсюда полезная привычка при разборе поломок - идти по этой цепочке. Под в состоянии ожидания - вопрос к планировщику: не нашлось узла. Под создан, но контейнер не поднимается - вопрос к агенту узла и к образу. Изменение не применилось вовсе - вопрос к правам на точке входа. Каждый симптом имеет свой адрес.
- точка входа
- единственный вход в кластер: принимает запросы, проверяет права, пишет в хранилище
- планировщик
- решает, на какой узел поставить под
- агент узла
- запускает контейнеры на своей машине и докладывает их состояние
Под и цепочка владения
Под - это группа контейнеров, которые всегда живут вместе: на одном узле, с общим сетевым адресом и общими томами (том - это хранилище данных, подключённое снаружи и живущее отдельно от контейнера). Чаще всего в нём один рабочий контейнер, иногда рядом ставят вспомогательный - сборщик логов или прокси. Обращаться к контейнеру напрямую нельзя, кластер оперирует подами.
Сам под никто не создаёт руками. Над ним стоит цепочка: развёртывание описывает желаемую версию и число экземпляров, оно создаёт набор реплик, а тот уже создаёт поды. В моём кластере это видно буквально: одно развёртывание, один набор реплик, два пода.
// Зачем промежуточный уровень: набор реплик привязан к КОНКРЕТНОЙ версии образа. При обновлении развёртывание создаёт новый набор и постепенно переводит поды в него, а старый оставляет пустым. Поэтому откат делается мгновенно - достаточно снова наполнить старый набор.
- под
- группа контейнеров с общим адресом и томами, всегда на одном узле
- набор реплик
- держит заданное число подов одной конкретной версии
- развёртывание
- управляет наборами реплик: обновляет версию и умеет откатывать
Как отвечать: «Что происходит после того, как вы применили описание?»
Описание уходит в точку входа кластера: там проверяются права и формат, после чего запись ложится в хранилище желаемого состояния. Дальше начинают работать контроллеры. Контроллер развёртывания видит новую запись и создаёт набор реплик, тот создаёт поды. Планировщик подбирает каждому поду узел по запросам ресурсов. Агент на этом узле забирает задание, тянет образ и запускает контейнер, а потом докладывает состояние наверх. И всё это - непрерывный цикл сверки, а не разовая команда: я специально удалял под руками, и через две секунды кластер поднял новый, потому что в записи по-прежнему требовалось два экземпляра. Поэтому и разбор поломок идёт по той же цепочке: под в ожидании - к планировщику, контейнер не стартует - к агенту узла и образу, ничего не произошло - к правам.
Ответ проходит весь путь и заканчивается тем, как эта же цепочка используется при разборе аварий. Замер с удалённым подом показывает, что человек это видел, а не читал.
На чём валятся
- −Считают кластер запускалкой контейнеров и не понимают, почему удалённый под возвращается.
- −Создают поды напрямую вместо развёртывания и лишаются и перезапуска, и обновления.
- −Привязываются к имени или адресу пода: они меняются при каждой замене.
- −Хранят данные внутри пода и теряют их при первом пересоздании.
- −Не знают про хранилище желаемого состояния и не делают его копий.
Проверьте себя
Пять вопросов из банка по этой подтеме. Всего их 8, остальные разбираются в тренажёре.
- Control plane недоступен: api-server не отвечает. Что происходит с уже работающими подами?A)Kubelet останавливает поды: без связи с мастером они считаются осиротевшимиB)Поды переезжают на ноды, которые ещё видят мастерC)Поды продолжают работать, но пропадает балансировка через сервисыD)Продолжают работать: kubelet держит их, а изменения и новые выкатки встают
показать ответ и разбор
+D)Продолжают работать: kubelet держит их, а изменения и новые выкатки встают// разбор: Рабочая нагрузка живёт на нодах: kubelet перезапускает упавшие контейнеры по локально известной спецификации, kube-proxy держит уже настроенные правила, трафик ходит. Ломается управление — новые деплои, масштабирование, реакция на падение ноды, kubectl. Потому отказ control plane тяжёлый, но не мгновенно фатальный: главное успеть починить до первой аварии на нодах.
- В кластере из трёх узлов etcd два вышли из строя. Что будет с кластером?A)Кворум потерян: запись невозможна, состояние замираетB)Оставшийся узел станет лидером и продолжит принимать записиC)Кластер перейдёт в режим кэша и синхронизируется после возврата узловD)Ничего: api-server держит состояние у себя и работает автономно
показать ответ и разбор
+A)Кворум потерян: запись невозможна, состояние замирает// разбор: etcd — хранилище всего состояния кластера с алгоритмом консенсуса: для записи нужен кворум, то есть большинство узлов. Из трёх переживают потерю одного; два выпавших узла означают отсутствие большинства — запись отвергается, а api-server остаётся с доступом на чтение. Отсюда правила: нечётное число узлов, разнос по зонам и регулярный снапшот, потому что бэкап etcd и есть бэкап кластера.
- Кто в Kubernetes решает, на какую ноду поедет под, а кто его там запускает?A)Scheduler и выбирает ноду, и сам запускает под на нейB)Scheduler назначает ноду, kubelet на ней запускает подC)kubelet выбирает ноду, а scheduler уже запускает контейнерыD)api-server напрямую раскидывает поды по нодам сам
показать ответ и разбор
+B)Scheduler назначает ноду, kubelet на ней запускает под// разбор: kube-scheduler подбирает ноду для Pending-пода (пишет nodeName в спеку) по requests, affinity и taints. kubelet на этой ноде тянет образы, запускает контейнеры и репортит статус. api-server лишь хранит желаемое состояние в etcd и не занимается размещением. Разделение ответственности: scheduler решает «где», kubelet делает «запустить».
- Ноды и приложения работают, но потеряли данные etcd, а бэкапа нет. Насколько это критично?A)Потеряно всё состояние кластера — по сути это его потеряB)Некритично: kubelet на каждой ноде хранит копию состоянияC)api-server восстановит состояние из логов запущенных подовD)Потеряются только метрики, рабочие нагрузки не пострадают
показать ответ и разбор
+A)Потеряно всё состояние кластера — по сути это его потеря// разбор: etcd хранит всё состояние кластера: объекты, спеки, RBAC, секреты. Ноды какое-то время крутят поды по локальному виду, но желаемое состояние и все определения есть только в etcd. Потеря etcd без бэкапа = невозможность восстановить кластер, даже если нагрузки ещё работают. Отсюда правило: регулярные снапшоты etcd (etcdctl snapshot save).
- Задеплоили валидирующий admission-webhook. Он лёг — и кластер перестал создавать поды. Почему и как избежать?A)Вебхук занял все соединения api-server, и тот перестал отвечатьB)Упавший вебхук каскадом удалил уже существующие поды кластераC)При failurePolicy=Fail упавший вебхук отклоняет create-операцииD)Поды не создаются, потому что вебхук держал их манифесты у себя
показать ответ и разбор
+C)При failurePolicy=Fail упавший вебхук отклоняет create-операции// разбор: Admission-вебхуки вызывает api-server на матчащих create/update. При failurePolicy: Fail, если эндпоинт вебхука недоступен, api-server отклоняет операцию — мёртвый вебхук блокирует все подходящие create. Смягчение: сузить namespaceSelector/objectSelector, исключить kube-system, задать разумный timeoutSeconds, поднять вебхук в HA, а где допустимо — failurePolicy: Ignore.
дальше
Теорию прочитали. Навык ставится повторением
В Сеньорчике эта подтема идёт в ежедневных сессиях: движок возвращает её, пока ответы не станут уверенными, и ведёт прогресс отдельно по каждой подтеме. Теория внутри тоже бесплатна, лимит только на количество вопросов в день.