Kubernetes в облаке: развёртывание контейнеров шаг за шагом


Эта статья — практическое руководство для тех, кто знает Docker и хочет научиться разворачивать контейнеризованные приложения в Kubernetes. Мы пройдём весь путь: от теоретической базы до запуска кластера на ноутбуке через Minikube, далее к ручному и автоматическому масштабированию, и, наконец, к переносу того же приложения в управляемый сервис в облаке.

Команды и YAML-манифесты можно копировать и выполнять по ходу чтения — все примеры проверены на актуальных версиях инструментов.

Зачем Kubernetes и зачем именно в облаке

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

Kubernetes построен вокруг декларативной модели. Вы не пишете императивный скрипт «возьми машину A и запусти там контейнер X». Вы описываете в YAML желаемое состояние — «должно быть запущено 3 реплики веб-приложения такой-то версии» — и кластер сам приводит реальное состояние к этому описанию. Если узел падает, поды автоматически переезжают на другие. Если приложение крашится, его перезапускают.

Запустить Kubernetes можно где угодно — на bare-metal серверах, на виртуалках, в собственном датацентре. Но в 2026 году подавляющее большинство продакшен-нагрузок крутится на управляемых сервисах в облаке. Причина простая: облачный провайдер берёт на себя самую сложную часть — control plane, обновления, отказоустойчивость, — а вы платите за это фиксированную сумму (порядка $0.10 в час за кластер) и занимаетесь только своими приложениями.

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

Архитектура Kubernetes за пять минут

Без минимальной картины устройства кластера дальнейшие команды будут непонятны. Поэтому быстро пройдёмся по терминам.

Кластер: control plane и worker nodes

Кластер Kubernetes состоит из двух частей: control plane (управляющий слой, иногда называют «мастер-нодой») и набор worker nodes — рабочих машин, на которых запускаются ваши приложения. В учебном кластере control plane может стоять на той же машине, что и воркеры. В продакшене control plane обычно реплицируется на 3+ узла для отказоустойчивости.

Control plane принимает решения: где запустить новый под, что делать, если узел отвалился, как обработать API-запрос. Воркеры выполняют эти решения: запускают контейнеры, обеспечивают сеть, репортят статус обратно.

Компоненты control plane

Их пять, и каждый отвечает за свою задачу:

  • kube-apiserver — точка входа в кластер. Принимает все команды (от kubectl, от других компонентов, от внешних систем), валидирует их и записывает изменения. Если apiserver недоступен — кластер не управляем.
  • etcd — распределённое key-value хранилище, единственный источник правды о состоянии кластера. Все объекты (поды, сервисы, секреты) живут именно в etcd. Потеря etcd — это потеря кластера.
  • kube-scheduler — выбирает, на какой узел поставить новый под, исходя из ресурсов, ограничений (taints/tolerations), правил affinity и т.д. Сам под он не запускает, только назначает.
  • kube-controller-manager — набор контроллеров, которые крутятся в цикле и тянут реальное состояние к желаемому. Например, Node Controller замечает упавшие узлы, ReplicaSet Controller следит за числом реплик.
  • cloud-controller-manager — мост к API облачного провайдера. Это он создаёт LoadBalancer в AWS, когда вы пишете type: LoadBalancer в манифесте Service.

Что есть на каждой ноде

На каждом воркере (и обычно на control-plane-узле тоже) работают три компонента:

  • kubelet — агент, который слушает API server, получает спецификации подов и говорит контейнерному рантайму их запустить. Он же шлёт обратно статус и метрики здоровья.
  • kube-proxy — поддерживает сетевые правила, благодаря которым работают Service-объекты. Когда вы обращаетесь к сервису по имени — это во многом заслуга kube-proxy.
  • container runtime — то, что непосредственно запускает контейнеры. В современных кластерах это обычно containerd или CRI-O; Docker как рантайм давно убран из upstream Kubernetes (хотя образы Docker по-прежнему отлично работают, потому что используется тот же OCI-формат).

Иерархия объектов

