Kubernetes для инженеров: от основ эксплуатации до миграции с HashiCorp Nomad

Комплексный курс по продвинутому администрированию Kubernetes, фокусирующийся на детальном разборе манифестов, сетевой связности и управлении ресурсами. Особое внимание уделяется методологии перехода с Nomad и реализации отказоустойчивых сценариев развертывания.

1. Архитектура Kubernetes и концептуальные отличия от HashiCorp Nomad

Архитектура Kubernetes и концептуальные отличия от HashiCorp Nomad

Представьте, что вы управляете огромным морским портом. В системе HashiCorp Nomad вы — диспетчер, который выдает четкие инструкции: «Этот контейнер поставить на этот корабль, а тот — перевезти на склад №4». Вы доверяете капитанам (агентам), но вся логика перемещений сосредоточена в ваших руках. В Kubernetes ситуация иная. Вы не даете приказов «переместить». Вы описываете идеальное состояние порта: «В любой момент времени на причале должно стоять три крана, а в зоне А — десять рефрижераторов». Если кран ломается, система сама, без вашего прямого участия, находит замену, чтобы привести реальность в соответствие с вашим описанием.

Переход от Nomad к Kubernetes — это не просто смена синтаксиса HCL на YAML. Это фундаментальный сдвиг в философии управления инфраструктурой: от императивного планировщика задач к декларативной системе управления состоянием.

Философия Control Plane: Мозг системы

Архитектура Kubernetes строится вокруг концепции Control Plane (узлы управления) и Worker Nodes (рабочие узлы). В отличие от Nomad, где серверная часть относительно легковесна и объединяет функции планировщика и хранилища, Control Plane Kubernetes представляет собой сложный ансамбль специализированных компонентов.

API Server: Единственное окно истины

