Kubernetes: что это, как устроен и первый деплой | Otus >
У этой рекламы есть минус
У этой рекламы есть минус
−10% на курсы со стартом в ноябре. Успейте получить выгоду, 10 числа акция закончится
−10% на курсы со стартом в ноябре. Успейте получить выгоду, 10 числа акция закончится
Выбрать курс

Kubernetes: что это, как устроен кластер и как запустить первое приложение

Kuber_Deep_14.4-5020-6a6dfe.png

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

В конце статьи вы запустите первое приложение в локальном кластере и своими руками проверите, как Kubernetes восстанавливает экземпляры и откатывает версию. Если захочется повторить эти шаги вместе с преподавателем и задать вопросы по ходу, посмотрите расписание бесплатных открытых уроков Otus по DevOps и выберите подходящую тему.

Содержание:

  1. Что такое Kubernetes и какие задачи он решает
  2. Как устроен кластер: control plane и рабочие узлы
  3. etcd и кворум: почему узлов управления три, а не два
  4. Объекты Kubernetes: Pod, ReplicaSet, Deployment, Service
  5. Как трафик попадает в приложение: Ingress и Gateway API
  6. Практика: первое приложение в Minikube
  7. Выводы

Что такое Kubernetes и какие задачи он решает

Представьте сервис, который работает в двух экземплярах. Один упал - процесс завершился с ошибкой, а заодно нужно выкатить новую версию, не останавливая сервис. Без оркестратора человек заходит на сервер, перезапускает упавший экземпляр и по очереди меняет версии вручную. Зависший процесс, который не завершился, а просто перестал отвечать, Kubernetes сам не распознает - для этого в манифест добавляют проверки живости (liveness probe), о них - в конце практики. Kubernetes берет такие действия на себя:

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

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

Проект начат в Google в 2014 году, сейчас его развивает сообщество в CNCF.

Kubernetes - не готовая платформа (не PaaS), а конструктор. Из коробки в нем нет сборки и доставки кода (CI/CD), сбора логов, баз данных и очередей: их подключают отдельно, и это стоит учесть, когда оцениваете, сколько работы займет переезд.

Как устроен кластер: control plane и рабочие узлы

Кластер Kubernetes делится на две части. Control plane (управляющая плоскость, раньше говорили «мастер») принимает решения: где запустить Pod, сколько экземпляров должно работать, что изменилось. Рабочие узлы (worker nodes) - физические или виртуальные машины, на которых работают контейнеры приложения.

Pod - группа из одного или нескольких контейнеров приложения, которую Kubernetes запускает как одно целое (подробнее - в разделе про объекты). Описание того, сколько Pod нужно и из какого образа, хранится в кластере. Рабочий узел сам ничего не решает: он запускает Pod, которые назначил ему control plane. На примере одного сервиса: планировщик в control plane выбирает узел, а kubelet на этом узле запускает контейнеры.

Компоненты и их роли:

Компонент Где работает Что делает
kube-apiserver control plane Единая точка входа: принимает запросы, проверяет права и объект, сохраняет его в etcd
etcd control plane или отдельные машины Согласованное хранилище «ключ - значение» для всех данных кластера
kube-scheduler control plane Находит Pod без узла и выбирает подходящий узел. Сам Pod не запускает
kube-controller-manager control plane Запускает контроллеры, которые приводят фактическое состояние к желаемому
cloud-controller-manager control plane, необязателен Связывает кластер с облаком: балансировщики, диски
kubelet каждый узел Следит, чтобы Pod, назначенные узлу, работали, и сообщает их статус
kube-proxy каждый узел, необязателен Поддерживает сетевые правила, через которые работают Service
Среда запуска контейнеров каждый узел Запускает контейнеры: containerd, CRI-O и другие совместимые с CRI

