Эта статья — практическое руководство для тех, кто знает 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, происходит примерно следующее:
- kubectl шлёт YAML в API server.
- API server валидирует и пишет объект в etcd.
- Deployment-контроллер видит новый объект и создаёт ReplicaSet.
- ReplicaSet-контроллер создаёт нужное число Pod-объектов (но без узла).
- Scheduler находит подходящие узлы и записывает в каждый под
nodeName. - kubelet на этом узле видит «свой» под, скачивает образ и запускает контейнер.
- Статус летит обратно в 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-нагрузок она почти всегда меньше, чем стоимость инженерного времени на поддержку своего кластера.
Стартовая планка входа высокая, но дальше идёт быстро. Пара недель практики — и вы спокойно выкатываете приложения, настраиваете автоскейлинг, читаете манифесты в чужих репозиториях. Главное — не пытайтесь сразу разобраться во всём; начните с тех объектов, что разобраны в этой статье, и постепенно добавляйте новые по мере необходимости.