Архитектура кластера: 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.
В kubeadm-кластере control plane узлы по умолчанию имеют taint NoSchedule. В kind по умолчанию single-node — taint снят, иначе негде было бы запускать Pod-ы. В managed K8s (EKS, GKE, AKS) control plane вынесен на стороне провайдера и вообще не виден пользователю как Node-объекты.
Control plane: пять компонентов
На каждом control plane узле работают четыре обязательных процесса и один опциональный.
Ключевое свойство: 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, мониторинга).
Запомните разделение: kubelet работает с локальной информацией (что мне делать со своими Pod-ами), kube-proxy — с глобальной (какие Services существуют в кластере). Оба слушают API server, но реагируют на разные подмножества событий.
Поток управления: что происходит при kubectl apply -f deploy.yaml
Возьмём типичный Deployment с 3 репликами и проследим путь от команды до запущенного контейнера.
Шесть процессов, ни один не вызывает другой напрямую. Все коммуницируют через 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-эффективности).
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.
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.