Kubernetes для разработчика: Pod, Deployment, Service, Ingress без страха
Kubernetes пугает объёмом — там сотни понятий и тысячи опций. Но для разработчика (не девопса) нужен небольшой набор объектов, на котором держится 90% повседневной работы. Разберём их по линии: как ваше приложение живёт в кластере от контейнера до входящего трафика.
Зачем вообще оркестратор
Без оркестратора вы запускаете контейнеры вручную: docker run на конкретной машине. Если машина падает — приложение падает вместе с ней. Если нагрузка растёт — вручную поднимаете ещё контейнеры и настраиваете перед ними балансировщик. На десятках контейнеров это превращается в ад.
Kubernetes (K8s) — это оркестратор контейнеров. Он берёт пул машин (кластер) и описание того, что должно работать («3 реплики моего сервиса»), и сам следит: поднимает контейнеры, перезапускает упавшие, перераспределяет при падении узлов, масштабирует под нагрузкой. Вы описываете желаемое состояние, а K8s поддерживает его актуальным.
Pod — минимальная единица
Pod — это один или несколько контейнеров, которые работают вместе на одной машине, делят network namespace (общий IP) и тома. Pod — атомарная единица, которой управляет K8s; напрямую контейнеры вы не запускаете, всегда через Pod.
В 95% случаев в Pod один контейнер — ваше приложение. Sidecar-паттерн (второй контейнер в том же Pod для логирования, метрик, прокси) — исключение, оправданное, когда контейнеры должны жить и умирать вместе.
Важное свойство Pod: у него нет постоянной идентичности. IP-адрес Pod меняется при каждом пересоздании, имя случайно. Если контейнер падает — K8s убивает Pod и создаёт новый, с новым IP. Поэтому нельзя обращаться к Pod по IP напрямую: сегодня он один, завтра — другой.
Deployment — рабочая лошадка
Чтобы Pod перезапускался при падении и чтобы их было ровно N, используют контроллер. Самый частый — Deployment:
apiVersion: apps/v1
kind: Deployment
metadata:
name: myapp
spec:
replicas: 3 # хотим 3 живых Pod
selector:
matchLabels:
app: myapp
template: # описание Pod
metadata:
labels:
app: myapp
spec:
containers:
- name: myapp
image: myapp:1.2.3
ports:
- containerPort: 8080
Deployment держит 3 реплики Pod и следит, чтобы всегда было ровно 3 живых. Один Pod упал — поднимется новый. При обновлении образа (image: myapp:1.2.4) Deployment делает rolling update: постепенно заменяет старые Pod на новые, без простоя. Если что-то пошло не так — kubectl rollout undo откатывает.
Это объект, который вы пишете и применяете почти всегда. Pod никогда не создают вручную.
Service — стабильная сеть поверх меняющихся Pod
IP Pod меняется при каждом пересоздании. Чтобы клиенты могли найти ваше приложение, нужен Service — стабильная сетевая абстракция поверх набора Pod:
apiVersion: v1
kind: Service
metadata:
name: myapp
spec:
selector:
app: myapp # обслуживает Pod с этим лейблом
ports:
- port: 80
targetPort: 8080
Service даёт:
- Постоянное DNS-имя (
myapp.default.svc.cluster.localвнутри кластера), которое не меняется. - Балансировку нагрузки между всеми Pod, подходящими под
selector. Запрос по имениmyappуходит на один из 3 Pod, автоматически. - Стабильный IP-адрес (ClusterIP), который тоже не меняется.
Без Service клиенты не нашли бы ваше приложение надёжно: IP Pod, к которому они обратились вчера, сегодня может принадлежать совсем другому сервису или быть мёртвым.
Ingress — вход HTTP-трафика в кластер
Service с типом ClusterIP доступен только изнутри кластера. Чтобы принимать HTTP-запросы извне (из интернета), нужен Ingress. За ним стоит Ingress-контроллер (nginx, traefik, contour), который терминирует TLS и маршрутизирует трафик по доменам/путям к разным Service:
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: myapp
spec:
rules:
- host: api.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: myapp
port:
number: 80
Теперь https://api.example.com направляется на Service myapp, а тот балансирует между Pod. Один Ingress-контроллер в кластере обслуживает много доменов и сервисов.
ConfigMap и Secret — конфигурация
Образ контейнера должен быть одинаковым во всех окружениях (это принцип immutable artifact). Конфигурация, которая различается (URL базы данных, токены, флаги), передаётся снаружи через ConfigMap (нечувствительные данные) и Secret (пароли, токены — хранятся закодированными и передаются защищённее). Они монтируются в Pod как переменные окружения или файлы.
Mental model и ежедневные команды
Вы пишете манифесты Deployment + Service (опционально Ingress, ConfigMap, Secret), применяете их через kubectl apply -f. Дальше K8s поддерживает желаемое состояние за вас.
Команды для ежедневной работы:
kubectl get pods # статус Pod (Running / CrashLoopBackOff)
kubectl get deployments # готовы ли реплики (3/3)
kubectl describe pod <name> # ПОЧЕМУ упал — события, причины
kubectl logs <pod> # логи контейнера
kubectl logs -f <pod> # логи с подпиской (как tail -f)
kubectl get events --sort-by=.lastTimestamp # лента событий в неймспейсе
kubectl exec -it <pod> -- sh # зайти в контейнер с шеллом
kubectl rollout status deployment/myapp # идёт ли ещё rolling update
kubectl rollout undo deployment/myapp # откатить к предыдущей версии
CrashLoopBackOff у Pod — главный сигнал беды. kubectl describe pod покажет, на чём спотыкается контейнер (ошибка в образе, отсутствие ConfigMap, OOMKill, неверный порт).
Что важно понимать про границы
Разработчику не обязательно знать все типы ресурсов (DaemonSet, StatefulSet, Job, CronJob, HorizontalPodAutoscaler, NetworkPolicy, PDB). Это инструменты девопса/платформы. Для повседневной работы достаточно Pod + Deployment + Service + ConfigMap/Secret + Ingress. Это покрывает деплой типового stateless-сервиса.
Stateful-приложения (базы данных, очереди) в K8s разворачивают через StatefulSet, и обычно этим занимается отдельная команда платформы, а не прикладной разработчик. Хорошее правило: не держите состояние в K8s сами, используйте managed-сервисы (RDS, Managed Postgres, Cloud Pub/Sub) — меньше боли.
Короткое summary
Kubernetes для разработчика держится на нескольких понятиях. Pod — минимальная единица, контейнер(ы) с общим network/volumes, без постоянной идентичности. Deployment — контроллер, который держит N реплик Pod, перезапускает упавшие и делает rolling updates. Service — стабильное DNS-имя + балансировка поверх меняющихся Pod. Ingress — вход HTTP-трафика извне, маршрутизация по доменам. ConfigMap/Secret — конфигурация и секреты снаружи образа. Всё остальное (StatefulSet, DaemonSet, HPA) — для специфичных задач, обычно в ведении платформенной команды. Ежедневные команды: get, describe, logs, exec — этого хватает на 90% работы.
Что почитать
- Kubernetes Docs: Kubernetes Components — что входит в кластер.
- Kubernetes Docs: Pods, Deployments, Services — официальный интерактивный tutorial по базовым объектам.
- «Kubernetes: Up and Running» (Burns, Hightower, Beda) — книга от авторов проекта, практический разбор.