Центральным элементом является kube-apiserver. Это единственный компонент, с которым взаимодействуют пользователи и другие части кластера. Важно понимать: в Kubernetes никто не правит базу данных напрямую. Любое изменение — будь то запуск нового пода или обновление секрета — проходит через API Server.

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

  • Аутентификация и авторизация: Проверка, кто делает запрос и имеет ли он на это право (RBAC).
  • Мутация и валидация: С помощью Admission Controllers сервер может изменять входящие запросы (например, добавлять стандартные метки) или отклонять их, если они нарушают политику безопасности.
  • Проксирование: Пересылка запросов к другим компонентам, например, к логам контейнеров.
  • etcd: Хранилище, которое нельзя терять

    Все состояние кластера хранится в etcd — распределенной высокодоступной базе данных типа «ключ-значение». Если Nomad может использовать Consul для хранения состояния, то Kubernetes жестко завязан на etcd.

    Здесь кроется первое важное отличие для инженеров, привыкших к Nomad. В Nomad потеря части данных в Consul может быть неприятной, но планировщик часто способен восстановить картину мира. В Kubernetes потеря etcd означает смерть кластера, так как в нем хранятся не только данные о запущенных задачах, но и все определения ресурсов (Custom Resource Definitions), права доступа и конфигурации.

    Scheduler: Математика размещения

    kube-scheduler — это компонент, который следит за новыми подами, у которых не назначен узел. Его работа математически сложна. Он проходит через два этапа:

  • Filtering (Filtering/Predicates): Отсеивание узлов, которые физически не могут принять нагрузку (недостаточно CPU/RAM, не подходят по nodeSelector или имеют taints, которые под не может игнорировать).
  • Scoring (Priorities): Ранжирование оставшихся узлов. Например, планировщик предпочтет узел, где уже скачан нужный Docker-образ, или узел в другой зоне доступности для обеспечения отказоустойчивости.
  • В Nomad планировщик (Scheduler) работает более монолитно. Kubernetes же позволяет легко подменять стандартный планировщик на кастомный или использовать несколько планировщиков одновременно для разных типов задач.

    Controller Manager: Двигатель декларативности

    Это, пожалуй, самый важный компонент для понимания «магии» Kubernetes. kube-controller-manager запускает контроллеры, которые представляют собой бесконечные циклы (Control Loops).

    Логика работы любого контроллера описывается простой формулой:

  • Получить текущее состояние объекта (Actual State) из etcd.
  • Получить желаемое состояние (Desired State) из манифеста.
  • Если состояния различаются, внести изменения (Reconciliation).
  • Например, Deployment Controller следит за тем, чтобы количество запущенных реплик совпадало с числом в манифесте. Если вы вручную удалите под, контроллер увидит расхождение и немедленно даст команду на создание нового. Nomad тоже умеет перезапускать упавшие задачи, но в Kubernetes эта логика выведена на уровень абстрактных контроллеров, которые можно писать самостоятельно для управления чем угодно — от баз данных до внешних облачных ресурсов.

    Анатомия рабочего узла: Где живет код

    Рабочие узлы (Worker Nodes) в Kubernetes выполняют тяжелую работу. В Nomad за это отвечает nomad agent в режиме клиента. В Kubernetes функционал разделен между двумя основными демонами.

    Kubelet: Капитан корабля

    kubelet — это агент, работающий на каждом узле кластера. Он получает от API Server спецификации подов (PodSpecs) и гарантирует, что описанные в них контейнеры запущены и здоровы.

    Важное отличие от Nomad: kubelet не управляет сетью на высоком уровне и не знает о сервисах. Его зона ответственности — жизненный цикл контейнеров на конкретной машине. Он общается с Container Runtime (например, containerd или CRI-O) через интерфейс CRI (Container Runtime Interface). Если в Nomad поддержка Docker или Java-приложений встроена через драйверы, то Kubernetes максимально абстрагирован от среды исполнения.

    Kube-proxy: Сетевой регулировщик

    kube-proxy отвечает за сетевую абстракцию Service. Он поддерживает сетевые правила на узле, которые позволяют подам общаться друг с другом и принимать внешний трафик. В современных кластерах он чаще всего манипулирует правилами iptables или IPVS.

    В Nomad сетевое взаимодействие часто перекладывается на Consul Connect или внешние балансировщики. В Kubernetes сеть является «первоклассным гражданином»: каждый под обязан иметь свой уникальный IP-адрес в рамках кластера, и kube-proxy обеспечивает маршрутизацию к ним, даже если поды постоянно переезжают с узла на узел.

    Kubernetes vs Nomad: Сравнение концепций

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

    Объектная модель: Job vs Deployment

    В Nomad основной единицей работы является Job, внутри которой находятся Groups и Tasks.

  • Task в Nomad Container в Kubernetes.
  • Group в Nomad Pod в Kubernetes.
  • Однако в Kubernetes мы редко работаем с подами напрямую. Мы используем Deployment. Разница в том, что Job в Nomad — это описание запуска, а Deployment в Kubernetes — это описание желаемого состояния долгоживущего сервиса.

    | Характеристика | HashiCorp Nomad | Kubernetes | | :--- | :--- | :--- | | Единица деплоя | Job (HCL) | Deployment / StatefulSet (YAML) | | Минимальный атом | Task | Pod (один или несколько контейнеров) | | Хранение состояния | Server State / Consul | etcd | | Сложность | Низкая (один бинарный файл) | Высокая (множество компонентов) | | Область применения | Контейнеры, бинарники, VM | Преимущественно контейнеры | | Сетевая модель | Зависит от драйвера (Host/Bridge) | Flat Network (IP-per-pod) |

    Декларативность против Императивности

    Nomad тяготеет к императивному подходу. Когда вы запускаете nomad job run, вы отправляете команду планировщику. Если планировщик не может найти место для задачи, он выдаст ошибку.

    Kubernetes — декларативен до мозга костей. Когда вы делаете kubectl apply, вы обновляете запись в etcd. API Server говорит: «Окей, я записал, что ты хочешь 5 реплик». Если в кластере сейчас нет места, API Server не выдаст ошибку. Он просто создаст объект Pod в состоянии Pending. Как только ресурсы появятся (например, вы добавите новую ноду), планировщик тут же подхватит этот под. Это позволяет строить системы, которые самовосстанавливаются без участия внешних скриптов автоматизации.

    Сетевая топология и Service Discovery

    В Nomad Service Discovery обычно реализуется через интеграцию с Consul. Приложение регистрирует себя в Consul, и другие сервисы ищут его там.

    В Kubernetes Service Discovery встроен в ядро. Объект Service предоставляет стабильный IP-адрес (ClusterIP) и DNS-имя. Даже если за сервисом стоят 10 подов, которые постоянно умирают и рождаются с новыми IP, ClusterIP остается неизменным.

  • В Nomad: «Найди мне в Consul сервис auth и дай его порты».
  • В Kubernetes: «Отправь запрос на http://auth-service, и внутренняя сеть кластера сама разберется, на какой под его доставить».
  • Жизненный цикл запроса: Как работает магия

    Чтобы закрепить понимание архитектуры, проследим, что происходит, когда вы вводите команду kubectl apply -f deployment.yaml.

  • Транспорт и Auth: kubectl отправляет HTTP POST запрос к API Server. Сервер проверяет ваш сертификат или токен, а затем сверяет ваши права с RBAC-политиками.
  • Admission Control: Запрос проходит через цепочку проверок. Например, ResourceQuota проверяет, не превысили ли вы лимит ресурсов в своем пространстве имен (Namespace).
  • Запись в etcd: Если все проверки пройдены, API Server сохраняет описание Deployment в etcd. На этом этапе для пользователя команда kubectl завершается успешно. Но работа в кластере только начинается.
  • Deployment Controller: Этот контроллер видит (через механизм Watch), что появился новый Deployment. Он понимает, что должен создать объект ReplicaSet.
  • ReplicaSet Controller: Видит новый ReplicaSet и создает нужное количество объектов Pod. У этих подов в поле nodeName пока пусто — они не назначены на узлы.
  • Scheduler: Замечает поды без узла. Он проводит фильтрацию и скоринг, выбирает лучший узел (например, node-01) и обновляет объект пода в API Server, записывая туда имя узла.
  • Kubelet: На узле node-01 агент kubelet постоянно опрашивает API Server на предмет подов, назначенных ему. Увидев новый под, он обращается к Container Runtime (например, Docker/containerd), чтобы скачать образы и запустить контейнеры.
  • Kube-proxy: Параллельно, если к поду привязан Service, kube-proxy на всех узлах обновляет правила маршрутизации, чтобы трафик начал поступать в новый контейнер.
  • Эта цепочка событий демонстрирует принцип Eventual Consistency (согласованность в конечном счете). Компоненты не вызывают друг друга напрямую, они лишь реагируют на изменения состояния в центральном хранилище.

    Граничные случаи и ограничения

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

    Когда Nomad может быть лучше?

  • Если вам нужно запускать неконтейнеризированные приложения (простые бинарные файлы Windows/Linux) без лишних слоев абстракции.
  • Если у вас очень ограниченные ресурсы на узлах управления (например, Edge-вычисления).
  • Если вам нужна федерация кластеров «из коробки» без сложных дополнений.
  • Почему Kubernetes побеждает в долгосрочной перспективе?

  • Экосистема: Helm для управления пакетами, Prometheus для мониторинга, Istio для Service Mesh — всё это создавалось в первую очередь для Kubernetes.
  • Стандартизация: Kubernetes стал «облачной операционной системой». Любое облако (AWS, GCP, Azure, Yandex Cloud) предоставляет управляемый сервис (Managed K8s), что снимает нагрузку по обслуживанию Control Plane.
  • Расширяемость: Через Custom Resource Definitions (CRD) вы можете заставить Kubernetes управлять чем угодно, превращая его в универсальный Control Plane для всей вашей инфраструктуры.
  • Практический взгляд на YAML манифест

    В Nomad мы привыкли к HCL (HashiCorp Configuration Language), который позволяет использовать переменные и функции прямо в коде. В Kubernetes манифесты — это чистый YAML. Это сознательное решение: манифест должен быть статичным отражением состояния. Динамика (шаблонизация) выносится во внешние инструменты, такие как Helm или Kustomize.

    Типичная структура манифеста Kubernetes всегда включает четыре обязательных поля:

  • apiVersion: Версия API (например, apps/v1).
  • kind: Тип ресурса (Deployment, Service, ConfigMap).
  • metadata: Имя, пространство имен и метки (labels).
  • spec: Спецификация — то самое «желаемое состояние».
  • Пример базового манифеста, который мы будем детально разбирать в следующих главах:

    Обратите внимание на поле selector. В Kubernetes связь между объектами (например, между Deployment и его подами или между Service и подами) осуществляется через Labels (метки). Это гораздо гибче, чем жесткие ссылки в Nomad. Если вы измените метку у пода на лету, Deployment «потеряет» его и тут же создаст новый, а старый под станет «сиротой», но продолжит работать. Эта слабая связанность компонентов — ключ к масштабируемости и устойчивости системы.

    Резюмируя архитектурный сдвиг

    Переход на Kubernetes требует принятия того факта, что кластер — это живой организм, а не просто набор серверов для запуска скриптов. В Nomad вы управляете задачами. В Kubernetes вы управляете ресурсами и их взаимоотношениями.

    Понимание того, как etcd, API Server и контроллеры взаимодействуют между собой, является фундаментом для траблшутинга. Если ваше приложение не запускается, вы теперь знаете, где искать:

  • Не создался под? Проверяем Controller Manager.
  • Под висит в Pending? Идем к Scheduler.
  • Под на узле, но не стартует? Смотрим логи Kubelet и Container Runtime.
  • Трафик не доходит? Проверяем Kube-proxy и правила Service.
  • Эта прозрачность и разделение ответственности делают Kubernetes сложным в освоении, но невероятно мощным инструментом в руках инженера, стремящегося к полной автоматизации жизненного цикла приложений.