Learning Platform
Глоссарий Troubleshooting
Урок 03.01 · 20 мин
Средний
Cluster architectureControl planeWorker nodesHAetcd topology

Архитектура кластера: control plane и worker nodes

Kubernetes-кластер — это N машин (виртуалок или железных серверов), на которых работает согласованный набор процессов. Часть машин принимает решения о том, что должно быть запущено и где — это control plane. Другая часть выполняет эти решения, запуская контейнеры — это worker nodes. В минимальной конфигурации обе роли могут жить на одной машине (kind, minikube, k3s), в production — это разные пулы узлов с разными политиками безопасности и доступности.

В этом уроке смотрим на кластер сверху: какие процессы существуют, кто с кем разговаривает, что хранится в etcd, и почему HA control plane всегда состоит из нечётного числа узлов.


Cluster = nodes + roles

Cluster в терминах Kubernetes — это N узлов (минимум один), каждый из которых:

  • Linux- или Windows-машина (VM, baremetal, или вложенный контейнер в случае kind)
  • Имеет установленный kubelet — primary agent узла
  • Зарегистрирован в API server как объект типа Node
  • Имеет роль: control-plane, worker, или обе сразу

Роль не задаётся жёстко — это лейбл node-role.kubernetes.io/control-plane="" и (исторически) taint node-role.kubernetes.io/control-plane:NoSchedule, который запрещает планировать пользовательские Pod-ы на control plane узлы. Снимите taint — и control plane превратится в полноценный worker.

NOTE

В kubeadm-кластере control plane узлы по умолчанию имеют taint NoSchedule. В kind по умолчанию single-node — taint снят, иначе негде было бы запускать Pod-ы. В managed K8s (EKS, GKE, AKS) control plane вынесен на стороне провайдера и вообще не виден пользователю как Node-объекты.


Control plane: пять компонентов

На каждом control plane узле работают четыре обязательных процесса и один опциональный.

Control plane процессы на одном узле
kube-apiserverStateless REST API сервер. Слушает :6443 (HTTPS). Единственный компонент, который имеет direct read/write доступ к etcd. Все остальные компоненты (kubelet, scheduler, controllers, kubectl) ходят к нему. Может масштабироваться горизонтально без координации между инстансами.
etcdDistributed key-value store с Raft consensus. Хранит ВСЁ API state — pods, deployments, secrets, configmaps, RBAC bindings. Слушает :2379 для client traffic, :2380 для peer-to-peer Raft. Может быть либо на control plane узлах (stacked), либо на отдельном кластере (external).
watch + write
kube-schedulerWatch-ит Pod-ы без .spec.nodeName в API server. Для каждого: фильтрует пригодные ноды (filtering phase), скорит их (scoring phase), POST-ит binding объект на API server. Не общается напрямую с kubelet — только через API server.
kube-controller-managerОдин процесс, в котором живёт ~30 controllers: DeploymentController, ReplicaSetController, NodeController, JobController, EndpointSliceController, ServiceAccountController, ... Каждый — reconcile loop: watch desired state, observe actual, diff, act. Активен только лидер (leader election через Lease).
cloud-controller-managerОпционально. Выносит cloud-specific логику: создание LoadBalancer-ов через cloud API, route-table management, node lifecycle (что значит «нода живая» в терминах cloud provider). Появился чтобы вычленить cloud-логику из kube-controller-manager и делать её out-of-tree.

Ключевое свойство: kube-apiserver — единственный, кто пишет в etcd. Scheduler не пишет напрямую — он отправляет POST на /api/v1/namespaces/<ns>/pods/<pod>/binding, и уже API server применяет изменение к etcd. То же самое — controllers, kubelet, kubectl. Это даёт единую точку для auth, admission control, audit logging и encryption at rest.


Node components: что работает на каждом узле

На worker-узле — три обязательных процесса (плюс кучу sidecar-демонов от CNI, CSI, мониторинга).

