Kubernetes

Теория: Архитектура Kubernetes

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

Понимание архитектуры даст направление, куда смотреть при проблемах. Если Pod не создаётся — проблема в Control Plane. Если создаётся, но не запускается — смотрим на Worker-узел. Если запускается, но недоступен по сети — смотрим kube-proxy и Service.

Два типа узлов

Kubernetes-кластер состоит из узлов двух типов, которые выполняют принципиально разные роли.

Master-узлы (Control Plane) управляют кластером. Они принимают решения: какой Pod на каком узле запустить, сколько реплик поддерживать, как реагировать на сбои. Master-узлы хранят состояние системы и координируют работу всех компонентов.

Worker-узлы выполняют полезную работу. Здесь запускаются контейнеры, обрабатываются запросы пользователей, хранятся данные приложений.

В production-кластерах обычно три Master-узла (для отказоустойчивости) и десятки или сотни Worker-узлов. В локальных кластерах (minikube, kind, k3s) один узел часто совмещает обе роли.

Посмотрите узлы вашего кластера:

kubectl get nodes

Вы увидите список узлов и их роли. Колонка ROLES покажет, какой узел является control-plane, а какой — worker (или <none> для worker-узлов в некоторых дистрибутивах).

Для более подробной информации:

kubectl get nodes -o wide

Флаг -o wide добавляет колонки: внутренний IP узла, версия ОС, версия Container Runtime. Это полезно при диагностике — сразу видно, какой runtime используется и доступен ли узел по сети.

Worker-узлы: где работают приложения

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

kubelet

kubelet — агент узла. Он постоянно опрашивает API Server: «Какие Pod'ы должны работать на моём узле?» Получив список, kubelet сравнивает его с тем, что уже запущено. Если нужен новый Pod — запускает контейнеры. Если Pod удалён — останавливает. Если контейнер упал — перезапускает.

kubelet — единственный компонент Kubernetes, который напрямую работает с контейнерами. Все остальные компоненты (Scheduler, Controller Manager, даже API Server) только создают и читают объекты. Они никогда не обращаются к контейнерам напрямую.

Помимо запуска контейнеров, kubelet выполняет liveness и readiness проверки. Если liveness probe не проходит — kubelet перезапускает контейнер. Если readiness probe не проходит — kubelet сообщает об этом, и Service перестаёт направлять трафик на этот Pod.

Container Runtime

Container Runtime — программа, которая непосредственно запускает контейнеры. kubelet не умеет работать с контейнерами сам — он делегирует это Container Runtime через стандартный интерфейс CRI (Container Runtime Interface).

Самые распространённые реализации — containerd и CRI-O. Docker тоже может использоваться, но через дополнительную прослойку (dockershim был удалён в Kubernetes 1.24). В современных кластерах Docker как runtime — редкость.

Посмотреть, какой runtime используется:

kubectl get nodes -o wide

В колонке CONTAINER-RUNTIME вы увидите что-то вроде containerd://1.6.20 или cri-o://1.27.0.

kube-proxy

kube-proxy — сетевой компонент. Он настраивает правила маршрутизации, чтобы Service мог направлять трафик к Pod'ам. Когда вы создаёте Service, kube-proxy на каждом узле добавляет правила: «трафик на ClusterIP

направлять на один из Pod'ов с соответствующими labels».

kube-proxy работает в одном из режимов: iptables, ipvs или nftables. Режим iptables — стандартный, но при тысячах Service'ов становится медленным из-за линейного поиска по правилам. Режим ipvs использует хеш-таблицы и работает быстрее на больших масштабах. Режим выбирается при настройке кластера.

В некоторых современных кластерах kube-proxy заменяют на eBPF-реализации (Cilium), которые работают ещё эффективнее.

Master-узлы: где принимаются решения

Master-узлы образуют Control Plane — управляющий слой кластера. Здесь хранится состояние и принимаются все решения.

API Server

API Server — единственная точка входа в кластер. Любое действие проходит через него: команды kubectl, запросы от kubelet, решения Scheduler'а, действия контроллеров.

API Server выполняет несколько функций:

  • Проверяет аутентификацию (кто вы?)
  • Проверяет авторизацию (что вам разрешено?)
  • Валидирует объекты (корректен ли манифест?)
  • Сохраняет объекты в etcd

Никто не может изменить состояние кластера, минуя API Server. Это упрощает безопасность — достаточно защитить одну точку входа.

Посмотреть адрес API Server:

kubectl cluster-info

Важно понимать: API Server не принимает решений. Он только принимает запросы, валидирует их и сохраняет данные. Решения о размещении Pod'ов принимает Scheduler, решения о поддержании состояния — контроллеры.

etcd

etcd — распределённое хранилище ключ-значение. Здесь лежит всё состояние кластера: описания Pod'ов, Deployment'ов, Service'ов, Secrets, ConfigMap'ов — всё.