Вы будете писать YAML на следующие сущности (в порядке от низкого уровня к высокому):

  • Pod — базовый исполняемый объект, самая маленькая единица в Kubernetes. Один или несколько контейнеров с общей сетью и хранилищем. Поды эфемерны: упавший под не «воскрешается» — на его место создаётся новый объект с другим IP.
  • ReplicaSet — следит, чтобы было запущено N одинаковых подов. Напрямую вы его почти никогда не пишете.
  • Deployment — обёртка над ReplicaSet, добавляющая rolling-обновления и откаты. Это основной объект для stateless-приложений.
  • Service — стабильная виртуальная точка входа к набору подов. Решает проблему «как мне найти под, если IP постоянно меняется».
  • Ingress — HTTP/HTTPS-роутинг с внешним адресом, путями и TLS. Уровень выше Service.

Для stateful-нагрузок (базы данных) есть StatefulSet, для запуска агента на каждом узле — DaemonSet, для одноразовых задач — Job, для запусков по расписанию — CronJob.

Все эти объекты живут внутри namespace — логического контейнера для группы ресурсов. По умолчанию вы работаете в namespace default, но в реальном кластере обычно делают разные namespace под окружения, команды или продукты, чтобы изолировать ресурсы и упростить управление правами. К теме namespace мы ещё вернёмся.

Декларативная модель в одной картинке

Когда вы выполняете kubectl apply -f deployment.yaml, происходит примерно следующее:

  1. kubectl шлёт YAML в API server.
  2. API server валидирует и пишет объект в etcd.
  3. Deployment-контроллер видит новый объект и создаёт ReplicaSet.
  4. ReplicaSet-контроллер создаёт нужное число Pod-объектов (но без узла).
  5. Scheduler находит подходящие узлы и записывает в каждый под nodeName.
  6. kubelet на этом узле видит «свой» под, скачивает образ и запускает контейнер.
  7. Статус летит обратно в API server и в etcd.

Главное здесь — каждый шаг автономен. Если упадёт scheduler, уже запущенные поды продолжат работать. Если временно отвалится etcd — kubelet будет крутить уже запущенные контейнеры по последней известной конфигурации. Это и есть та самая отказоустойчивость, ради которой Kubernetes придумывали.

Локальный кластер на Minikube

Прежде чем платить облачному провайдеру, имеет смысл потрогать всё локально. Для этого есть Minikube — официальный инструмент проекта Kubernetes, который поднимает однонодовый кластер у вас на машине.

Что такое Minikube и для чего он не нужен

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

  • знакомства с Kubernetes;
  • разработки и отладки приложений локально;
  • тестирования манифестов перед деплоем в облако.

Он не подходит для продакшена: одна нода, нет HA, нет реальных облачных интеграций.

Альтернативы Minikube для локальной разработки: kind (Kubernetes IN Docker), k3s (легковесный дистрибутив), Docker Desktop со встроенным Kubernetes. Все они достойны, но Minikube остаётся самым популярным и хорошо документированным вариантом для старта.

Что нужно установить заранее

Для работы Minikube требуется один из менеджеров контейнеров или виртуализации: Docker, Podman, VirtualBox, Hyper-V, KVM, Parallels или VMware. Самый простой путь — Docker Desktop (на macOS и Windows) или просто Docker Engine (на Linux). Минимальные требования к железу: 2 CPU, 2 GB RAM свободной памяти и около 20 GB диска.

Дополнительно понадобится kubectl — основной CLI для общения с кластером. Без него Minikube тоже работает, но смысла в этом мало.

Установка

macOS (через Homebrew):
brew install minikube kubectl
Linux (x86-64, через прямой бинарник — это рекомендуемый официальной документацией способ):
curl -LO https://storage.googleapis.com/minikube/releases/latest/minikube-linux-amd64
sudo install minikube-linux-amd64 /usr/local/bin/minikube
rm minikube-linux-amd64

Для kubectl на Linux:

curl -LO "https://dl.k8s.io/release/$(curl -L -s https://dl.k8s.io/release/stable.txt)/bin/linux/amd64/kubectl"
sudo install -o root -g root -m 0755 kubectl /usr/local/bin/kubectl
Windows (через Chocolatey):
choco install minikube kubernetes-cli

Проверим, что всё на месте:

minikube version
kubectl version --client

Запуск кластера

Команда запуска одна:

minikube start --driver=docker --cpus=2 --memory=4096

Что происходит при первом запуске: Minikube скачивает базовый образ Kubernetes (это медленно, минут 5–10 в зависимости от канала), поднимает контейнер с control plane и kubelet, прописывает kubeconfig в ~/.kube/config и переключает текущий контекст на minikube. На последующие запуски нужны секунды.

Флаги --cpus и --memory ограничивают, сколько ресурсов хоста сможет съесть кластер. Без них Minikube возьмёт значения по умолчанию, которые на ноутбуке могут оказаться избыточными. Если у вас MacBook на Apple Silicon — --driver=docker работает отлично. Для Linux без Docker можно указать --driver=kvm2 или --driver=virtualbox.

Если запуск прошёл успешно, проверяем:

minikube status
kubectl get nodes

Должно быть что-то вроде:

NAME       STATUS   ROLES           AGE   VERSION
minikube   Ready    control-plane   1m    v1.34.x

Одна нода со статусом Ready — этого нам пока достаточно.

Дашборд и аддоны

У Minikube есть встроенный веб-интерфейс — официальный Kubernetes Dashboard. Включить и открыть его в браузере можно одной командой:

minikube dashboard

Команда повисит в терминале — это нормально, она держит проксирование. Для повседневной работы дашборд не критичен, но в первые часы знакомства полезен: видно поды, деплойменты, события, логи. В реальных проектах официальный Dashboard используют редко — большинство команд предпочитают k9s (TUI), Lens или Headlamp.

Аддоны — это набор опциональных компонентов, которые Minikube умеет включать парой команд. Список:

minikube addons list

В этой статье нам понадобится metrics-server (для автоскейлинга, к нему вернёмся ниже) и при желании ingress (для HTTP-роутинга). Включаются так:

minikube addons enable metrics-server
minikube addons enable ingress

На этом этапе у нас есть рабочий кластер. Время что-нибудь в нём запустить.

Первое приложение: поды, деплойменты, сервисы

В этом разделе мы развернём nginx и пройдёмся по объектам Kubernetes снизу вверх — от голого пода до сервиса с внешним доступом.

Pod: базовая единица исполнения

Чисто учебно создадим один Pod. В реальной жизни так почти никогда не делают, но раз — для понимания — стоит.

Создайте файл pod.yaml:

apiVersion: v1
kind: Pod
metadata:
  name: nginx-pod
  labels:
    app: nginx
spec:
  containers:
  - name: nginx
    image: nginx:1.27
    ports:
    - containerPort: 80

Разберём поля:

  • apiVersion: v1 — Pod относится к core-группе API, у которой версия v1.
  • kind: Pod — тип объекта.
  • metadata.name — уникальное имя в рамках namespace (мы работаем в default, его можно не указывать).
  • metadata.labels — пары ключ-значение, по которым к поду потом смогут «прицепиться» сервисы и контроллеры.
  • spec.containers — список контейнеров. У нас один — nginx версии 1.27, слушает порт 80 внутри пода.

Применяем:

kubectl apply -f pod.yaml
kubectl get pods

Через несколько секунд под перейдёт в статус Running. Логи:

kubectl logs nginx-pod

Зайти внутрь можно через kubectl exec:

kubectl exec -it nginx-pod -- /bin/bash

Теперь главный вопрос: что произойдёт, если этот под умрёт? Проверим:

kubectl delete pod nginx-pod
kubectl get pods

Ничего. Под удалён, и его никто не воскресит. В этом и проблема голых подов — они не самовосстанавливаются. Поэтому в реальной жизни используют контроллеры более высокого уровня, прежде всего Deployment.

Deployment: репликация и rolling-обновления

Deployment — это API-ресурс верхнего уровня для stateless-приложений. Он гарантирует, что нужное число реплик запущено, автоматически заменяет упавшие поды и поддерживает rolling-обновления и откаты.

Удалим (если ещё не удалили) предыдущий под и создадим deployment.yaml:

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

Разберём, что нового:

  • apiVersion: apps/v1 — Deployment живёт в группе apps, не в core.
  • spec.replicas: 3 — желаемое число подов.
  • spec.selector.matchLabels — какие поды Deployment считает «своими». Должны совпадать с spec.template.metadata.labels. Несовпадение — самая частая ошибка новичков.
  • spec.template — это, по сути, шаблон Pod, по которому будут создаваться реплики.
  • resources.requests — сколько CPU и памяти под просит у планировщика. На основании этих чисел scheduler решает, поместится ли под на узел.
  • resources.limits — жёсткий потолок. Если контейнер пытается использовать больше CPU — его троттлят; если больше памяти — его убивают (OOMKilled).

