Поды в Kubernetes: что это такое, роль в архитектуре и жизненный цикл с примерами команд | AdminWiki

Поды в Kubernetes: что это такое, роль в архитектуре и жизненный цикл с примерами команд

03 октября 2026 14 мин. чтения
Содержание статьи

Что такое под в Kubernetes и почему это не контейнер

Под (Pod) в Kubernetes - минимальная вычислительная единица, которую можно создать и которой можно управлять через API. Это не контейнер, а обёртка над одним или несколькими контейнерами, которые совместно используют сетевое пространство и тома. Планировщик, kubelet и контроллеры работают именно с подами: ставят их на узлы, перезапускают, удаляют и учитывают в квотах.

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

Минимальный манифест пода с одним контейнером nginx выглядит так:

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

Применяем и смотрим результат:

kubectl apply -f pod.yaml
kubectl get pods
kubectl get pods -o wide

В выводе kubectl get pods колонки означают: NAME - имя пода, READY - сколько контейнеров готовы из общего числа, STATUS - отображаемое поле для удобства пользователя (фаза пода или причина сбоя), RESTARTS - число перезапусков, AGE - время с момента создания. Расширенный вывод добавляет IP пода и NODE, на который его поставил планировщик.

Помимо прикладных контейнеров под может содержать init-контейнеры, которые отрабатывают последовательно до старта основных, и sidecar-контейнеры, работающие параллельно с приложением. Все они описаны в одном манифесте и делят ресурсы пода.

Один или несколько контейнеров: зачем нужны multi-container поды

Сценарии с несколькими контейнерами в одном поде сводятся к трём паттернам:

  • Sidecar - вспомогательный контейнер расширяет функциональность основного. Типичный пример: приложение пишет логи в файл, а sidecar с fluentd читает этот файл и отправляет записи в централизованное хранилище.
  • Ambassador - контейнер-прокси, через который основной контейнер ходит во внешний мир: например, подключение к базе через прокси с шифрованием и балансировкой.
  • Adapter - контейнер приводит вывод приложения к единому формату. Так экспортёр метрик превращает локальные счётчики приложения в формат, понятный системе мониторинга.

Общие у контейнеров пода только сеть и тома. Файловые системы у них отдельные: каждый контейнер запускается из своего образа и не видит файлы соседа, если их не положить в общий том. Поэтому обмен строят либо через localhost, либо через смонтированный volume.

Multi-container поды - продвинутый сценарий. Для веб-сервера, очереди или API в большинстве случаев достаточно одного контейнера, а лишние sidecar добавляют потребление памяти и усложняют отладку.

Общее сетевое пространство и тома: что это даёт на практике

У пода один IP-адрес и один набор портов на все контейнеры. Практическое следствие: два контейнера в одном поде не могут слушать один и тот же порт - второй не стартует, потому что порт занят. Основной контейнер открывает 80, sidecar с метриками - 9090, и оба доступны по адресу пода.

Для обмена файлами внутри пода используют том emptyDir. Он создаётся вместе с подом и живёт ровно столько же: при перезапуске контейнера файлы в нём остаются на месте, а при удалении и пересоздании пода том очищается. Пример пода, где два контейнера делят каталог /data:

apiVersion: v1
kind: Pod
metadata:
  name: shared-volume-pod
spec:
  containers:
    - name: writer
      image: busybox
      command: ["sh", "-c", "while true; do date >> /data/log.txt; sleep 5; done"]
      volumeMounts:
        - name: shared-data
          mountPath: /data
    - name: reader
      image: busybox
      command: ["sh", "-c", "tail -f /data/log.txt"]
      volumeMounts:
        - name: shared-data
          mountPath: /data
  volumes:
    - name: shared-data
      emptyDir: {}

Кроме emptyDir в под монтируют configMap и secret с конфигурацией, а также persistentVolumeClaim для постоянного хранения: этот том переживает пересоздание пода. Тома описываются на уровне пода и подключаются в контейнеры через volumeMounts.

Архитектурная роль пода: место в иерархии объектов Kubernetes

