kubernetes kubectl

Словарик

ТерминЧто означает
Control plane («мастер»)Управляющий этаж кластера: принимает решения, приложений не запускает
ВоркерРабочий узел, на котором живут поды с приложениями
ПодМинимальная единица Kubernetes: контейнер (или несколько) со своим IP
pause-контейнерТехнический контейнер пода, удерживает сетевой неймспейс и IP
API-серверЕдинственная дверь в кластер, через нее проходит все общение
kubectlКонсольная утилита для общения с API-сервером
etcdБаза данных кластера: желаемое и текущее состояние
SchedulerВыбирает, на каком узле жить новому поду; сам ничего не запускает
КонтроллерыБесконечный цикл сверки желаемого с реальным
kubeletАгент на каждом узле: запускает поды, следит за ними, отчитывается
Container runtimeФизически запускает контейнеры; в современных кластерах containerd
kube-proxyСетевые правила узла: доставляет трафик Service до живых подов
ServiceПостоянный адрес перед сменяющимися подами, с балансировкой
CNIСлой сетевых плагинов, строящих общую сеть подов: Flannel, Calico, Cilium
Managed KubernetesКластер от облака: control plane спрятан и обслуживается провайдером

Архитектура Kubernetes

На прошлом шаге Kubernetes был черным ящиком: сказали «пять реплик», и он их держит. Теперь откроем крышку и посмотрим, кто внутри принимает решения, кто исполняет и как именно команда из одной строчки превращается в запущенные контейнеры на разных серверах. Знание это не академическое: «расскажите, что происходит после kubectl apply» один из самых частых вопросов на собеседованиях по Kubernetes, и к концу шага у вас будет на него ответ.

Два этажа кластера

Удобнее всего представлять кластер Kubernetes как компанию с управляющим этажом и рабочим. Управляющий этаж называется control plane (по-старому «мастер», и это название до сих пор повсюду в статьях и на собеседованиях). Рабочий этаж это воркеры, обычные серверы, на которых живут ваши приложения.

Разделение труда между этажами жесткое. Control plane только думает: хранит, что должно быть, замечает расхождения, решает, кому что делать, а ваши приложения на нем не работают. Воркеры только исполняют: запускают контейнеры и отчитываются о состоянии. Узел, о котором шла речь ранее, это и есть один сервер кластера, управляющий или рабочий.

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

Под как единица управления

Kubernetes не управляет контейнерами напрямую. Его минимальная единица это под: обертка вокруг контейнера с небольшой обвязкой, прежде всего с собственным IP-адресом.

Адрес держится не на пустом месте. Внутри пода всегда есть незаметный технический контейнер, его называют pause-контейнер: он первым стартует и удерживает сетевой неймспейс и IP, пока остальные контейнеры пода приходят и уходят. Поэтому в под можно положить несколько тесно связанных контейнеров, которые должны жить вместе и делить один адрес. На старте, впрочем, достаточно простой модели: под это контейнер плюс паспорт с адресом.

У подов есть свойство, которое стоит принять сразу: они смертны, и это нормально. Под не лечат и не чинят, упавший под просто пересоздается заново, уже с другим IP. Вся архитектура Kubernetes построена вокруг этой идеи: отдельный под ценности не представляет, ценность в том, чтобы нужное количество подов всегда существовало. Запомните это, к смертности подов мы вернемся в разговоре про сеть.

Control plane принимает решения

API-сервер, входная дверь

Это единственная дверь в кластер. Через API-сервер проходит вообще все: ваши команды, запросы пайплайна, общение внутренних компонентов между собой. Никто в кластере не разговаривает друг с другом напрямую, только через API-сервер. Такая централизация дает один источник правды и одну точку, где проверяются права: прежде чем что-то принять, API-сервер выясняет, кто пришел и можно ли ему это делать.

Человек общается с API-сервером через kubectl, консольную утилиту, которая переводит команды вроде kubectl apply в запросы к API. Когда говорят «общаться с кластером», имеют в виду именно это.

etcd, память кластера

Это база данных, в которой лежит все: каждый манифест, каждый под, каждая настройка, и желаемое состояние, и текущее. Если control plane мозг кластера, то etcd его память, и отношение к ней соответствующее: etcd регулярно бэкапят, потому что потерять ее значит потерять кластер. Без etcd кластер не помнит, что он должен делать.

Важная деталь устройства: в etcd ходит только API-сервер. Ни scheduler, ни kubelet, ни кто-либо еще прямого доступа к памяти не имеет, все читают и пишут через ту самую единственную дверь.

Scheduler, распределитель

Когда появляется новый под, кто-то должен решить, на каком из воркеров ему жить. Это работа scheduler. Он смотрит, сколько под просит ресурсов, сколько свободно на каждом узле, учитывает ограничения (например, «эти поды не селить на один узел») и выбирает машину.

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

Контроллеры, надзиратели

Та непрерывная сверка желаемого с реальным, о которой шла речь ранее, физически живет здесь. Контроллеры это набор процессов, каждый из которых бесконечно крутит один и тот же цикл: прочитать желаемое состояние, посмотреть на реальное, при расхождении действовать. Заявлено пять реплик, наблюдаю четыре, создаю пятую. Узел перестал выходить на связь, его поды нужно пересоздать на других. Этот цикл никогда не останавливается, поэтому кластер чинит себя и ночью, и в выходные.

Воркеры исполняют работу

kubelet, агент на каждом узле

На каждом воркере живет kubelet, главный работяга всей системы. Его жизнь устроена просто: он постоянно спрашивает у API-сервера «что должно работать на моем узле?», запускает то, чего не хватает, останавливает лишнее, следит за здоровьем своих подов и отчитывается наверх о состоянии.