Компоненты работают с состоянием кластера через API-сервер: планировщик и контроллеры не вызывают друг друга, а читают и меняют объекты через него. Сам API-сервер пишет в etcd, kubelet передает команды среде запуска контейнеров, а сетевой трафик приложений идет своим путем, не через API. Вот что происходит после команды «запусти три экземпляра приложения»:

  1. kubectl отправляет манифест Deployment в API-сервер, тот записывает его в etcd.
  2. Контроллер Deployment создает ReplicaSet, контроллер ReplicaSet - три объекта Pod.
  3. Планировщик назначает каждому Pod узел, где хватает ресурсов.
  4. kubelet на этом узле через среду запуска стартует контейнеры.
  5. Если упал контейнер, kubelet перезапускает его внутри того же Pod. Если Pod удалили или отказал весь узел, Pod становится меньше трех, и контроллер ReplicaSet создает замену.

Docker на узле больше не обязателен: Kubernetes работает со средой запуска через интерфейс CRI, а встроенную прослойку для Docker Engine удалили в версии 1.24. Образы, собранные через docker build, запускаются как раньше - формат общий.

etcd и кворум: почему узлов управления три, а не два

etcd - отдельный open-source проект, распределенное хранилище. Запись в нем подтверждает большинство узлов (кворум), поэтому данные согласованы даже при отказе части машин. Без кворума etcd не принимает изменений, и кластер не может менять состояние: например, новые Pod не назначаются на узлы.

Почему не два узла, видно из таблицы отказоустойчивости etcd:

Узлов etcd Нужно для кворума Сколько можно потерять
1 1 0
2 2 0
3 2 1
4 3 1
5 3 2

Второй узел не добавляет устойчивости: кворум из двух требует обоих, и отказ любого останавливает запись. Поэтому etcd собирают из нечетного числа узлов: три переживают отказ одного, пять - двух. Документация Kubernetes для продакшена рекомендует кластер etcd из пяти узлов и регулярные резервные копии. Если etcd работает на самих узлах control plane (частая схема), это и есть число узлов управления. У некоторых облегченных дистрибутивов Kubernetes хранилище может быть другим, но в стандартной сборке это etcd.

Объекты Kubernetes: Pod, ReplicaSet, Deployment, Service

Приложение в Kubernetes описывают объектами в YAML-манифестах. Связывают объекты метки (labels) - пары «ключ - значение» - и селекторы, которые выбирают объекты по меткам. В примере из практики ниже это выглядит так: на каждом Pod стоит метка app: hello. Селектор Deployment с той же меткой говорит, какими Pod он управляет, а селектор Service - на какие Pod отправлять запросы.

Основные объекты, с которых начинают:

Объект Что делает
Pod Минимальная единица запуска: один или несколько контейнеров с общей сетью и томами. Обычно его создает контроллер, а не человек
ReplicaSet Поддерживает заданное число одинаковых Pod
Deployment Описывает желаемое состояние (образ, число реплик, стратегию обновления) и управляет ReplicaSet. Основной объект для приложений без состояния
Service Дает группе Pod постоянное имя и адрес и распределяет запросы между ними
ConfigMap, Secret Хранят настройки и чувствительные данные отдельно от образа

Получается матрешка: Deployment управляет ReplicaSet, ReplicaSet - Pod. При обновлении образа Deployment создает новый ReplicaSet и постепенно переносит на него Pod, а старый оставляет для отката. В старых материалах можно встретить объект Replication Controller - это предшественник ReplicaSet; сейчас приложения описывают через Deployment, потому что он умеет обновлять и откатывать версии.

Pod при каждом пересоздании получает новый IP-адрес, поэтому обращаются не к нему, а к Service. Тип Service по умолчанию - ClusterIP: адрес доступен только внутри кластера, другим сервисам. В практике ниже нужен доступ к приложению с вашего компьютера, поэтому там выбран NodePort: он дополнительно открывает порт на узле кластера, а команда minikube service hello --url выдает готовый адрес. В облаке для внешнего доступа обычно берут тип LoadBalancer - облако выдает внешний балансировщик.

Чувствительные данные в Secret тоже стоит понимать правильно. В манифесте и в ответах API поле data записано в base64 - это просто способ записи, а не защита: строку легко раскодировать. А в etcd данные Secret по умолчанию хранятся без шифрования. Защищают их отдельно: включают шифрование данных в etcd и ограничивают права на чтение секретов через RBAC.

Как трафик попадает в приложение: Ingress и Gateway API