etcd хранит и желаемое состояние (сколько реплик должно быть), и текущее состояние (сколько реплик есть сейчас). Вся логика Kubernetes строится вокруг постоянного согласования текущего состояния с желаемым.

etcd — единственный stateful-компонент Control Plane. Потеря данных etcd означает потерю всего кластера — Kubernetes «забудет», что должно работать. Поэтому backup etcd критически важен в production.

etcd доступен только для API Server. Никакой другой компонент не читает и не пишет в etcd напрямую. Это упрощает архитектуру и повышает безопасность.

Посмотреть Pod etcd (если кластер развёрнут с помощью kubeadm):

kubectl get pods -n kube-system | grep etcd

etcd использует алгоритм консенсуса Raft. Один узел выбирается лидером и принимает записи, остальные реплицируют данные. Если лидер падает — автоматически выбирается новый. Благодаря этому несколько экземпляров etcd всегда имеют согласованные данные даже при сбоях.

Scheduler

Scheduler выбирает узел для каждого нового Pod'а. Когда появляется Pod без назначенного узла (поле spec.nodeName пустое), Scheduler начинает работу.

Процесс состоит из двух этапов. Сначала filtering — Scheduler отбрасывает узлы, которые точно не подходят: не хватает ресурсов, не соответствуют nodeSelector или affinity, есть taints без соответствующих tolerations. Затем scoring — из оставшихся узлов Scheduler выбирает лучший по набору критериев: равномерность нагрузки, предпочтения размещения, другие факторы.

После выбора Scheduler записывает решение через API Server — заполняет поле nodeName в объекте Pod.

Scheduler не запускает Pod. Он только принимает решение и записывает его. Запуск — работа kubelet на выбранном узле.

Controller Manager

Controller Manager — это набор контроллеров, каждый из которых следит за определённым типом объектов.

ReplicaSet Controller проверяет: «должно быть 3 реплики, а запущено 2» — и создаёт ещё один Pod. Deployment Controller управляет ReplicaSet'ами при обновлениях. Node Controller следит за состоянием узлов и помечает недоступные. Job Controller управляет выполнением Job'ов. И так далее.

Все контроллеры работают по одному принципу: наблюдают текущее состояние, сравнивают с желаемым, предпринимают действия для устранения разницы. Этот паттерн называется reconciliation loop.

Именно контроллеры делают Kubernetes самовосстанавливающейся системой. Если Pod упал — ReplicaSet Controller создаст новый. Если узел стал недоступен — Pod'ы будут пересозданы на других узлах.

Cloud Controller Manager

Cloud Controller Manager присутствует в managed-кластерах (EKS, GKE, AKS) и кластерах, интегрированных с облаком. Он взаимодействует с API облачного провайдера.

Когда вы создаёте Service типа LoadBalancer, Cloud Controller Manager создаёт реальный балансировщик в облаке. Когда вы запрашиваете PersistentVolume — он создаёт облачный диск. Когда виртуальная машина удаляется в облаке — он удаляет соответствующий узел из кластера.

В кластерах на bare metal или в локальных окружениях Cloud Controller Manager отсутствует.

Как компоненты работают вместе

Посмотрим, что происходит при выполнении команды:

kubectl create deployment nginx --image=nginx --replicas=3

kubectl отправляет запрос в API Server. API Server проверяет ваши права, валидирует объект Deployment и сохраняет его в etcd.

Deployment Controller (часть Controller Manager) замечает новый Deployment. Он создаёт объект ReplicaSet с тремя репликами и записывает его через API Server.

ReplicaSet Controller замечает новый ReplicaSet. Он видит, что нужно 3 Pod'а, а существует 0. Он создаёт три объекта Pod через API Server. У этих Pod'ов поле nodeName пустое — они ещё не назначены на узлы.

Scheduler замечает три Pod'а без назначенного узла. Для каждого он оценивает доступные Worker-узлы, выбирает подходящий и записывает решение в API Server (заполняет spec.nodeName).

kubelet на каждом выбранном узле замечает, что ему назначен новый Pod. Он скачивает образ nginx через Container Runtime и запускает контейнер. После запуска kubelet начинает выполнять probes и отправлять статус в API Server.

Весь процесс занимает секунды. Каждый компонент делает свою часть работы и общается с остальными только через API Server.

Вы можете увидеть эту цепочку в событиях:

kubectl describe deployment nginx

В секции Events вы увидите, как Deployment Controller создал ReplicaSet. Теперь посмотрите события Pod'а:

kubectl get pods
kubectl describe pod <имя-пода>

В событиях Pod'а вы увидите: Scheduled (Scheduler назначил узел), Pulled (kubelet скачал образ), Created (контейнер создан), Started (контейнер запущен).

Важные детали

Taints и master-узлы

По умолчанию на master-узлах есть taint node-role.kubernetes.io/control-plane:NoSchedule. Это означает, что Scheduler не будет назначать на них обычные Pod'ы.

Но это не жёсткий запрет — это настройка. Если Pod имеет соответствующий toleration, он может быть запущен на master-узле. В небольших кластерах (minikube, k3s, тестовые окружения) taint часто убирают, и на единственном узле работает всё: и Control Plane, и приложения.

Посмотреть taints узла:

kubectl describe node <имя-узла> | grep Taints

Static Pods

Большинство Pod'ов создаются через API Server. Но компоненты самого Control Plane (API Server, etcd, Scheduler, Controller Manager) — это тоже контейнеры. Как они запускаются, если API Server ещё не работает?

Ответ — Static Pods. kubelet может читать манифесты Pod'ов напрямую из директории на файловой системе (обычно /etc/kubernetes/manifests). Такие Pod'ы kubelet запускает сам, без участия API Server.

Именно так обычно запускаются компоненты Control Plane в кластерах, созданных kubeadm. Static Pods видны в API Server (kubelet создаёт для них «зеркальные» объекты), но удалить их через kubectl нельзя — нужно удалить файл манифеста на узле.

Посмотреть компоненты Control Plane:

kubectl get pods -n kube-system

Вы увидите Pod'ы с именами вроде etcd-<имя-узла>, kube-apiserver-<имя-узла>, kube-scheduler-<имя-узла>. Суффикс с именем узла — признак Static Pod.

Потеря связи с API Server

Что происходит, если Worker-узел теряет связь с API Server?

Pod'ы на этом узле продолжают работать — kubelet не останавливает их. Но kubelet не может отправить статус, и Control Plane не знает, что происходит на узле.

Через определённое время (по умолчанию 5 минут) Node Controller пометит узел как NotReady. Ещё через некоторое время Pod'ы с этого узла будут помечены для пересоздания на других узлах (если они управляются Deployment или ReplicaSet).

Но старые Pod'ы на «потерянном» узле продолжат работать, пока узел не восстановит связь или не будет перезагружен. Это важно понимать: вы можете получить ситуацию, когда один Pod работает на «потерянном» узле, а его «замена» — на другом. Для stateful-приложений это может вызвать проблемы с дублированием.

High Availability

В production Control Plane должен быть отказоустойчивым. Стандартная конфигурация — три master-узла.

Почему три, а не два? Из-за etcd. etcd использует алгоритм консенсуса Raft, которому нужен кворум — большинство узлов должны быть доступны для записи. С двумя узлами потеря одного означает потерю кворума (остался 1 из 2 — это не большинство). С тремя узлами кластер переживёт потерю одного (останется 2 из 3 — большинство).

По этой же причине используют нечётное количество master-узлов: 3, 5, 7. Четыре узла не лучше трёх — для кворума нужно три из четырёх, так что допустима потеря только одного, как и при трёх узлах.

API Server работает в режиме active-active — все экземпляры обрабатывают запросы, нагрузка распределяется через балансировщик. Scheduler и Controller Manager работают в режиме active-standby — работает один экземпляр (лидер), остальные ждут. Если лидер падает, один из ожидающих становится новым лидером.

Диагностика через архитектуру

Понимание архитектуры необходимо для настройки системного процесса.

  • Pod в статусе Pending — Scheduler не может найти подходящий узел. Смотрите события Pod'а (kubectl describe pod), проверяйте ресурсы узлов (kubectl describe nodes), проверяйте taints и tolerations.
  • Pod создан, но контейнер не запускается — проблема на уровне kubelet или Container Runtime. Смотрите события Pod'а, логи kubelet на узле, проверяйте доступность образа.
  • Pod Running, но сервис недоступен — смотрите kube-proxy, проверяйте Service и Endpoints (kubectl get endpoints), проверяйте NetworkPolicy.
  • Команды kubectl не работают — проблема с API Server. Проверяйте доступность API Server (kubectl cluster-info), смотрите логи API Server в kube-system.

Итог

Worker-узлы содержат kubelet, Container Runtime, kube-proxy и пользовательские Pod'ы. Здесь выполняется работа.

Master-узлы содержат API Server, etcd, Scheduler, Controller Manager и (в облаке) Cloud Controller Manager. Здесь принимаются решения.

Все компоненты общаются только через API Server. etcd доступен только API Server. kubelet — единственный компонент, который работает с контейнерами напрямую.

Эта архитектура определяет, куда смотреть при проблемах. Запомните цепочку: kubectl → API Server → etcd → Controller → Scheduler → kubelet → Container Runtime. Проблема может быть на любом этапе, и понимание архитектуры помогает быстро локализовать её.

Рекомендуемые программы

+7 800 100 22 47

бесплатно по РФ

+7 495 085 21 62

бесплатно по Москве

108813 г. Москва, вн.тер.г. поселение Московский,
г. Московский, ул. Солнечная, д. 3А, стр. 1, помещ. 20Б/3
ОГРН 1217300010476
ИНН 7325174845