Архитектура Kubernetes: компоненты Control Plane и узлов инфраструктуры
Вы вводите в терминале команду kubectl run web --image=nginx и нажимаете Enter. Через пару секунд терминал сообщает: pod/web created. Для пользователя это выглядит как мгновенная магия, но под капотом разворачивается сложнейшая хореография распределенной системы. Десятки компонентов обмениваются сертификатами, опрашивают базы данных, оценивают свободную оперативную память на серверах и настраивают правила маршрутизации ядра Linux. Понимание того, кто именно, в какой последовательности и на каком сервере выполняет эту работу — фундаментальный навык для сдачи экзамена CKA и ежедневного администрирования Kubernetes.
Kubernetes не является монолитным приложением. Это набор независимых бинарных файлов (процессов), которые общаются друг с другом исключительно через REST API. Концептуально кластер разделен на две большие части: Control Plane (плоскость управления, или управляющие узлы) и Worker Nodes (рабочие узлы).
Control Plane принимает глобальные решения по кластеру (например, на каком узле запустить приложение), обнаруживает события и реагирует на них. Worker Nodes — это «рабочие лошадки», которые непосредственно запускают контейнеры с вашим кодом и обеспечивают их сетевую доступность.
Компоненты Control Plane: мозг кластера
В Production-средах компоненты Control Plane обычно разворачиваются на нескольких выделенных серверах для обеспечения отказоустойчивости (High Availability). Если падает один управляющий узел, кластер продолжает работу. В рамках подготовки к CKA важно досконально знать зону ответственности каждого из четырех главных компонентов Control Plane.
kube-apiserver: центральный узел связи
kube-apiserver — это фасад всего кластера. Это единственный компонент, с которым взаимодействует администратор (через утилиту kubectl), и единственный компонент, который имеет право напрямую читать и писать в базу данных состояния кластера (etcd).
Все остальные компоненты, будь то планировщик, контроллеры или агенты на рабочих узлах, не общаются друг с другом напрямую. Они постоянно опрашивают API-сервер (механизм watch) на предмет изменений.
Ключевые функции kube-apiserver:
- Аутентификация и авторизация: проверка того, кто прислал запрос и есть ли у него права на выполнение действия (RBAC).
- Мутация и валидация (Admission Control): перед сохранением объекта в базу данных API-сервер может изменить его (например, автоматически добавить дефолтные лимиты ресурсов) или отклонить (например, если образ контейнера скачивается из неразрешенного реестра).
- Маршрутизация к etcd: трансляция REST-запросов в операции с базой данных.
API-сервер спроектирован так, чтобы масштабироваться горизонтально. Он не хранит состояние внутри себя (stateless). Вы можете запустить 2, 3 или 10 экземпляров kube-apiserver за балансировщиком нагрузки, и они будут работать корректно, так как единым источником истины выступает внешнее хранилище. По умолчанию API-сервер слушает защищенный порт 6443.
etcd: память кластера
Если kube-apiserver — это мозг, то etcd — это его память. Это распределенное, строго согласованное (strongly consistent) хранилище типа «ключ-значение» (key-value store). В нем хранится абсолютно всё состояние кластера: какие узлы подключены, какие поды должны работать, какие секреты и конфигурации существуют.
Для обеспечения отказоустойчивости etcd работает в кластерном режиме (обычно 3 или 5 узлов) и использует алгоритм консенсуса Raft. Чтобы кластер etcd мог принимать решения (например, записывать новые данные), необходимо, чтобы большинство узлов (кворум) были живы и могли договориться. Формула кворума: , где — общее количество узлов etcd. При трех узлах кворум равен 2. Если два узла из трех выйдут из строя, etcd перейдет в режим «только чтение», и API-сервер Kubernetes не сможет создавать или изменять объекты.
Особенности etcd в контексте администрирования:
- Слушает порт 2379 для клиентских запросов (от API-сервера) и порт 2380 для общения узлов
etcdмежду собой. - Хранит данные в виде бинарного дерева (BboltDB).
- Является самым критичным компонентом для резервного копирования. Потеря данных
etcdозначает полную потерю кластера Kubernetes, даже если все контейнеры на рабочих узлах продолжают физически работать.
kube-scheduler: логистика и планирование
Когда вы просите Kubernetes создать Pod (минимальную единицу развертывания), API-сервер просто сохраняет запись об этом в etcd. У этого Pod'а в поле nodeName (имя узла) изначально пусто.
kube-scheduler — это компонент, который непрерывно следит за API-сервером в поисках Pod'ов без назначенного узла. Как только он находит такой Pod, его задача — выбрать оптимальный сервер для запуска.
Процесс выбора узла состоит из двух этапов:
- Фильтрация (Filtering / Predicates): планировщик отбрасывает узлы, которые физически не могут принять Pod. Например, если Pod требует 2 ГБ оперативной памяти, а на узле свободно только 1 ГБ, этот узел исключается. Также на этом этапе проверяются ограничения, заданные администратором (Taints, Node Selectors, Affinity).
- Оценка (Scoring / Priorities): оставшиеся после фильтрации узлы получают баллы от 0 до 100. Планировщик оценивает, где Pod будет работать эффективнее. Например, узел, на котором останется больше свободных ресурсов после размещения Pod'а, получит более высокий балл. Также учитывается распределение Pod'ов одного приложения по разным узлам для отказоустойчивости.
Узел с максимальным баллом выбирается победителем. Планировщик отправляет API-серверу запрос на обновление Pod'а (операция Binding), вписывая имя выбранного сервера в поле nodeName. На этом работа планировщика заканчивается — он не запускает контейнеры.
kube-controller-manager: поддержание желаемого состояния
В Kubernetes применяется декларативный подход: вы описываете желаемое состояние системы (Desired State), а кластер сам приводит текущее состояние (Current State) к желаемому. За эту работу отвечает kube-controller-manager.
Это единый бинарный файл, внутри которого в виде отдельных потоков (goroutines) работают десятки различных контроллеров. Каждый контроллер отвечает за свой тип объектов. Они работают в бесконечном цикле (Control Loop), постоянно сравнивая желаемое состояние с текущим. Если , контроллер предпринимает действия для устранения разницы.
Ключевые контроллеры внутри менеджера:
- Node Controller: следит за состоянием рабочих узлов. Если
kubeletна узле перестает присылать сигналы жизни (heartbeats), Node Controller ждет определенное время (по умолчанию 40 секунд), помечает узел какNotReady, а еще через 5 минут начинает процесс принудительного удаления (eviction) Pod'а с этого узла. - ReplicaSet Controller: гарантирует, что в кластере запущено строго заданное количество копий (реплик) Pod'а. Если вы указали 3 реплики, а один узел сгорел вместе с Pod'ом, контроллер увидит, что текущее количество равно 2, и создаст запрос на запуск нового Pod'а.
- Endpoints Controller: связывает сервисы (Services) и Pod'ы, обновляя списки IP-адресов, на которые нужно направлять трафик.
- Service Account & Token Controllers: создают учетные записи по умолчанию и токены доступа для новых пространств имен (Namespaces).
Компоненты Worker Node: исполнители на местах
Рабочие узлы — это серверы, на которых выполняется полезная нагрузка. Компоненты на этих узлах отвечают за поддержание работы Pod'ов и обеспечение сетевой связности.
kubelet: капитан узла
kubelet — это главный агент Kubernetes, который работает на каждом узле кластера (включая управляющие узлы). В отличие от большинства других компонентов, которые запускаются в виде контейнеров, kubelet обычно устанавливается как системный сервис (systemd daemon) прямо в операционную систему сервера.
Его главная задача — убедиться, что контейнеры, описанные в спецификации Pod'а, запущены и здоровы.
Как работает kubelet:
- Он слушает API-сервер. Когда
kube-schedulerназначает Pod на его узел,kubeletскачивает спецификацию этого Pod'а (PodSpec). - Он обращается к Container Runtime (среде выполнения контейнеров) и дает команду: «скачай такой-то образ и запусти контейнер с такими-то параметрами».
- Он постоянно мониторит состояние запущенных контейнеров (выполняет Liveness и Readiness пробы) и регулярно отправляет отчет о статусе узла и Pod'ов обратно на API-сервер.
Важный нюанс для экзамена CKA: kubelet может запускать Pod'ы не только по команде от API-сервера. Если положить YAML-манифест Pod'а в специальную директорию на сервере (обычно /etc/kubernetes/manifests/), kubelet прочитает файл и запустит так называемый Static Pod. Именно таким образом утилита kubeadm разворачивает компоненты Control Plane (kube-apiserver, etcd, kube-scheduler, kube-controller-manager) — как статические поды, которыми управляет локальный kubelet на мастер-узле.
Container Runtime: двигатель контейнеров
Сам по себе Kubernetes не умеет запускать контейнеры. Ему нужен сторонний софт, который умеет работать с пространствами имен Linux (namespaces) и контрольными группами (cgroups). Для стандартизации взаимодействия kubelet с различными средами выполнения был создан интерфейс CRI (Container Runtime Interface).
Исторически Kubernetes использовал Docker, но сейчас поддержка Docker как среды выполнения (Dockershim) удалена. В современных кластерах используются среды, напрямую реализующие CRI:
- containerd: изначально часть Docker, теперь самостоятельная, легковесная и невероятно стабильная среда выполнения. Является стандартом де-факто в большинстве современных инсталляций.
- CRI-O: среда выполнения, разработанная Red Hat специально для Kubernetes, строго следующая стандартам OCI (Open Container Initiative).
Container Runtime отвечает за стягивание образов из реестров (например, Docker Hub), распаковку слоев, настройку изоляции на уровне ядра ОС и непосредственный запуск процессов.
kube-proxy: сетевой регулировщик
Каждый Pod в Kubernetes получает свой собственный IP-адрес. Однако Pod'ы эфемерны: они удаляются, пересоздаются на других узлах и получают новые IP-адреса. Чтобы приложения могли стабильно общаться друг с другом, используются Сервисы (Services) — абстракции, предоставляющие постоянный виртуальный IP-адрес (ClusterIP), балансирующий трафик на группу динамических Pod'ов.
За реализацию этой магии отвечает kube-proxy. Это сетевой агент, работающий на каждом узле.
Он следит за API-сервером на предмет создания новых Сервисов или изменения Endpoints (IP-адресов Pod'ов). Когда происходит изменение, kube-proxy не маршрутизирует трафик сам (он не является прокси-сервером в классическом понимании). Вместо этого он программирует сетевой стек ядра Linux на своем узле.
kube-proxy может работать в нескольких режимах:
- iptables (по умолчанию): создает огромное количество правил в брандмауэре Linux. Когда пакет пытается уйти на виртуальный IP Сервиса, ядро Linux перехватывает его на уровне iptables, случайным образом выбирает IP-адрес одного из живых Pod'ов и подменяет адрес назначения (DNAT).
- IPVS: более современный и производительный режим. Использует технологию балансировки нагрузки внутри ядра Linux (IP Virtual Server). В отличие от iptables, который читает правила последовательно (что замедляет работу в кластерах с тысячами сервисов), IPVS использует хеш-таблицы, обеспечивая мгновенный поиск маршрута.
Симфония компонентов: жизненный цикл создания Pod'а
Чтобы по-настоящему понять архитектуру, нужно проследить, как компоненты взаимодействуют во времени. Рассмотрим пошагово, что происходит после выполнения команды kubectl run.
- Инициация:
kubectlформирует JSON-объект, описывающий Pod, и отправляет POST-запрос на порт 6443 компонентуkube-apiserver. - Аутентификация и валидация: API-сервер проверяет ваши сертификаты. Если у вас есть права, запрос пропускается через Admission Controllers.
- Сохранение состояния: API-сервер записывает объект Pod в
etcd. На этот момент статус Pod'а —Pending(в ожидании), а полеnodeNameпустое. API-сервер отвечаетkubectl, что Pod успешно создан (хотя контейнеры еще не запущены). - Реакция планировщика:
kube-schedulerчерез механизм watch видит новый Pod без узла. Он выполняет фильтрацию и оценку, выбирает, например,worker-node-2, и отправляет API-серверу запрос на привязку (Binding). - Обновление базы: API-сервер обновляет запись в
etcd, указывая, что Pod теперь привязан кworker-node-2. - Реакция kubelet:
kubeletна узлеworker-node-2через свой watch-канал замечает, что ему назначен новый Pod. - Запуск:
kubeletобращается к Container Runtime (например, containerd) через интерфейс CRI с командой запустить контейнеры. - Обратная связь: Container Runtime запускает процесс и сообщает об успехе.
kubeletотправляет API-серверу обновление статуса: Pod переходит в состояниеRunning. API-сервер сохраняет этот статус вetcd.
На протяжении всего этого процесса ни один компонент не общался с другим напрямую. Все взаимодействия происходили исключительно через kube-apiserver.
Диагностика компонентов: взгляд администратора
На экзамене CKA вам часто придется сталкиваться со сломанными кластерами. Понимание архитектуры позволяет быстро локализовать проблему.
Если вы создаете Deployment, но Pod'ы даже не появляются в списке (kubectl get pods ничего не выдает), проблема скорее всего в kube-controller-manager. Именно он должен был увидеть Deployment и создать ReplicaSet, который в свою очередь создал бы Pod'ы.
Если Pod'ы появились, но висят в статусе Pending, это классический симптом проблем с kube-scheduler (он не работает) или нехватки ресурсов в кластере (планировщик работает, но фильтрация отбросила все узлы).
Если Pod назначен на узел (поле NODE в выводе kubectl get pods -o wide заполнено), но статус застрял на ContainerCreating, проблема находится на конкретном рабочем узле. Виноват либо kubelet (не может связаться с API-сервером), либо Container Runtime (не может скачать образ из интернета), либо сетевой плагин CNI (не может выдать IP-адрес).
В кластерах, развернутых с помощью kubeadm, компоненты Control Plane работают как статические поды в пространстве имен kube-system. Вы можете проверить их статус обычной командой:
kubectl get pods -n kube-system
Если API-сервер недоступен, команда kubectl перестанет работать. В этом случае диагностику нужно проводить прямо на сервере Control Plane. Так как kubelet управляет статическими подами, его логи — первый источник правды. На узле с ОС Linux это делается командой systemd:
journalctl -u kubelet -f
Также можно проверить статус контейнеров компонентов напрямую через утилиту crictl (клиент командной строки для CRI), которая работает в обход Kubernetes:
crictl ps
Архитектура Kubernetes, несмотря на кажущуюся сложность, построена на элегантном принципе разделения обязанностей и декларативного управления состоянием. Понимание роли каждого винтика в этом механизме — от kube-apiserver до kube-proxy — превращает кластер из «черного ящика» в предсказуемую и управляемую систему, готовую к любым операционным вызовам.