Процессы на worker node
kubeletPrimary node agent. Watch-ит API server на Pod-ы, у которых .spec.nodeName совпадает с именем этой ноды. Для каждого Pod-а: pull images, монтирует volumes, запускает контейнеры через CRI, исполняет probes, обновляет .status в API server. Не запускает контейнеры сам — делегирует через CRI gRPC.
kube-proxyWatch-ит Services и EndpointSlices в API server. Для каждого Service генерирует правила iptables / IPVS / nftables, которые DNAT-ят ClusterIP на endpoint Pod. ClusterIP — это не реальный IP, на нём никто не слушает. Это просто правило в netfilter.
container runtimeРеализует CRI gRPC API. Обычно это containerd или CRI-O. Pulls images через CRI image service, запускает Pod sandbox (pause-контейнер с network/PID namespaces), затем запускает контейнеры внутри этого sandbox. Docker как runtime убрали в v1.24 — Docker shim удалён.
плюс daemonset-ы
CNI pluginCalico, Cilium, Flannel, Weave, ... Конфигурирует сеть Pod-а: создаёт veth-пару, настраивает routing, IPAM, NetworkPolicy enforcement. Вызывается kubelet-ом при старте Pod-а через CNI binary protocol.
CSI pluginРеализует Container Storage Interface — provision/attach/mount volumes. node-plugin работает на каждом узле (mount), controller-plugin — централизованно (provision). Примеры: ebs-csi-driver, rook-ceph, openebs.
мониторинг agentsDaemonSet-ы: node-exporter (Prometheus), fluent-bit / fluentd (логи), nvidia-device-plugin (GPU), и т.д. Не часть K8s core, но фактически всегда есть в production-кластере.
TIP

Запомните разделение: kubelet работает с локальной информацией (что мне делать со своими Pod-ами), kube-proxy — с глобальной (какие Services существуют в кластере). Оба слушают API server, но реагируют на разные подмножества событий.


Поток управления: что происходит при kubectl apply -f deploy.yaml

Возьмём типичный Deployment с 3 репликами и проследим путь от команды до запущенного контейнера.

kubectl apply → Pod Running, пошагово
1. kubectlkubectl читает kubeconfig, находит cluster URL и credentials. Сериализует YAML в JSON, делает HTTP POST на /apis/apps/v1/namespaces/default/deployments.
HTTPS :6443
2. kube-apiserverAuthentication (client cert / bearer token) → Authorization (RBAC) → Admission Control (mutating webhook-и → validating webhook-и) → запись объекта Deployment в etcd под ключом /registry/deployments/default/myapp. Возвращает 201 Created с присвоенным UID и resourceVersion.
watch event ADDED
3. DeploymentControllerInternal watch на Deployment-ы. Видит новый Deployment, создаёт ReplicaSet объект (тоже POST на API server). Не создаёт Pod-ы напрямую — это работа ReplicaSetController-а ниже по цепочке.
4. ReplicaSetControllerWatch на ReplicaSet-ы. Видит новый RS с replicas=3, считает текущие Pod-ы (0), нужно создать 3. POST-ит 3 Pod-объекта на API server. У Pod-ов .spec.nodeName пустой — ещё не назначен на ноду.
watch Pending pods
5. kube-schedulerWatch на Pod-ы с пустым .spec.nodeName. Для каждого: filtering (какие ноды подходят по resources, taints, nodeSelector), scoring (какая лучшая по spread / least requested / affinity). POST на /pods/<name>/binding с выбранной нодой.
watch на своей ноде
6. kubelet (на nodeA, nodeB)Watch на Pod-ы с .spec.nodeName == own-hostname. Видит новые Pod-ы для своей ноды. Pulls images через CRI, создаёт Pod sandbox (pause container, network namespace), запускает контейнеры. Когда контейнер up и probes пройдены → PATCH .status.phase=Running на API server.

Шесть процессов, ни один не вызывает другой напрямую. Все коммуницируют через kube-apiserver и watch-streams. Это event-driven архитектура с eventual consistency. Если в любой момент любой из компонентов упадёт и поднимется — он перечитает state из API server и продолжит с того места.


Анатомия процесса: PID, address space, file descriptors

etcd: что именно там хранится

Всё API state. Без преувеличения — всё. Каждый Pod, каждый Secret, каждый ConfigMap, каждое RBAC-биндинг, каждая Lease для leader election.

Структура ключей в etcd v3 — /registry/<resource>/<namespace>/<name> или /registry/<resource>/<name> для cluster-scoped ресурсов.

/registry/pods/default/nginx-7d4b9f-abc
/registry/pods/default/nginx-7d4b9f-def
/registry/deployments/default/nginx
/registry/replicasets/default/nginx-7d4b9f
/registry/configmaps/kube-system/coredns
/registry/secrets/default/default-token-xyz
/registry/services/endpoints/default/kubernetes
/registry/clusterroles/cluster-admin
/registry/leases/kube-system/kube-controller-manager
/registry/leases/kube-system/kube-scheduler