Адрес ClusterIP доступен только внутри кластера. Запрос пользователя из интернета приходит на домен, например shop.example.com/api, и кто-то должен его принять и решить, какому Service его отдать. Для этого в кластере есть точка входа с правилами по домену и пути. Путь одного запроса: точка входа принимает HTTP-запрос и по правилу выбирает Service, Service отдает запрос одному из своих Pod, а до нужного Pod запрос доводят сетевые правила узла, которые поддерживает kube-proxy или заменяющий его сетевой плагин.

Такой точкой входа для HTTP долго был Ingress - объект с правилами маршрутизации по домену и пути. Сам по себе он ничего не делает: нужен Ingress-контроллер, который читает правила и принимает трафик. API Ingress заморожен: он стабилен и удалять его не планируют, но развивать не будут, и проект Kubernetes рекомендует вместо него Gateway API. Популярный контроллер ingress-nginx больше не получает исправлений, в том числе исправлений безопасности, поэтому для новой установки выбирают поддерживаемую реализацию Gateway API.

Gateway API делит ответственность по ролям: GatewayClass описывает тип балансировщика, Gateway - точку входа с портами и сертификатами, HTTPRoute - маршруты к Service. Команда платформы владеет точкой входа, разработчики меняют свои маршруты.

Практика: первое приложение в Minikube

Для первого запуска подойдет Minikube - локальный кластер из одного узла, где совмещены control plane и рабочая роль. Что нужно до начала:

  • установленные Minikube и kubectl - по официальной инструкции Minikube для вашей ОС;
  • запущенный Docker (на macOS и Windows - Docker Desktop): с драйвером docker Minikube работает внутри контейнера.

Шаг 1. Поднять кластер и убедиться, что kubectl смотрит на него.

minikube start --driver=docker
kubectl config current-context   # ожидаем: minikube
kubectl get nodes                # один узел minikube в статусе Ready

Проверка контекста - не формальность: если kubectl раньше смотрел на рабочий кластер, команды ниже, включая удаление Pod, ушли бы туда. Если отдельного kubectl нет, подойдет встроенный: minikube kubectl -- get nodes.

Шаг 2. Описать приложение. Это учебный пример: Deployment из двух реплик тестового эхо-сервера из документации Minikube и Service типа NodePort. Создайте папку для манифестов:

mkdir hello

Скопируйте первый блок ниже в файл hello/deployment.yaml:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: hello
  labels:
    app: hello
spec:
  replicas: 2
  selector:
    matchLabels:
      app: hello
  template:
    metadata:
      labels:
        app: hello
    spec:
      containers:
        - name: echo
          image: kicbase/echo-server:1.0
          ports:
            - containerPort: 8080
          resources:
            requests:
              cpu: 50m
              memory: 32Mi
            limits:
              memory: 128Mi

Второй блок - в файл hello/service.yaml:

apiVersion: v1
kind: Service
metadata:
  name: hello
spec:
  type: NodePort
  selector:
    app: hello
  ports:
    - port: 8080
      targetPort: 8080

Главное в манифестах - метки. Селектор Deployment (matchLabels) обязан совпадать с метками шаблона Pod, иначе API-сервер отклонит объект. Селектор Service выбирает те же Pod по app: hello - так запросы находят нужные экземпляры.

Шаг 3. Проверить и применить.

kubectl apply -f hello/ --dry-run=server   # проверка на сервере, без изменений
kubectl apply -f hello/
kubectl rollout status deployment/hello
kubectl get deployments,replicasets,pods -l app=hello

Последняя команда показывает всю вложенность: один Deployment, один ReplicaSet с суффиксом в имени и два Pod в статусе Running.

Шаг 4. Открыть приложение.

minikube service hello --url

Команда выведет адрес приложения - какой именно, зависит от ОС и драйвера Minikube. Откройте именно его в браузере или выполните curl <адрес>: если все работает, эхо-сервер ответит строкой «Request served by hello-...» с именем Pod, который обработал запрос, а ниже покажет метод, путь и заголовки вашего запроса. На macOS и Windows с драйвером docker Minikube держит для этого туннель, поэтому терминал с командой не закрывайте, пока проверяете. Запасной путь - kubectl port-forward service/hello 7080:8080 и адрес http://localhost:7080.

