Kubernetes просто, часть 1: зачем Kubernetes, устройство кластера

Но что происходит, когда нагрузка растет? Приходится масштабироваться - и здесь уже начинаются сложности.
Допустим, что нагрузка выросла настолько, что вместо одного контейнера backend их уже четыре:

К тому же, работать они должны не на одном сервере, а на нескольких одновременно.

В таком случае, несомненно возникнет целый ряд вопросов:
1) Если образ на одном из серверов умер, кто его перезапустит на другом сервере?
2) Как производить дальнейшее масштабирование с 4 серверов, скажем, на 20?
3) Как обновить backend с v1 до v2 не положив работу сервиса целиком?
Именно для решения таких задач и нужен Kubernetes.
Что такое Kubernetes?
Говоря простым языком,
Kubernetes - это система управления контейнеризированными приложениями на множестве машин.
Хотя, само по себе это определение мало чего дает, гораздо важнее понять простую идею:
В Kubernetes вместо того, чтобы говорить системе
Запусти контейнер здесь;
Потом еще один там;
Если контейнер упадет - перезапусти;
Сервер умер - перенеси приложение;
Мы обычно описываем просто желаемый результат, так называемый Desired state:
Условно:
backend
replicas: 3
version: 2.0
CPU: 500m
RAM: 2Gi
Что по смыслу означает: «Я хочу, чтобы у меня постоянно работало 3 экземпляра backend версии 2.0, с такими требованиями и ресурсами»
После этого Kubernetes уже сам следит за тем, что происходит в кластере и от вас практически ничего не требуется.
Скажем, мы захотели запустить три экземпляра контейнера:
Desired State: 3 Pods
Но сейчас работают только 2:

Kubernetes видит несоответствие:
Desired State: 3 Pods
Actual State: 2 Pods
Здесь появилось новое для нас Actual State - это текущее состояние кластера.
Теперь Kubernetes попытается снова привести систему к желаемому:
Desired State: 3 Pods
Actual State: 3 Pods
Это и есть ключевая идея всей системы - она находится в постоянных попытках привести Actual State к Desired State.
Весь этот процесс называется reconciliation loop (рус. цикл согласования)
Идея согласования является основой для функционирования многих механизмов Kubernetes и к ней мы вернемся еще не раз.
Что такое Kubernetes-кластер?
Теперь можно перейти к архитектуре:
Мы уже говорили о том, что предназначение Kubernetes - управление приложениями на множестве машин. Очевидно, что все они должны быть объединены в единую систему. Такая система и называется кластером.

Кластер состоит из машин под управлением Kubernetes. Каждая такая машина называется Node (рус. нода/узел)
Node
Node - машина, входящая в Kubernetes-кластер.
Нодой может быть:
1) физический сервер
2) виртуальная машина
3) облачный инстанс
Характеристики отдельных узлов, конечно, могут быть разными:

Для Kubernetes - совокупность машин в кластере это пул вычислительных ресурсов по которым он будет распределять приложения.
Но сами узлы бывают двух ролей и чтобы понять их четче - лучше разделить дальнейшее рассмотрение на две большие части

В упрощенном виде кластер делится на две роли
Control Plane;
Worker Nodes.
Control Plane - управляющая часть Kubernetes.
Она отвечает не за содержание в себе контейнеров приложений, а за управление состоянием кластера.
В ней принимаются такие решения, как:
1) какие объекты должны существовать?
2) сколько экземпляров приложений должно быть запущено?
3) на какой Node поставить новый под приложения?
4) Нужно ли создавать новые поды?
5) Соответствует ли текущее состояние (actual state) желаемому (desired state)?
Если говорить коротко, control plane можно назвать «мозгом» кластера
Worker Node
Worker Node - машина, на которой непосредственно работают приложения
Именно здесь в итоге оказываются ваши контейнеры.
То есть, общая цепочка выглядит так:

Как видите на схеме, над контейнером стоит еще одна «оболочка» - Pod. Это важный термин, давайте обсудим его подробнее.
Pod
Для новичка естественно думать, что Kubernetes запускает контейнеры.
Это не совсем ошибочно, но формулировка очень грубая.
Основная единица развертывания, которой оперирует Kubernetes - Pod (под).
Простейший Pod можно представить так:

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

Такие контейнеры друг к другу находятся очень близко и могут использовать совместно часть ресурсов, это называется multi-container Pods, сегодня разбирать подробно мы их не будем.
На данный момент важно запомнить одну мысль, Kubernetes запускает контейнеры внутри подов.
Сам под является минимальной единицей развертывания для Kubernetes.
Что находится в Control Plane?
Сейчас у нас следующая общая картина:

Из чего состоит сам Control Plane?
Несколько упрощенно имеем следующее:

У каждого из этих компонентов своя роль. Для полного понимания рассмотрим подробнее каждый из них.
API Server
Представим, что мы хотим что-либо изменить в кластере, к примеру, в дальнейшем мы часто будем пользоваться командой:
kubectl apply -f deployment.yaml
Утилита kubectl не управляет нодами напрямую.
От неё запрос идет к Kubernetes API.
Итак, API-сервер - центральная точка взаимодействия с Kubernetes API.
Через него:
пользователь создаёт объекты;
компоненты Kubernetes читают состояние кластера;
компоненты записывают изменения состояния;
внешние инструменты взаимодействуют с Kubernetes.
Легко запомнить, API-сервер - входная дверь в Kubernetes.
etcd
Kubernetes должен хранить огромное количество информации.
К примеру:
В кластере есть Node 1
В кластере есть Node 2
Нужно 3 реплики backend
Существуют такие-то Pods
Для всей этой информации нужно иметь удобное и быстрое хранилище, которое будет работать независимо от перезапусков Control Plane.
В качестве такого постоянного хранилища в Kubernetes используется хранилище etcd.
etcd - распределённое key-value хранилище, в котором Kubernetes хранит состояние своего API.
Его можно представить так:

При этом важно понимание, с etcd работают практически все компоненты Control Plane, но они делают это не напрямую. Они обращаются к нему через API Server.
Scheduler
Задача Scheduler - выбрать подходящую Node для размещения Pod, которому еще не назначена машина .
Здесь важно не перепутать 2 важные вещи:
Scheduler не запускает контейнер - он лишь решает где он будет запущен. До вопроса, кто же занимается развертыванием подов на отдельных машинах мы дойдем уже скоро.
Controllers
Вернемся к примеру:
Desired State : 3 Pods
Actual State : 2 Pods
Кто замечает несоответствие? - Контроллеры
Они постоянно наблюдают за состоянием кластера и замечают несоответствия в желаемом состоянии и действительном предпринимая действия по их согласованию. Они и выполняют reconciliation loop, о котором мы говорили выше.
Что находится на Worker Node?
Теперь вернемся к Worker Node:
Scheduler уже определил:
Pod A запустить на Node 2
Но сам планировщик не подключается и не развертывает поды и контейнеры.
На каждой ноде в кластере для этого работает специальный агент - kubelet .

kubelet - агент Kubernetes, который работают на конкретной ноде.
Он следит за тем, чтобы поды, назначенные для этой машины были запущены и соответствовали своей спецификации.
Получается разделение ответственности: планировщик решает на какой ноде запустить под, а kubelet на этой конкретной ноде занимается отслеживанием корректной работы этих подов и контролем их запуска.
Конкретно запуском контейнеров в каждой Worker Node занимается контейнерный движок containerd и общая схема выглядит следующим образом:

Соберем архитектуру воедино

Теперь разберем, что происходит, когда мы говорим: “запусти три backend”
Шаг 1. Пользователь отправляет запрос
Мы сообщаем Kubernetes желаемое состояние.
Позже это будет выглядеть примерно так:
replicas: 3
Запрос проходит:
kubectl -> API Server
API Server принимает и обрабатывает изменение состояния.
Шаг 2. Желаемое состояние сохраняется
Теперь Kubernetes знает:
desired replicas = 3
Это состояние сохраняется в API и в конечном счёте хранится в etcd.
То есть кластер теперь «помнит»:
Для этого приложения должно существовать три экземпляра.
Важно, что это именно описание результата, а не одноразовая команда «создай три Pod».
Шаг 3. Controllers замечают несоответствие
Допустим, пока не существует ни одного Pod:
Desired: 3
Actual: 0
Контроллер видит, что желаемое состояние не достигнуто.
В результате в системе появляются необходимые объекты Pods.
Пока они существуют логически, но ещё не знают, на каких Node будут работать.
Получается:
Pod A -> ?
Pod B -> ?
Pod C -> ?
Шаг 4. Scheduler выбирает Nodes
Scheduler замечает Pods, которым ещё не назначена Node.
После анализа ресурсов и других правил он может принять, например, такое решение:
Pod A -> Node 1
Pod B -> Node 2
Pod C -> Node 2
Теперь каждый Pod знает, на какой Node он должен выполняться.
Но сами контейнеры всё ещё нужно запустить.
Шаг 5. kubelet выполняет Pod на Node
kubelet на Node 1 видит:
Pod A должен работать здесь
kubelet на Node 2 видит:
Pod B должен работать здесь
Pod C должен работать здесь
Дальше kubelet взаимодействует с container runtime, чтобы необходимые контейнеры были созданы и запущены.
Теперь:
Desired: 3
Actual: 3
На этом reconciliation loop не заканчивается.
Kubernetes продолжает следить за системой.
Именно поэтому следующий сценарий особенно показателен.
Что будет, если Node умрет?
Представим, что Node 2 упал вместе со всеми подами, тогда они перестанут быть доступными и тогда Actual State снова уменьшится, а контроллер заметит несоответствие и цикл, описанный сверху повторится. Выполнится reconciliation loop.
Итог части 1
На текущем этапе не нужно запоминать десятки команд и объектов.
Сейчас достаточно четко осознавать схему:

Проверь себя
Перед следующей частью полезно убедиться, что архитектура действительно уложилась.
Зачем Kubernetes может понадобиться, если у нас десять серверов и сотни контейнеров?
Что такое Pod и чем он отличается от контейнера?
За что отвечает API Server?
Что хранится в etcd?
Кто решает, на какой Node поставить новый Pod?
Кто на самой Worker Node следит за назначенными ей Pods?
Что такое reconciliation?
Почему после падения Node Kubernetes может восстановить потерянные экземпляры приложения?
В чём разница между Scheduler и kubelet?
Почему Kubernetes удобнее мыслить через Desired State, а не через последовательность команд?
В следующей части перейдём от архитектуры к взаимодействию с кластером: разберём декларативный подход, YAML-манифесты, kubectl, Deployment, ReplicaSet, labels и selectors
KioskNews shows a cleaned-up reading view extracted from the publisher’s page — the original always lives on their site, not ours.