Значение — это protobuf-сериализованный объект (исторически JSON, с v1.6 default — protobuf для memory- и CPU-эффективности).

WARNING

Secrets в etcd по умолчанию хранятся в base64 — не зашифрованы. Encryption at rest требует явной настройки kube-apiserver через --encryption-provider-config. Без неё кто имеет доступ к диску etcd-узла — имеет доступ ко всем Secrets кластера. Это распространённая дыра в self-managed кластерах.


Single-node и HA control plane

Минимальный кластер — одна машина, на которой работают и control plane, и kubelet. Так устроены kind, minikube, k3s (в default режиме), k0s. Это годится для разработки, CI, тренировок — но не для production, потому что падение этой машины = полная потеря кластера.

Production-кластер строится с HA control plane: 3 или 5 узлов с control plane процессами. Нечётное число — потому что etcd требует write quorum N/2 + 1.

Quorum в etcd-кластере
3 узлаWrite quorum = 2. Переживаем падение одного. Минимальный HA-кластер. Чаще всего используется в production.
5 узловWrite quorum = 3. Переживаем падение двух. Используется когда нужна устойчивость к ребуту/обслуживанию одной ноды без потери HA.
7 узловWrite quorum = 4. Переживаем 3 падения. В большинстве случаев избыточно — etcd hot path замедляется из-за большего количества участников Raft.
чётные числа бесполезны
4 узлаWrite quorum = 3. Переживаем 1 падение — то же, что у 3 узлов. Лишняя машина не даёт устойчивости — только тратит ресурсы и замедляет Raft.

Stacked vs external etcd

Два паттерна HA control plane по расположению etcd.

Stacked etcd

etcd работает на тех же узлах, что и остальные control plane процессы. Каждый control plane узел — это и kube-apiserver, и etcd member. Установка проще — kubeadm так делает по умолчанию. Минус: при потере узла теряем сразу два компонента (apiserver instance + etcd member).

External etcd

etcd кластер вынесен на отдельные машины. Control plane узлы только запускают apiserver/scheduler/controllers, etcd — на стороне. Сложнее в установке, но даёт независимое масштабирование и failure domain isolation. Используется в больших кластерах и в managed K8s (EKS, GKE — там etcd на стороне cloud провайдера).


Минимальная цепочка коммуникации

Кто кому делает HTTP/gRPC запрос — стоит запомнить.

kubectl ─────────────► kube-apiserver
                              ▲ ▲ ▲ ▲ ▲
                              │ │ │ │ │
                              │ │ │ │ └── kubelet (watch + status updates)
                              │ │ │ └──── kube-proxy (watch services/endpoints)
                              │ │ └────── scheduler (watch + bind)
                              │ └──────── controller-manager (watch + write)

                              └── etcd (gRPC) ← только apiserver ходит сюда

Эта однонаправленность — фундаментальное свойство архитектуры. kubelet не звонит scheduler-у. scheduler не звонит controller-у. Все через apiserver, через watch-streams. Падение любого участника не блокирует остальных — они продолжают reconcile со своим cached state.


Проверка знанийKnowledge check
Почему HA control plane всегда строят с нечётным числом узлов (3, 5, 7), а не с чётным (4, 6)?
ОтветAnswer
Из-за write quorum в Raft consensus, который использует etcd. Для записи нужно N/2+1 подтверждений. У 3 узлов quorum=2, переживаем 1 падение. У 4 узлов quorum=3, тоже переживаем 1 падение — добавление одного узла не увеличивает отказоустойчивость, но замедляет Raft (больше пиров для репликации). У 5 узлов quorum=3, переживаем 2 падения. Поэтому чётные числа никогда не используются — они дороже без выигрыша в устойчивости. Стандарт production — 3 (минимальный HA) или 5 (для maintenance одной ноды без потери HA).

Проверьте понимание

Результат: 0 из 0
Концептуальный
Вопрос 1 из 5. Какое утверждение про kube-apiserver и etcd корректно?

Закончили урок?

Отметьте его как пройденный, чтобы отслеживать свой прогресс

Войдите чтобы оценить урок

Прогресс модуля
0 из 6