Kubernetes простыми словами: как устроен кластер и зачем он нужен

Kubernetes простыми словами: как устроен кластер и зачем он нужен

Kubernetes пугает новичков в основном потому, что почти любое объяснение начинается с YAML и команд kubectl. Если сначала разобраться, зачем он появился и как устроен внутри, все остальное складывается в логичную картину.

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

Как мы пришли к контейнерам

Способы доставки приложений менялись последовательно: монолит на сервере, виртуальные машины, контейнеры и, наконец, оркестрация. Каждый этап решал одну большую проблему и приносил новую. Контейнеры дали скорость, переносимость и одинаковое окружение везде, но управлять сотнями контейнеров вручную оказалось отдельной задачей. Если коротко, контейнеры решили упаковку, а Kubernetes решил эксплуатацию.

Жизнь до контейнеров

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

Проблема монолита

Классический монолит большой, сильно связанный, его нельзя масштабировать по частям, он дорогой в поддержке, и обновлять его страшно. Одна ошибка может положить всю систему. Большая кодовая база редко падает быстро, зато падает дорого.

Виртуальные машины

Виртуалки принесли изоляцию и стабильность. Цена оказалась ощутимой: у каждой машины своя операционная система со всеми накладными расходами, запуск занимает минуты, ресурсы используются неэффективно. Изоляцию виртуальные машины дали, скорости не прибавили.

Docker и контейнеры

Docker сделал контейнеры массовыми. Контейнер легкий, стартует за секунды и содержит приложение вместе со всеми зависимостями. Окружение наконец стало одинаковым у разработчика, в тестах и на проде, и фраза «у меня локально работает» перестала быть оправданием.

Когда контейнеров становится много

На масштабе сразу возникают вопросы. Как автоматически добавлять контейнеры под нагрузкой? Что делать, если контейнер упал ночью? Как настроить сеть между сотнями сервисов? Как выкатывать новую версию без простоя? Контейнерам нужен дирижер, и эту роль взял на себя Kubernetes.

Что такое Kubernetes

Kubernetes, или K8s, это платформа оркестрации контейнеров. Она берет на себя деплой, масштабирование, сеть, самовосстановление и конфигурацию приложений. Docker при этом никуда не исчезает: Kubernetes координирует контейнеры по всем машинам кластера, а запускает их container runtime. Удобная модель: Kubernetes работает как операционная система для распределенных приложений.

Главная идея: декларативность

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

apiVersion: apps/v1 kind: Deployment metadata: name: web spec: replicas: 3 selector: matchLabels: app: web template: metadata: labels: app: web spec: containers: - name: web image: nginx:1.27 ports: - containerPort: 80 resources: requests: cpu: "250m" memory: "128Mi"

В этом манифесте нет ни слова о том, на какие серверы ставить контейнеры и что делать при падении. Вы заявляете намерение, остальное Kubernetes делает сам.

Архитектура кластера

Кластер делится на две части. Control plane принимает решения, рабочие ноды запускают приложения. Именно это разделение позволяет Kubernetes масштабироваться и самостоятельно восстанавливаться после сбоев.

Control plane: как Kubernetes думает

Control plane принимает запросы, решает, где запускать нагрузку, хранит состояние кластера и следит, чтобы желаемое состояние совпадало с фактическим. Сам он контейнеры приложений не запускает, его задача в управлении. Внутри работают четыре ключевых компонента.

API Server: единственная дверь

API Server служит единственной точкой входа в кластер. Через него проходят все команды kubectl, через него общаются между собой все компоненты, и он же проверяет подлинность и права каждого запроса. Без API Server кластер просто перестает быть управляемым.

etcd: память кластера

etcd представляет собой распределенное хранилище ключ-значение, где лежат конфигурация кластера, желаемое и текущее состояние всех объектов. Это единственный источник правды для Kubernetes: если чего-то нет в etcd, для кластера этого не существует. Поэтому резервные копии etcd в продакшене обязательны.

Scheduler: подбор ноды

Scheduler решает, на какой ноде запустить под. Он смотрит на запрошенные CPU и память, свободные ресурсы нод, правила affinity и ограничения. Контейнеры он не запускает, его работа заканчивается назначением пода на ноду.

Controller Manager: постоянная сверка

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

Рабочие ноды: где живут приложения

Решения control plane исполняются на рабочих нодах. Здесь крутятся ваши приложения, и здесь работают три компонента.

Kubelet: управляющий ноды

Kubelet запущен на каждой рабочей ноде. Он регистрирует ноду в кластере, следит за назначенными ей подами, проверяет, что нужные контейнеры работают, и отправляет данные о состоянии в control plane. Если под должен работать на этой ноде, kubelet добьется, чтобы он работал.

Container runtime: исполнитель

Container runtime скачивает образы, создает контейнеры, запускает и останавливает их. Kubelet общается с ним через стандартный интерфейс CRI, поэтому Kubernetes все равно, какой именно runtime стоит на ноде. Сегодня чаще всего это containerd или CRI-O: прямую поддержку Docker Engine убрали еще в версии 1.24, при этом образы, собранные Docker, работают как раньше.

kube-proxy: сеть внутри кластера

kube-proxy отвечает за маршрутизацию трафика: виртуальные IP сервисов, балансировку нагрузки и связь между подами. Поды постоянно пересоздаются и меняют адреса, а сервис остается стабильной точкой доступа. Поды временны, сервисы постоянны.

Как все работает вместе

Вы отправляете манифест через kubectl. API Server проверяет запрос и сохраняет объект в etcd. Scheduler выбирает ноду для каждого пода. Kubelet на этой ноде получает задание и просит container runtime запустить контейнеры. kube-proxy настраивает маршруты, чтобы трафик доходил до новых подов. Контроллеры тем временем продолжают следить за состоянием, и этот цикл не останавливается никогда.

Самовосстановление легко увидеть своими глазами:

kubectl apply -f web.yaml # описали желаемое состояние kubectl get pods -o wide # на каких нодах запустились поды kubectl delete pod <имя-пода> # убили один под вручную kubectl get pods -w # контроллер тут же создает замену

Почему это важно понимать

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

Итог

Kubernetes сложен ровно настолько, насколько сложны распределенные системы. Когда понятно, зачем появились контейнеры, почему им понадобилась оркестрация и как control plane взаимодействует с нодами, Kubernetes перестает пугать и становится мощным инструментом.

Полная версия статьи на uproger.com. Больше материалов по DevOps в разделе DevOps на uproger.