Под - низкоуровневая единица. Напрямую (bare pods) их создают для отладки и разовых проверок, потому что такой под не пересоздаётся: удалили - и он исчез, а при падении узла не переедет на другой. В продакшене подами управляют контроллеры: Deployment, ReplicaSet, StatefulSet, DaemonSet. Они следят за нужным количеством реплик, обновляют шаблон пода и восстанавливают его после сбоев.

Иерархия для stateless-приложений выглядит так: Deployment описывает желаемое состояние, создаёт ReplicaSet, а ReplicaSet держит нужное число подов. Deployment с replicas: 3 в итоге даст три пода на узлах кластера. Проверить всю цепочку можно тремя командами:

kubectl get deployments
kubectl get replicasets
kubectl get pods

Deployment и ReplicaSet: как работает масштабирование и обновление

ReplicaSet отвечает за количество подов, Deployment - за версию шаблона и историю обновлений. Когда вы меняете образ или параметры в шаблоне пода, Deployment создаёт новый ReplicaSet и постепенно переносит реплики из старого в новый. Так получается обновление без простоя.

Есть две стратегии. RollingUpdate (по умолчанию) заменяет поды партиями, ориентируясь на параметры maxSurge и maxUnavailable. Recreate сначала удаляет все старые поды и только потом поднимает новые: короткий простой в обмен на простую логику, подходит приложениям без поддержки параллельных версий.

kubectl set image deployment/nginx nginx=nginx:1.25
kubectl rollout status deployment/nginx
kubectl rollout undo deployment/nginx

Первая команда меняет образ, вторая показывает ход обновления, третья откатывает релиз к предыдущему ReplicaSet. Настройку probes, requests и limits в шаблоне пода разбираем отдельно в руководстве по Deployment.

StatefulSet и DaemonSet: когда нужны особые контроллеры

StatefulSet нужен приложениям, которым важны стабильные идентификаторы и свои данные: базам данных, брокерам сообщений, etcd, ZooKeeper. Каждый под получает предсказуемое имя (например, db-0, db-1), собственный PVC и запускается по порядку - следующий только после готовности предыдущего.

apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: postgres
spec:
  serviceName: postgres-headless
  replicas: 3
  selector:
    matchLabels:
      app: postgres
  template:
    metadata:
      labels:
        app: postgres
    spec:
      containers:
        - name: postgres
          image: postgres:16
          volumeMounts:
            - name: data
              mountPath: /var/lib/postgresql/data
  volumeClaimTemplates:
    - metadata:
        name: data
      spec:
        accessModes: ["ReadWriteOnce"]
        resources:
          requests:
            storage: 10Gi

DaemonSet ставит по одному поду на каждый подходящий узел. Так запускают сборщики логов (fluentd, filebeat), агенты мониторинга (node-exporter) и сетевые плагины. Новый узел появляется в кластере - DaemonSet сразу создаёт на нём свой под.

apiVersion: apps/v1
kind: DaemonSet
metadata:
  name: node-exporter
spec:
  selector:
    matchLabels:
      app: node-exporter
  template:
    metadata:
      labels:
        app: node-exporter
    spec:
      containers:
        - name: node-exporter
          image: prom/node-exporter:v1.7.0
          ports:
            - containerPort: 9100

Сравнение всех контроллеров, включая Job и CronJob, с таблицей различий собрано в материале о выборе контроллера Kubernetes.

Жизненный цикл пода: от создания до завершения

Путь пода начинается с отправки манифеста в API-сервер, продолжается планированием на узел и запуском контейнеров, а заканчивается удалением или падением. Состояние пода описывают пять фаз:

  • Pending - под принят кластером, но контейнеры ещё не запущены: ждёт планирования, скачивания образа или монтирования тома.
  • Running - под назначен на узел, все контейнеры созданы, и хотя бы один из них работает.
  • Succeeded - все контейнеры в поде завершились успешно и не будут перезапущены. Типично для Job.
  • Failed - все контейнеры завершились, и как минимум один завершился с ошибкой (ненулевой код выхода или остановка системой) и не настроен на автоматический перезапуск.
  • Unknown - состояние пода получить не удалось, обычно из-за ошибки связи с узлом, на котором под должен выполняться.

