Kubernetes для разработчика: Pod, Deployment, Service, Ingress без страха

Инфраструктура4 мин чтения
  • #kubernetes
  • #k8s
  • #pod
  • #deployment
  • #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% работы.

Что почитать