Шаг 5. Посмотреть самовосстановление, масштабирование и откат.

Сначала самовосстановление. Удалите Pod и сразу понаблюдайте за списком:

kubectl delete pod -l app=hello --wait=false   # удалить Pod (только в учебном кластере)
kubectl get pods -l app=hello -w               # наблюдать за списком; выход - Ctrl+C

Удаление идет в фоне, поэтому список сначала покажет старые Pod в статусе Terminating, а затем Error, рядом с новыми в ContainerCreating. Дождитесь, пока появятся два Pod с новыми именами в статусе Running, и остановите наблюдение сочетанием Ctrl+C. Это и есть самовосстановление: Pod удалили, ReplicaSet создал замену. Если процесс в контейнере просто завершится с ошибкой, Pod останется тем же, а контейнер в нем перезапустит kubelet - в колонке RESTARTS вырастет счетчик.

Теперь масштабирование и откат. Увеличьте число реплик, посмотрите переменные окружения в шаблоне Pod, измените шаблон и откатите изменение:

kubectl scale deployment/hello --replicas=3
kubectl set env deployment/hello --list        # переменные шаблона: пока пусто
kubectl set env deployment/hello GREETING=v2   # изменение шаблона = новая ревизия
kubectl rollout status deployment/hello
kubectl set env deployment/hello --list        # GREETING=v2
kubectl rollout history deployment/hello       # ревизии 1 и 2
kubectl rollout undo deployment/hello          # вернуть предыдущую ревизию
kubectl rollout status deployment/hello
kubectl set env deployment/hello --list        # снова пусто
kubectl get pods -l app=hello                  # три Pod

Команда set env --list печатает строку-заголовок # Deployment hello, container echo и под ней переменные шаблона. До изменения под заголовком пусто, после set env - GREETING=v2, после rollout undo снова пусто. Эхо-сервер эту переменную не показывает, образ приложения не меняется - поэтому это откат изменения шаблона Pod, и увидеть его можно только так. После set env Deployment создаст второй ReplicaSet и заменит Pod по одному, после отката вернется к первому. Число реплик при этом останется тем, что задал scale, - три: откат не отменяет масштабирование. Вернуть две реплики можно отдельной командой kubectl scale deployment/hello --replicas=2. При откате kubectl предупредит, что объект создавался через kubectl apply: в учебном кластере это не страшно, а в рабочем изменения обычно вносят в файлы манифестов и применяют их тем же kubectl apply, чтобы файлы и кластер не расходились.

Шаг 6. Посмотреть кластер в веб-интерфейсе (по желанию). Pod и Deployment, которые вы только что запустили, удобно посмотреть в браузере. Для этого подойдет Headlamp, в Minikube он ставится как аддон:

minikube addons enable metrics-server   # графики потребления ресурсов
minikube addons enable headlamp
minikube service headlamp -n headlamp

Последняя команда откроет страницу входа. Headlamp попросит токен - длинную строку, по которой кластер узнает пользователя. Получите его во втором терминале:

kubectl create token headlamp --duration 24h -n headlamp

Скопируйте выведенную строку целиком и вставьте в форму входа. Команда запрашивает временный токен сроком на сутки, фактический срок задает API-сервер. Токен дает права администратора учебного кластера, поэтому никуда его не пересылайте. Об аддоне есть страница Headlamp в документации Minikube, но способ получения токена там старый и с текущей версией аддона не работает - берите команду выше. Старый Kubernetes Dashboard (minikube dashboard) больше не развивается, поэтому в новых материалах его заменяют Headlamp.

Шаг 7. Свое приложение (по желанию). Если у вас уже есть свое приложение с Dockerfile, его можно запустить так же. Соберите образ docker build -t demo:v1 ., загрузите его в кластер командой minikube image load demo:v1 (у Minikube свое хранилище образов, образ с вашего компьютера он сам не увидит) и укажите в манифесте image: demo:v1 с явным тегом. Как написать Dockerfile и собрать образ, разобрано в статье «Docker для начинающих».

Шаг 8. Уборка.

kubectl delete -f hello/
minikube stop
minikube delete      # удаляет кластер и все его данные

minikube delete необратим, а minikube delete --all удалит все профили Minikube на машине.