Фаза - высокоуровневое состояние. Детали лежат в статусах контейнеров и условиях пода, а поведение после завершения контейнера определяет политика перезапуска (restart policy): она задаёт, как Kubernetes реагирует на завершение контейнеров из-за ошибок или по другим причинам. Под существует до момента завершения в соответствии с этой политикой. Полный статус пода в YAML смотрите командой kubectl get pod <pod-name> -o yaml.

Формулировки фаз и статусов стоит сверять с документацией вашей версии Kubernetes: между релизами добавляются новые причины и уточняются переходы между состояниями.

Фазы пода и статусы контейнеров: как читать вывод kubectl

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

Значение STATUSЧто означает
RunningФаза пода: контейнеры запущены, под работает
PendingФаза пода: контейнеры ещё не стартовали
SucceededФаза пода: все контейнеры завершились успешно и не будут перезапущены
FailedФаза пода: контейнеры завершились, минимум один - с ошибкой
CrashLoopBackOffОтображаемое поле kubectl: контейнер падает и перезапускается с растущей задержкой
ImagePullBackOffОтображаемое поле kubectl: образ не удалось скачать

У контейнера внутри пода есть собственное состояние: Waiting (ждёт запуска), Running (работает) и Terminated (завершён с кодом выхода). Условия пода (PodConditions) дают ещё один срез: PodScheduled - под привязан к узлу, Initialized - init-контейнеры успешно отработали, ContainersReady - все контейнеры готовы, Ready - под может принимать трафик.

kubectl describe pod nginx-pod
kubectl get pod nginx-pod -o yaml
kubectl logs nginx-pod -c nginx

В выводе describe ключевой блок - Events в самом низу: там видно, что именно происходило с подом: попытки скачать образ, ошибки монтирования тома, отказ в привязке к узлу.

Probes: как Kubernetes проверяет здоровье пода

Три типа проб решают разные задачи. livenessProbe перезапускает контейнер, если он завис. readinessProbe держит под вне балансировки, пока приложение не готово принимать трафик. startupProbe даёт медленно стартующим приложениям время на инициализацию, отключая остальные проверки до первого успеха.

apiVersion: v1
kind: Pod
metadata:
  name: web
spec:
  containers:
    - name: web
      image: nginx:1.25
      ports:
        - containerPort: 80
      readinessProbe:
        httpGet:
          path: /
          port: 80
        initialDelaySeconds: 3
        periodSeconds: 5
      livenessProbe:
        httpGet:
          path: /
          port: 80
        initialDelaySeconds: 10
        periodSeconds: 10

Без readinessProbe трафик через Service пойдёт на под, который ещё читает конфигурацию или прогревает кэш, и часть запросов закончится ошибкой. Без livenessProbe зависший процесс останется в статусе Running, хотя реально не отвечает.

Практика: создаём под и проверяем его состояние

Соберём последовательность шагов, по которой видно жизненный цикл пода целиком.

  1. Сохраните манифест nginx-pod в файл pod.yaml и примените его: kubectl apply -f pod.yaml.
  2. Проверьте статус: kubectl get pods. Через несколько секунд после Pending под перейдёт в Running.
  3. Посмотрите детали: kubectl describe pod nginx-pod, блок Events покажет планирование, скачивание образа и старт контейнера.
  4. Проверьте логи: kubectl logs nginx-pod.
  5. Зайдите внутрь контейнера: kubectl exec -it nginx-pod -- /bin/sh.
  6. Удалите под: kubectl delete pod nginx-pod. Он больше не появится - bare pod никто не пересоздаёт.

Теперь тот же сценарий для пода под управлением Deployment: удалите под командой kubectl delete pod, и ReplicaSet через несколько секунд поднимет новый. В этом главное практическое отличие контроллера от одиночного пода. Пошаговый разбор первого запуска с minikube есть в гайде по Kubernetes для начинающих.

Диагностика проблем: CrashLoopBackOff, ImagePullBackOff и другие

Набор команд для разбора почти любого сбоя одинаков:

