🐳 Раздел 14 · Вопрос #10

Что такое Node в Kubernetes

Если Control Plane — это «мозг» и администрация кластера, то ноды — это его «рабочие руки». Кластер объединяет вычислительные ресурсы десятков таких нод (процессоры, оперативную...


🟢 Junior Level

30-секундный ответ

Node (Узел) — это отдельный физический сервер или виртуальная машина в кластере Kubernetes, на которой фактически запускаются и выполняются поды с приложениями.

Если Control Plane — это «мозг» и администрация кластера, то ноды — это его «рабочие руки». Кластер объединяет вычислительные ресурсы десятков таких нод (процессоры, оперативную память, диски) в единый общий пул.


Наглядная аналогия из жизни

Представьте международный грузовой порт:

  • Control Plane (Диспетчерская): принимает заказы, распределяет грузы по кораблям, следит за расписанием.
  • Node (Грузовой корабль): обладает физической грузоподъёмностью (CPU и RAM).
  • Pod (Контейнеры с грузом): размещаются на палубе корабля.
  • Капитан корабля (kubelet): следит за креплением грузов, проверяет целостность и непрерывно докладывает в диспетчерскую о свободном месте на палубе.

3 обязательных компонента на каждом Worker Node

  1. kubelet — главный агент узла. Принимает от kube-apiserver задания на запуск подов, общается с рантаймом контейнеров, выполняет Health Checks и отправляет сигналы жизнеспособности узла (Node Heartbeats).
  2. kube-proxy — сетевой агент. Настраивает правила фильтрации пакетов и маршрутизации (iptables или IPVS) в ядре операционной системы хоста, отвечая за балансировку трафика сервисов.
  3. Container Runtime (CRI) — низкоуровневая среда исполнения контейнеров. Начиная с Kubernetes 1.24 (после удаления dockershim), стандартом индустрии являются containerd и CRI-O.

🟡 Middle Level

Бюджет ресурсов ноды: Capacity vs Allocatable

Распространённая ошибка — считать, что вся оперативная память физического сервера доступна подам. Kubernetes рассчитывает доступные ресурсы по строгой формуле:

\[\mathbf{Allocatable} = \mathbf{Capacity} - \mathbf{kube\text{-}reserved} - \mathbf{system\text{-}reserved} - \mathbf{eviction\text{-}threshold}\]
+--------------------------------------------------------------------------+
|                        Node Capacity (Например: 32 GiB RAM)              |
|                                                                          |
|  [ kube-reserved ]   -> Память для процессов K8s (kubelet, containerd)   |
|  [ system-reserved ] -> Память для ядра ОС и системных демонов (sshd)    |
|  [ hard-eviction ]   -> Защитный буфер безопасности (порог выселения)    |
| ──────────────────────────────────────────────────────────────────────── |
|  [ Allocatable ]     -> ТОЛЬКО ЭТА ЧАСТЬ доступна для планирования подов!|
+--------------------------------------------------------------------------+

[!IMPORTANT] Планировщик kube-scheduler оперирует исключительно значением Allocatable. Если сумма requests уже запущенных подов исчерпала Allocatable, планировщик никогда не посадит новый под на эту ноду, даже если физически память свободна.


Статусы и условия ноды (Node Conditions)

Kubelet непрерывно сообщает состояние здоровья ноды через флаги в объекте Node: | Condition | Нормальное значение | Описание | | :— | :—: | :— | | Ready | True | Нода здорова, kubelet активен и готов принимать поды. | | MemoryPressure | False | Если True — свободная память упала ниже порога, планирование блокируется. | | DiskPressure | False | Если True — диск переполнен (не хватает места под образы или логи). | | PIDPressure | False | Если True — исчерпан лимит доступных идентификаторов процессов Linux PID. | | NetworkUnavailable| False | Если True — маршрутизация CNI-сети для ноды не настроена. |


Обслуживание нод: Cordon и Drain

Перед проведением технических работ на физическом сервере (обновление ядра Linux, замена RAM):

  1. kubectl cordon <node> (Оцепление):
    • Помечает ноду как SchedulingDisabled.
    • Новые поды на ноду не планируются, но уже запущенные продолжают работать.
  2. kubectl drain <node> --ignore-daemonsets --delete-emptydir-data (Опустошение):
    • Блокирует планирование и бережно выселяет (evict) все работающие поды на другие здоровые ноды кластера через механизм Graceful Shutdown.
  3. kubectl uncordon <node> (Снятие оцепления):
    • Возвращает сервер в строй после перезагрузки.

🔴 Senior Level

Управление размещением: Taints, Tolerations и Node Affinity

Senior-инженер настраивает гибкое распределение нагрузок между специализированными серверами (например, узлы с GPU или узлы под базы данных).

   [ Обычный Pod ]                      [ GPU Pod с Toleration ]
          │                                        │
          ▼                                        ▼
   ❌ Отталкивается!                       🟢 Разрешено размещение!
          │                                        │
          └─────────────► [ Node с Taint ] ◄───────┘
                          gpu=true:NoSchedule

1. Taints (Метки отторжения на ноде)

Задаются на узле: kubectl taint nodes worker-gpu dedicated=gpu:NoSchedule.

  • NoSchedule: новые поды без соответствующей толерантности не будут запланированы на узел.
  • PreferNoSchedule: планировщик попытается избежать размещения, но при дефиците ресурсов посадит под сюда.
  • NoExecute: поды без толерантности немедленно выселяются с узла, даже если они уже работали на нём!

2. Tolerations (Толерантность в манифесте Pod)

Позволяет поду игнорировать метку отторжения:

tolerations:
- key: "dedicated"
  operator: "Equal"
  value: "gpu"
  effect: "NoSchedule"

3. Node Affinity (Притяжение пода к ноде)

  • requiredDuringSchedulingIgnoredDuringExecution: жесткое правило (hard rule). Если ноды с меткой topology.kubernetes.io/zone=eu-west-1a нет, под останется в статусе Pending.
  • preferredDuringSchedulingIgnoredDuringExecution: мягкое правило (soft rule) с выставлением весовых коэффициентов приоритета.

Архитектура Node Leases (Оптимизация etcd)

Исторически (до K8s 1.14) каждый kubelet каждые 10 секунд слал полное обновление объекта NodeStatus в kube-apiserver.

  • При кластере в 2000 нод это вызывало огромную нагрузку на etcd и сеть (размер объекта Node с десятками образов и адресов — десятки килобайт).
  • Современная архитектура: разделение статуса:
    • Тяжелый объект Node обновляется редко (только при изменении условий, например раз в минуту).
    • Быстрый heartbeat передаётся через сверхлегковесный объект Lease в пространстве имён kube-node-lease каждые 10 секунд. Если аренда Lease не продлевается более 40 секунд, Controller Manager переводит ноду в статус NotReady.

Kubelet Eviction Manager (Вытеснение подов при дефиците памяти)

Когда на ноде срабатывает жесткий порог evictionHard: memory.available < 100Mi:

  1. Kubelet не ждёт реакции Control Plane, а принимает решение локально прямо на ноде.
  2. Kubelet ранжирует поды по потреблению ресурсов относительно их requests и классу качества обслуживания:
    • Сначала выселяются поды BestEffort, потребляющие больше всех памяти.
    • Затем выселяются поды Burstable, превысившие свой requests.memory.
    • Поды Guaranteed выселяются в последнюю очередь, только если на ноде больше нет других подов.

4 Tricky Questions

1. Почему команда kubectl drain <node> по умолчанию завершается ошибкой, если на ноде есть поды с томами emptyDir или поды без контроллера (Bare Pods)?

Ответ: Команда drain защищает пользователя от случайной безвозвратной потери данных:

  1. Тома emptyDir: данные в emptyDir существуют исключительно на диске конкретной ноды. Удаление пода уничтожит эти данные навсегда. Поэтому drain требует явного подтверждения флагом --delete-emptydir-data.
  2. Поды без контроллера (Bare Pods): если под создан напрямую (kind: Pod), а не через Deployment или StatefulSet, то при его удалении его некому пересоздать на другой ноде. Он просто исчезнет. Для их удаления требуется флаг --force.
  3. DaemonSet: поды DaemonSet обязаны работать на каждой ноде. Команда drain не может их выселить на соседнюю ноду (там уже запущен свой экземпляр), поэтому требует флага --ignore-daemonsets.

2. Что произойдёт с работающими подами на ноде, если процесс kubelet на этом сервере аварийно завершится (упадёт или зависнет)?

Ответ:

  1. Процессы контейнеров продолжат работать! Запущенные контейнеры управляются рантаймом containerd, который работает как независимый демон. Трафик через kube-proxy продолжит идти к подам.
  2. Нода перестанет обновлять свой объект Lease в kube-apiserver. Через 40 секунд Controller Manager присвоит ноде состояние Ready=Unknown.
  3. Запустится таймер pod-eviction-timeout (по умолчанию 5 минут).
  4. Если kubelet не восстановится за 5 минут, контроллер начнёт создавать замещающие копии подов на других нодах кластера.
  5. Однако исходные поды на сломанной ноде продолжат выполняться до тех пор, пока kubelet не оживёт и не получит команду на их удаление.

3. В чём принципиальная разница между эффектами Taints NoSchedule и NoExecute?

Ответ:

  • NoSchedule: влияет только на будущие поды в момент работы планировщика kube-scheduler. Поды, которые уже были запущены на этой ноде до добавления taint, продолжат спокойно работать и не будут тронуты.
  • NoExecute: влияет и на новые, и на уже работающие поды! Если на ноду повесить taint с эффектом NoExecute, планировщик мгновенно инициирует удаление (eviction) всех подов на этой ноде, у которых нет явного toleration для этой метки (или выселит их по истечении таймера tolerationSeconds).

4. Чем технология автомасштабирования узлов Karpenter (от AWS) архитектурно превосходит классический Kubernetes Cluster Autoscaler?

Ответ:

  • Cluster Autoscaler (Классический подход): привязан к группам автомасштабирования облака (AWS Auto Scaling Groups — ASG). При появлении Pending подов он увеличивает размер предопределённой группы узлов фиксированного типа (например, добавляет ещё один m5.large). Процесс занимает 3–5 минут, а узлы часто расходуются неэффективно.
  • Karpenter: работает напрямую с API облачного провайдера в обход ASG. Он анализирует совокупные требования всех Pending подов (CPU, GPU, архитектура ARM/x86, зоны доступности) и за 30–45 секунд заказывает ровно один идеально подходящий сервер (Right-Sized Instance или Spot-инстанс), экономя до 50% бюджета на облако.

🎯 Шпаргалка для интервью

Главное о Node

  • Суть: рабочий сервер кластера (физический или ВМ).
  • 3 обязательных демона: kubelet (агент пода), kube-proxy (L4-сеть), containerd / CRI-O (рантайм).
  • Ресурсы: $\text{Allocatable} = \text{Capacity} - \text{Зарезервировано под K8s и ОС}$. Планирование идёт только по Allocatable!
  • Сервисные команды: cordon (запрет новых), drain (плавное выселение), uncordon (возврат в строй).
  • Taints / Tolerations: механизм отталкивания неподходящих подов (NoSchedule, NoExecute).
  • Node Affinity: притяжение подов к серверам по меткам (SSD, GPU, Availability Zone).

Сравнение эффектов Taints

| Эффект Taint | Новые поды | Уже работающие поды | | :— | :— | :— | | NoSchedule | ❌ Не пускает | 🟢 Не трогает (продолжают работать) | | PreferNoSchedule | 🟡 Старается не пускать (мягкое правило)| 🟢 Не трогает | | NoExecute | ❌ Не пускает | 🔴 Немедленно выселяет с ноды! |

Красные флаги на собеседовании (Чего говорить нельзя)

  • ❌ «Node и Pod — это одно и то же» — Node это сервер (хост), Pod это контейнерная группа, работающая на сервере.
  • ❌ «Вся память сервера доступна подам» — часть памяти жестко зарезервирована под kubelet, containerd и ОС (Allocatable < Capacity).
  • ❌ «Падение kubelet сразу убивает запущенные контейнеры» — контейнеры продолжают работать в containerd, но теряется управление нодой.

Связанные вопросы