Если не получилось

Типовые симптомы и что проверить:

Симптом Вероятная причина Что сделать
minikube start падает с ошибкой драйвера Docker не запущен Запустить Docker и повторить
Pod в ImagePullBackOff или ErrImagePull Кластер не может получить образ: опечатка в имени или теге, нет доступа к реестру, нужны учетные данные, нет сети; для своего образа - он не загружен в Minikube Сначала kubectl describe pod <имя> и раздел Events - там точная причина. Для своего образа - minikube image load и явный тег вместо latest
Pod в Pending Не хватает ресурсов узла kubectl describe pod <имя>, раздел Events
Pod в CrashLoopBackOff Приложение падает при старте kubectl logs <имя> --previous

Чего этому примеру не хватает до продакшена. Для рабочего сервиса подбирают по его задачам проверки готовности и живости (probes), права RBAC, способ хранения секретов и мониторинг. Шаблона «для любого сервиса» здесь нет: liveness-проверка бывает не нужна, если процесс сам завершается при сбое, а плохо настроенная проверка способна сама вызвать каскадные перезапуски; секреты нужны не каждому приложению. Отдельный выбор архитектуры и процесса - как принимать внешний трафик (например, через Gateway API) и как доставлять изменения в кластер (например, через GitOps): решение зависит от команды и инфраструктуры.

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

Для учебы есть и другие локальные кластеры, например kind - он удобен для нескольких узлов на одной машине и для CI. В продакшене берут управляемый Kubernetes облака или платформу для своих серверов. Команды kubectl и манифесты из практики работают везде одинаково.

Выводы

  • Вы запустили приложение в кластере: Deployment поддерживает заданное число Pod, ReplicaSet заменяет удаленные, Service по меткам находит их и отдает запросы.
  • Kubernetes сам держит заданное состояние, но решения остаются за вами: откат запускает человек, а число реплик после отката остается таким, каким его задали.
  • Перед продакшеном для своего сервиса подбирают проверки работоспособности, права, хранение секретов и мониторинг по его задачам, а также выбирают, как принимать трафик и доставлять изменения.

Как перейти от учебного кластера к рабочему

Kubernetes берут там, где сервисов много и их обновляют без простоя: продуктовые команды, внутренние платформы для разработчиков, облачные сервисы. Его устройство нужно знать DevOps- и SRE-инженерам, а все чаще и бэкенд-разработчикам.

Если хотите пройти путь от первого деплоя до рабочей платформы - с GitOps, секретами и защитой кластера, - записывайтесь на курс «Инфраструктурная платформа на основе Kubernetes».

Смежные темы: Docker для начинающих: образ, контейнер, Dockerfile и Compose, CI/CD: что это и как устроено развертывание приложения, Observability: что это и чем наблюдаемость отличается от мониторинга, Рекомендуемая литература по Kubernetes.

FAQ

Чем Kubernetes отличается от Docker Compose? Различие в способе управления. Compose описывает набор сервисов в файле и запускает их на одной машине; с политикой restart (on-failure, always, unless-stopped) Docker перезапустит остановившийся контейнер. Kubernetes устроен иначе: вы описываете желаемое состояние в API, а контроллеры постоянно его поддерживают - держат заданное число Pod у Deployment, создают замену удаленным, обновляют версии по очереди и могут разносить Pod по нескольким узлам. Работать он может и на одном узле, как Minikube в этой статье, и на многих.

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

Почему Kubernetes называют K8s? Это сокращение-нумероним: между первой буквой K и последней s в слове Kubernetes восемь букв. В русскоязычной среде также говорят «кубер».

Не пропустите новые полезные статьи!

Спасибо за подписку!

Мы отправили вам письмо для подтверждения вашего email.
С уважением, OTUS!

Автор
0 комментариев
Для комментирования необходимо авторизоваться
Популярное
Сегодня тут пусто
Снизили цены на 10 дней
Снизили цены на 10 дней
−10% на все курсы со стартом в ноябре, акция действует до 10 числа. Выбирайте направление — начинайте обучение
−10% на все курсы со стартом в ноябре, акция действует до 10 числа. Выбирайте направление — начинайте обучение