kubectl describe pod <pod-name>
kubectl logs <pod-name> --previous
kubectl get events --sort-by=.metadata.creationTimestamp
kubectl get nodes
  • CrashLoopBackOff - контейнер стартует и падает, kubelet перезапускает его с растущей паузой. Смотрите kubectl logs --previous: причина в выводе упавшего процесса, часто это отсутствующая переменная окружения или непримонтированный конфиг.
  • ImagePullBackOff и ErrImagePull - образ не скачивается. Проверьте имя и тег, наличие imagePullSecrets для приватного реестра и доступность реестра из кластера; детали будут в Events у describe.
  • Pending - под не может найти узел. Частые причины: запрошено больше CPU или памяти, чем есть на узлах; nodeSelector или taints не совпадают с метками узлов; PVC остался в статусе Pending из-за неподходящего класса хранилища.
  • Ошибки монтирования томов - в Events видно сообщение вида FailedMount. Проверьте, существует ли configMap или secret с указанным именем и в том же namespace, что и под.

Связь пода с сетью и сервисами: как трафик попадает в под

Каждый под получает собственный IP-адрес, но этот адрес не постоянен: после пересоздания он изменится. Поэтому обращаться к подам напрямую по IP в приложениях нельзя. Service - абстрактный способ предоставить доступ к приложению, которое работает на наборе подов (документация Kubernetes). Тип ClusterIP даёт сервису виртуальный IP в масштабе кластера, и Kubernetes распределяет трафик к этому адресу между нижележащими подами.

Кроме ClusterIP используют NodePort (открывает порт на каждом узле) и LoadBalancer (получает внешний адрес у облачного провайдера). Внутри кластера чаще всего достаточно ClusterIP: другие поды обращаются к сервису по DNS-имени. Посмотреть список сервисов и их адреса: kubectl get svc. Как сервисы связаны с подами и остальными объектами API, разбираем в материале об архитектуре кластера.

ClusterIP: автоматическое и ручное выделение, конфликты

Control plane либо сам выбирает свободный адрес из настроенного диапазона, либо берёт тот, который вы указали в манифесте вручную. Каждый ClusterIP в кластере уникален, поэтому попытка создать Service с уже занятым адресом возвращает ошибку (Allocation des ClusterIP des Services). Пример сервиса с заданным адресом:

apiVersion: v1
kind: Service
metadata:
  name: nginx-svc
spec:
  clusterIP: 10.96.0.20
  selector:
    app: nginx
  ports:
    - port: 80
      targetPort: 80

Чтобы не ловить конфликты, статические адреса резервируют в нижней части диапазона. Официальная документация приводит такие примеры полос: для диапазона 10.96.0.0/24 статическая полоса идёт с 10.96.0.1 по 10.96.0.16 при конце диапазона 10.96.0.254; для 10.96.0.0/20 - с 10.96.0.1 по 10.96.1.0 при конце диапазона 10.96.15.254; для 10.96.0.0/16 - с 10.96.0.1 по 10.96.1.0 при конце 10.96.255.254. Динамическое выделение по умолчанию использует верхнюю полосу, а после её исчерпания переходит к нижнему диапазону. Показательный случай с DNS Service: при диапазоне 10.96.0.0/16 адрес 10.96.0.10 не зарезервирован, поэтому при динамическом выделении его может занять другой сервис, и создание DNS Service закончится ошибкой конфликта.

Безопасность подов: PodSecurityPolicy и Pod Security Standards

Права пода ограничивают на уровне admission-контроллеров. Исторический механизм PodSecurityPolicy (PSP) сопоставляется с Pod Security Standards, а поля PodSecurityPolicySpec описывают, какие изменения нужно внести в под и какие проверки он обязан пройти (таблица сопоставления PSP и Pod Security Standards). Дословный разбор соответствий по полям здесь не приводим: значения зависят от версии, сверять их нужно по первоисточнику.

Pod Security Standards задают три профиля с чёткой линейной прогрессией от наиболее безопасного (Restricted) к наименее безопасному (Privileged). Privileged почти не ограничивает под. Baseline запрещает известные опасные возможности: привилегированный доступ к хосту, совместное использование хостовых пространств имён, привилегированные поды, добавление capabilities сверх разрешённого списка, а также тома HostPath. Restricted идёт дальше: требует запуск не от root, запрещает повышение привилегий и сужает набор допустимых capabilities. Restricted - разумная цель для большинства приложений, но он часто ломает старые образы, которые стартуют от root.

Проверить, какое именно правило нарушает ваш под, проще всего в тестовом namespace: под будет отклонён с сообщением о нарушении, а конкретный параметр окажется в Events.