Указывать requests критически важно: без них не будет работать Horizontal Pod Autoscaler, о котором речь ниже. Единица CPU 100m — это 100 «милли-CPU», то есть 0.1 ядра.

Применяем и смотрим:

kubectl apply -f deployment.yaml
kubectl get deployments
kubectl get pods -l app=nginx

Должно быть три пода со статусом Running. Под капотом Deployment создал ReplicaSet, а тот — три Pod-объекта.

Self-healing на практике

Сейчас удобный момент проверить заявленную «самовосстанавливаемость». Удалим один из подов:

POD_NAME=$(kubectl get pods -l app=nginx -o jsonpath='{.items[0].metadata.name}')
kubectl delete pod $POD_NAME
kubectl get pods -l app=nginx

Через секунду-две вы увидите, что общее число подов снова равно трём — ReplicaSet создал замену. То же произойдёт, если упадёт сам контейнер внутри пода (правда, тут есть нюансы с restartPolicy и probes — это тема отдельной статьи).

Service: стабильная точка входа

У каждого пода свой IP-адрес внутри кластера, но эти адреса меняются при пересоздании подов. Чтобы другие части системы могли надёжно обращаться к нашему приложению, нужна абстракция уровня выше — Service. Service — это виртуальный IP и DNS-имя, за которыми kube-proxy балансирует трафик между текущими живыми подами.

Есть три основных типа сервисов:

  • ClusterIP (по умолчанию) — сервис доступен только внутри кластера. Используется для внутренней коммуникации между микросервисами.
  • NodePort — открывает на каждом узле кластера один и тот же порт в диапазоне 30000–32767. Удобно для Minikube, неудобно для прода.
  • LoadBalancer — просит у облачного провайдера создать настоящий балансировщик нагрузки с публичным IP. В Minikube без minikube tunnel это не работает; в облаке — работает «из коробки».

Создадим service.yaml:

apiVersion: v1
kind: Service
metadata:
  name: nginx-service
spec:
  type: NodePort
  selector:
    app: nginx
  ports:
  - port: 80
    targetPort: 80
    nodePort: 30080

selector: app: nginx — это и есть та строчка, которая связывает сервис с подами Deployment. Сервис будет балансировать трафик на все поды с лейблом app: nginx. port — порт, на котором сервис слушает внутри кластера; targetPort — порт пода, куда трафик идёт; nodePort — внешний порт на узле.

Кстати, к сервису внутри кластера можно обращаться не только по ClusterIP, но и по имени. В кластере по умолчанию работает CoreDNS, который резолвит nginx-service в ClusterIP сервиса. Полное FQDN-имя выглядит как nginx-service.default.svc.cluster.local, но из подов того же namespace достаточно короткого имени. Именно поэтому в нагрузочной демке ниже мы пишем просто http://php-apache — это работает.

Применяем:

kubectl apply -f service.yaml
kubectl get services

В Minikube открыть приложение в браузере проще всего так:

minikube service nginx-service --url

Команда вернёт URL вида http://192.168.49.2:30080, по которому открывается nginx-страничка по умолчанию. Поздравляю, у вас в кластере крутится первое работающее приложение.

Rolling-обновление и откат

Допустим, мы хотим обновить nginx с 1.27 на 1.28. Меняем тег образа в deployment.yaml:

        image: nginx:1.28

Применяем и наблюдаем:

kubectl apply -f deployment.yaml
kubectl rollout status deployment/nginx-deployment

Под капотом Kubernetes создаёт новый ReplicaSet с новым тегом и постепенно гасит старые поды, добавляя новые. Параметрами maxUnavailable и maxSurge (поля внутри spec.strategy.rollingUpdate) можно регулировать, насколько агрессивно идёт обновление. По умолчанию — 25% и 25%, что означает «не больше 25% подов могут быть недоступны и не больше 125% реплик в моменте».

Если что-то пошло не так, откат — одна команда:

kubectl rollout undo deployment/nginx-deployment

История изменений деплоймента доступна через kubectl rollout history.

Масштабирование: от ручного к автоматическому

Три реплики — это хорошо, но реальные нагрузки колеблются. Вечером пик, ночью затишье. Платить за избыточные мощности круглосуточно невыгодно. На помощь приходят два механизма масштабирования: ручной и автоматический.

Ручное масштабирование

Самый простой способ — императивная команда:

kubectl scale deployment/nginx-deployment --replicas=5
kubectl get pods -l app=nginx

Это работает, но есть проблема: ваш YAML-файл и реальное состояние кластера разошлись. В YAML по-прежнему replicas: 3, в кластере — 5. При следующем kubectl apply -f deployment.yaml число реплик откатится. Поэтому в зрелых командах ручной kubectl scale используют разве что для разовой реакции на инцидент, а основным способом управления конфигурацией становится Git: меняем число в файле, делаем коммит, CI применяет изменения. Это и есть GitOps-подход, на котором, например, построены ArgoCD и Flux.

Horizontal Pod Autoscaler: что это такое

HPA — это объект Kubernetes, который автоматически меняет число реплик вашего Deployment (или StatefulSet) в зависимости от нагрузки. Если средняя загрузка CPU подов превышает целевой порог — HPA добавляет реплики; если падает ниже — убирает. Горизонтальное масштабирование (добавление новых подов) обычно предпочтительнее вертикального (увеличения ресурсов уже запущенных подов), потому что лучше распределяет нагрузку и не требует рестарта приложения.

Помимо HPA, в Kubernetes есть Vertical Pod Autoscaler (увеличивает CPU/memory у существующих подов) и Cluster Autoscaler (добавляет и удаляет ноды). На практике обычно сочетают HPA + Cluster Autoscaler: HPA добавляет поды, а если нодам не хватает ресурсов — Cluster Autoscaler докидывает узлы.

Подготовка: metrics-server

HPA не умеет читать загрузку подов «из воздуха» — ему нужен источник метрик. Базовая установка — metrics-server, отдельный компонент, который собирает CPU/memory с kubelet'ов и отдаёт через Metrics API.

В Minikube уже есть готовый аддон:

minikube addons enable metrics-server

Подождите минуту и проверьте, что метрики идут:

kubectl top nodes
kubectl top pods

Если видите значения CPU и памяти — всё работает. Если получаете metrics not available yet — подождите ещё пару минут.

Важный момент: HPA вычисляет процент утилизации относительно resources.requests. Если в манифесте Deployment не указан requests.cpu — HPA выдаст ошибку и не будет масштабировать. У нас в Deployment выше уже стоит cpu: "100m", так что мы готовы.

Манифест HPA

Для большей наглядности соберём сразу HPA на демо-приложение. Вместо nginx удобнее взять что-то, что умеет реально нагружать CPU — официальный туториал Kubernetes использует php-apache. Заменим Deployment на:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: php-apache
spec:
  replicas: 1
  selector:
    matchLabels:
      run: php-apache
  template:
    metadata:
      labels:
        run: php-apache
    spec:
      containers:
      - name: php-apache
        image: registry.k8s.io/hpa-example
        ports:
        - containerPort: 80
        resources:
          requests:
            cpu: 200m
          limits:
            cpu: 500m
---
apiVersion: v1
kind: Service
metadata:
  name: php-apache
  labels:
    run: php-apache
spec:
  ports:
  - port: 80
  selector:
    run: php-apache

Применяем:

kubectl apply -f php-apache.yaml

Создаём HPA-объект:

apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
  name: php-apache
spec:
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: php-apache
  minReplicas: 1
  maxReplicas: 10
  metrics:
  - type: Resource
    resource:
      name: cpu
      target:
        type: Utilization
        averageUtilization: 50

Что здесь происходит: HPA смотрит на Deployment php-apache, держит число реплик в диапазоне от 1 до 10 и стремится к тому, чтобы средняя загрузка CPU по всем подам была около 50% от их requests.cpu (то есть от 200m).

Применяем:

kubectl apply -f hpa.yaml
kubectl get hpa

Ту же HPA можно создать одной командой без YAML:

kubectl autoscale deployment php-apache --cpu-percent=50 --min=1 --max=10

Демо нагрузки

Нагрузим приложение и посмотрим, как HPA отреагирует. В отдельном терминале запустим бесконечный цикл запросов к сервису:

kubectl run -i --tty load-generator --rm \
  --image=busybox:1.28 --restart=Never \
  -- /bin/sh -c "while sleep 0.01; do wget -q -O- http://php-apache; done"

В исходном терминале наблюдаем:

kubectl get hpa --watch

Через несколько минут вы увидите, как TARGETS (текущая загрузка) подскочит выше 50%, а REPLICAS начнёт расти. Параллельно kubectl get pods покажет появление новых реплик php-apache.

После остановки нагрузки (Ctrl+C в окне с load-generator) пройдёт около пяти минут, и HPA постепенно уменьшит число реплик обратно до минимума. Это не баг и не задержка — это так называемое stabilization window для масштабирования вниз. По умолчанию scale-down имеет окно стабилизации в 300 секунд: HPA берёт максимальное рекомендуемое число реплик за этот период, чтобы избежать «флапанья» (постоянного добавления и удаления подов при колебаниях нагрузки). Scale-up по умолчанию реагирует мгновенно. Эти параметры настраиваются через поле behavior в манифесте HPA — туда можно указать свои policies для scale-up и scale-down, разные периоды и шаги.

Что важно помнить про HPA

Несколько вещей, о которые часто спотыкаются:

  • Без resources.requests у контейнеров HPA не работает.
  • Без metrics-server (или альтернативного провайдера метрик) HPA не работает.
  • HPA масштабирует только сами поды. Если все ноды кластера уже забиты — новые поды зависнут в статусе Pending до тех пор, пока кто-то не добавит ноды. В облаке этим занимается Cluster Autoscaler (или его более современная замена в AWS — Karpenter).
  • Скейлить можно не только по CPU. Custom metrics (через prometheus-adapter) позволяют масштабироваться по числу запросов в секунду, длине очереди в RabbitMQ, любому бизнес-показателю.
  • HPA и VPA нельзя одновременно включать на одну и ту же метрику (например, оба на CPU) — они начнут конфликтовать.

Переход в облако с Cloud4Y

Minikube — отличная песочница, но для реального проекта нужен кластер, который не лежит, когда ноутбук закрыт, который сам обновляется и за которым кто-то следит. Это и есть managed Kubernetes: облачный провайдер берёт на себя установку и поддержку control plane, его высокую доступность, регулярные обновления и интеграцию с инфраструктурой. Worker nodes остаются в вашей зоне ответственности, но даже их можно отдать на откуп автоскейлингу.

Cloud4Y предоставляет услугу Kubernetes-as-a-Service (KaaS). Под капотом — Container Service Extension (CSE), расширение VMware Cloud Director, через которое арендатор разворачивает полноценные K8s-кластеры внутри своего облачного тенанта. Можно поднять кластер из веб-панели Cloud Director (меню More → Kubernetes Container Clusters → Add) или через CLI vcd cse cluster create — время развёртывания, по данным Cloud4Y, сокращается с дней до 15 минут.

Что вы получаете «из коробки»:

  • автоматизацию жизненного цикла кластера — создание, масштабирование, обновление, удаление через панель или CLI;
  • горизонтальный автоскейлинг ресурсов в заданных границах — платите только за реально используемое;
  • встроенный мониторинг, логирование и сбор метрик для анализа состояния кластера;
  • поддержку MetalLB как реализации сервисов типа LoadBalancer и NFS как persistent storage;
  • размещение в двух географически разнесённых ЦОД и инфраструктуру, соответствующую требованиям ФЗ-152.

Главное при переезде с Minikube — те же YAML-манифесты, что мы писали в этой статье, переносятся практически без изменений. Поды, деплойменты, сервисы, HPA — всё работает как раньше. Меняется только обвязка: тип сервиса NodePort заменяется на LoadBalancer, и кластер сам поднимает балансировщик через MetalLB. В этом и состоит главная ценность Kubernetes как платформы — один раз изучили, дальше работаете везде.

Что дальше изучить

Эта статья — фундамент. Чтобы строить что-то серьёзное, понадобится знание ещё нескольких ключевых тем:

  • Ingress и Ingress Controllers (nginx-ingress, Traefik) — HTTP/HTTPS-роутинг с одним внешним IP на десятки сервисов, TLS-терминация, WAF.
  • Liveness и readiness probes — без них Kubernetes не знает, когда контейнер «жив, но не готов» или «нужно перезапустить». Базовая вещь, которая должна быть в любом серьёзном Deployment.
  • ConfigMap и Secret — отделение конфигурации от кода, хранение чувствительных данных. Важный нюанс: содержимое Secret в etcd по умолчанию закодировано в base64, а не зашифровано. В managed-кластерах это решается включением шифрования etcd через KMS — в проде обязательно. Для секретов, которые нужно версионировать в Git, используйте Sealed Secrets или External Secrets Operator.
  • Persistent Volumes и StorageClass — как работать с дисками для stateful-приложений.
  • StatefulSet — для баз данных и других нагрузок с устойчивой идентичностью подов.
  • Helm — пакетный менеджер для Kubernetes; вместо двадцати YAML-файлов вы устанавливаете «чарт» одной командой.
  • Namespaces и RBAC — изоляция окружений и разграничение прав.
  • Observability: Prometheus + Grafana для метрик, Loki/ELK для логов, OpenTelemetry для трейсов.
  • GitOps (ArgoCD, Flux) — управление кластером через Git как источник правды.

Типичные ошибки новичков

Несколько граблей, на которые наступают практически все:

  • Забыли resources.requests — HPA молча не работает, scheduler принимает странные решения. Указывайте всегда.
  • Несовпадение лейблов между Deployment'овским selector и шаблоном пода — поды создаются, но Deployment их не видит. Сервисы по той же причине не находят поды.
  • Тег latest в имени образа — невоспроизводимые деплои, неработающие откаты, неожиданные перезапуски. Используйте конкретные версии.
  • Секреты в Git — даже в приватном репозитории это плохая практика. Sealed Secrets или External Secrets Operator решают проблему.
  • Один namespace default для всего — рано или поздно тестовое приложение «убьёт» продакшен. Namespaces по окружениям, по командам или по продуктам — обязательно.
  • Привилегированные контейнеры по умолчанию — security context должен быть runAsNonRoot: true, плюс настройка Pod Security Standards (PSS) — стандарта, который с Kubernetes 1.25 заменил устаревший PodSecurityPolicy.
  • Игнорирование probes — kubelet не понимает, готов ли ваш под принимать трафик. Сервис балансирует на не готовый под, пользователи видят ошибки.

Чек-лист перед продакшеном

Краткий список того, что должно быть в любом серьёзном Deployment-манифесте:

  • resources.requests и resources.limits для каждого контейнера.
  • livenessProbe (Kubernetes перезапустит зависший контейнер) и readinessProbe (Service не пошлёт трафик в неготовый под).
  • Стратегия обновления RollingUpdate с разумными maxUnavailable и maxSurge.
  • imagePullPolicy: IfNotPresent (по умолчанию для тегированных образов) и явные версии вместо latest.
  • Минимально необходимые права в RBAC для сервис-аккаунта пода.
  • Ограниченные сетевые политики (NetworkPolicy), если в кластере несколько проектов.
  • Бэкапы etcd — или доверие managed-провайдеру, что он их делает (большинство managed-сервисов делают это автоматически).
  • Мониторинг ключевых метрик: CPU/RAM подов, restart count, ошибки в логах, latency приложения.

Заключение

Мы прошли путь от «что такое Kubernetes» до запущенного в облаке кластера. Главные мысли, которые стоит унести:

Kubernetes построен вокруг декларативной модели: вы описываете желаемое состояние, контроллеры приводят к нему реальное. Все те команды и YAML, что мы писали, — это варианты этой одной идеи.

Локальный Minikube и облачный managed-Kubernetes — это один и тот же Kubernetes. Манифесты переносятся между ними почти без изменений. Это и делает Kubernetes настолько ценным навыком: один раз изучили — работаете везде.

Облако даёт три реальных преимущества по сравнению с self-managed: managed control plane (его сопровождает провайдер), интеграция с балансировщиками и хранилищами, автоматическое масштабирование на уровне нод. За это вы платите фиксированную почасовую плату; для production-нагрузок она почти всегда меньше, чем стоимость инженерного времени на поддержку своего кластера.

Стартовая планка входа высокая, но дальше идёт быстро. Пара недель практики — и вы спокойно выкатываете приложения, настраиваете автоскейлинг, читаете манифесты в чужих репозиториях. Главное — не пытайтесь сразу разобраться во всём; начните с тех объектов, что разобраны в этой статье, и постепенно добавляйте новые по мере необходимости.


Полезный материал?
0
0
Автор: Всеволод
опубликовано: 08.05.2026
Читайте нас: 
Последние статьи
Вверх!