У kubelet есть и локальные полномочия: если контейнер внутри пода упал, kubelet перезапустит его сам, на месте, не дожидаясь указаний сверху. Уровни самовосстановления получаются такие: умер контейнер, чинит kubelet на узле; умер целый узел, подключаются контроллеры control plane и пересоздают поды на других машинах.

Container runtime, кто физически запускает

Сам контейнеры kubelet не запускает, он командует. Физически контейнеры создает container runtime, в современных кластерах это containerd.

Здесь стоит снять путаницу, на которую вы наверняка наткнетесь в интернете: «из Kubernetes удалили Docker». Звучит страшно, на практике безобидно. containerd это сердцевина самого Docker, тот его кусок, который и занимается запуском контейнеров. Kubernetes просто стал использовать сердцевину напрямую, без остальной обвязки. Для вас не меняется ничего: образы, собранные обычным docker build, как запускались в кластерах, так и запускаются, ведь формат образов стандартный и общий.

kube-proxy, сетевой регулировщик

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

Что происходит после kubectl apply

Теперь соберем всех персонажей в одну историю. Вы написали манифест: приложение web из трех реплик nginx (такое описание называется Deployment, и в сокращенном виде оно выглядит так):

apiVersion: apps/v1
kind: Deployment
metadata:
  name: web
spec:
  replicas: 3
  template:
    spec:
      containers:
        - name: nginx
          image: nginx:1.27

И выполнили kubectl apply -f web.yaml. Дальше по шагам:

  1. kubectl отправляет манифест API-серверу. Тот проверяет права и корректность и записывает желаемое состояние в etcd. На этом ваша часть закончена: человек больше не нужен.
  2. Контроллер, следящий за такими описаниями, замечает расхождение: заявлено три пода, существует ноль. Он создает в etcd три записи о подах. Пока это только записи, у подов еще нет узлов, на которых они будут жить.
  3. Scheduler видит поды без назначенного узла. Для каждого он подбирает воркер по свободным ресурсам и записывает решение: первый под на воркер 1, второй на воркер 3, третий на воркер 1.
  4. Kubelet воркера 1, в очередной раз спросив «что должно работать у меня?», обнаруживает два новых пода. Он командует containerd скачать образ nginx.27 и запустить контейнеры. То же самое делает kubelet воркера 3 со своим подом.
  5. Kubelet’ы отчитываются наверх: поды запущены, живы. Статусы оседают в etcd, и kubectl get pods покажет вам три зеленых пода.
  6. История не заканчивается: контроллеры продолжают свой вечный цикл сверки.

Благодаря шагу 6 система реагирует на любые неприятности. Упал контейнер, kubelet перезапускает его прямо на узле, control plane даже не беспокоится. Умер воркер 1 целиком, его kubelet перестал выходить на связь, через таймаут контроллер помечает узел недоступным и пересоздает его поды, scheduler находит им новые узлы, чужие kubelet’ы их запускают. Заявленные три реплики снова существуют, и ни на одном шаге не участвовал человек.

Обратите внимание на то, как ровно устроена схема: ни один компонент не делает чужую работу. Контроллер не выбирает узлы, scheduler не запускает контейнеры, kubelet не принимает решений о количестве реплик. Каждый крутит свой маленький цикл, а вместе получается система, которая держит желаемое состояние.

Честно про сеть

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

Первая идея: у каждого пода свой IP-адрес, и все поды кластера видят друг друга по этим адресам напрямую, на каких бы узлах они ни жили. Поверх обычной сети серверов натягивается общая плоская сеть подов. Сама собой она не появляется, ее строит отдельный сетевой плагин (этот слой называется CNI, популярные представители: Flannel, Calico, Cilium). Выбор и настройка плагина одна из причин репутации кубернетесовской сети.

Вторая идея: помните, поды смертны? Под пересоздался, IP сменился. Значит, обращаться к подам по их адресам бессмысленно: сегодня тройка nginx живет на одних адресах, завтра на других. Эту проблему решает Service, постоянный адрес-вывеска, за которым стоят сменяющиеся поды:

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

Третья идея: превращением адреса Service в адреса реальных подов на каждом узле занимается тот самый kube-proxy. Он следит за списком живых подов и настраивает сетевые правила узла так, чтобы трафик доезжал куда надо.

За рамками остается вход трафика снаружи кластера (этот механизм называется Ingress) и все глубины плагинов, это уже территория буткемпа. Но знайте: сетевые вопросы по Kubernetes на собеседованиях любят именно потому, что по ним видно, понимает человек устройство кластера или заучил слова.

Где все это живет в реальности

Собирать кластер по кирпичику руками вам, скорее всего, не придется. В облаках есть managed Kubernetes (Yandex Managed Service for Kubernetes, Amazon EKS, Google GKE): control plane целиком прячется у провайдера, вы получаете готовый API-сервер и воркеры, а etcd, scheduler и контроллеры для вас невидимы и обслуживаются облаком. На работе чаще всего встречается именно такой вариант либо кластер, который собрала и поддерживает платформенная команда.

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

И последнее, зачем все это знать. Kubernetes стал стандартом индустрии для запуска приложений в компаниях с масштабом, и схема отсюда это скелет, на который ляжет все ваше дальнейшее знакомство с ним: любой объект, любая команда, любая ошибка кластера объясняется через «кто из компонентов что сделал или не сделал». А на собеседованиях вопросы «что делает kubelet», «зачем нужен Service» и «что происходит после kubectl apply» встречаются настолько стабильно, что разобранную здесь схему можно считать инвестицией в будущий оффер.