Миграция с PodSecurityPolicy на PodSecurity admission controller

PodSecurityPolicy был объявлен устаревшим в Kubernetes v1.21 и удалён из Kubernetes в v1.25. Его место занимает встроенный PodSecurity admission controller: вместо PSP можно применять аналогичные ограничения на поды с помощью встроенного контроллера или стороннего admission-плагина, который вы разворачиваете и настраиваете сами (миграция описана в документации). Встроенный контроллер работает без отдельного API-объекта: политика задаётся метками на namespace, а режимы enforce, audit и warn позволяют сначала наблюдать нарушения, а потом блокировать их.

kubectl label namespace prod \
  pod-security.kubernetes.io/enforce=baseline \
  pod-security.kubernetes.io/audit=restricted \
  pod-security.kubernetes.io/warn=restricted

Порядок миграции такой: включите warn и audit, соберите список нарушений по существующим подам, исправьте манифесты, и только после этого ставьте enforce. Резкая смена профиля на prod-кластере заблокирует создание подов, которые раньше стартовали без замечаний.

Типичные ошибки при работе с подами и как их избежать

  • Bare pods в продакшене. Такой под не пересоздаётся после удаления и не переезжает при падении узла. Для stateless-приложений нужен Deployment, для stateful - StatefulSet.
  • Нет readinessProbe. Service начинает слать трафик сразу после старта контейнера, и часть запросов падает. Добавьте проверку готовности для любого приложения, которое тратит время на инициализацию.
  • Некорректные requests и limits. Приводят к Pending (запросили больше, чем есть на узлах) или OOMKilled (превысили лимит памяти).
  • Конфликты ClusterIP. Указанный вручную адрес уже занят другим сервисом, и создание завершается ошибкой. Держите статические адреса в зарезервированной полосе.
  • Игнорирование Pod Security Standards. Под с privileged: true и доступом к хостовым путям проходит в кластер без ограничений и превращает уязвимость в компрометацию узла.
  • Состояние в emptyDir. Том очищается вместе с подом, поэтому данные базы или очереди там хранить нельзя.

Ресурсы и лимиты: как не получить OOMKilled или Pending

requests - сколько ресурсов планировщик гарантирует поду и учитывает при выборе узла. limits - верхняя граница, которую контейнер не может превысить. По CPU превышение лимита даёт троттлинг, по памяти - убийство контейнера с причиной OOMKilled.

resources:
  requests:
    cpu: 100m
    memory: 128Mi
  limits:
    cpu: 500m
    memory: 128Mi

Если под висит в Pending, проверьте запросы: сумма requests всех подов на узле не может превышать его доступные ресурсы. Если контейнер убивают по памяти, смотрите kubectl describe pod и реальное потребление, а не только пиковые значения. Для критичных сервисов разумно ставить requests равными limits: тогда под получает класс качества Guaranteed и не вытесняется другими подами.

Когда использовать поды напрямую, а когда контроллеры

Прямое создание пода оправдано в двух случаях: отладка манифеста и разовая проверка сети или тома в кластере. Всё, что должно жить дольше пяти минут, ставьте под контроллер. Stateless-приложение (веб-сервер, API, воркер очереди) - Deployment. Stateful-приложение с собственными данными и стабильным именем - StatefulSet. Агент, который нужен на каждом узле, - DaemonSet.

Контроллеры дают самовосстановление, горизонтальное масштабирование и обновление по стратегии RollingUpdate. Повторяемо разворачивать и обновлять приложения помогает скрипт с параметрами namespace и тега образа, пример собран в разборе скрипта развертывания.

Чек-лист перед деплоем в кластер:

  1. Поды управляются контроллером (Deployment, StatefulSet или DaemonSet), а не созданы напрямую.
  2. Настроены readinessProbe и livenessProbe, для медленного старта добавлена startupProbe.
  3. Заданы requests и limits, для критичных сервисов requests равны limits.
  4. Профиль Pod Security Standards выбран осознанно: сначала warn и audit, потом enforce.
  5. Для доступа к подам создан Service, статические ClusterIP вынесены в нижнюю полосу диапазона.
  6. Настроены алерты на CrashLoopBackOff, Pending и рост RESTARTS.
Поделиться: