Материал открыт для чтения

Войдите, чтобы сохранять прогресс и результаты.

Войти
Kubernetes — Основы

Pod: минимальная единица приложения

чтение ~10 минУрок 3 из 30

Pod: минимальная единица приложения

module k8s intro Image05

В Kubernetes ты почти никогда не запускаешь “просто контейнер”. Kubernetes работает через абстракции: мы описываем объект, а кластер старается привести реальность к этому описанию. Самая маленькая прикладная абстракция для запуска приложения называется Pod.

Pod отвечает на простой запрос: “запусти один экземпляр приложения из одного или нескольких тесно связанных контейнеров”. Например, веб-сервер может жить в основном контейнере, а рядом с ним может быть helper-контейнер, который готовит конфиг, собирает служебные файлы или отдаёт sidecar-функцию. Для Kubernetes это всё один Pod: он планируется на одну node, получает один IP и живёт как одна маленькая группа.

Для первого понимания думай так:

Уровень Что это
Container процесс приложения и его filesystem из image
Pod оболочка вокруг контейнера: IP, volumes, lifecycle
Node машина, на которой реально запущен Pod
Cluster набор node'ов и control plane

Один Pod получает один IP внутри кластера. Если в Pod несколько контейнеров, они видят друг друга через localhost, потому что живут в одном сетевом пространстве.

Контейнеры внутри Pod делят:

  • сетевой namespace: общий IP и доступ друг к другу через localhost;
  • volume'ы, если они подключены в spec.volumes и volumeMounts;
  • lifecycle Pod'а: Kubernetes создаёт, переносит и удаляет их как одну группу.

При этом контейнеры не становятся “одним контейнером”. У каждого остаётся свой image, свой filesystem из image, свои переменные окружения, свои процессы и свои настройки ресурсов. Поэтому Pod удобен именно для тесно связанных контейнеров, которым нужно жить рядом, а не для любых независимых частей системы.

Почему Pod не равен приложению целиком

Pod обычно считается временным. Его можно удалить, пересоздать, перенести на другую node. Поэтому важная идея Kubernetes: не привязываться к конкретному Pod как к вечному серверу.

Если тебе нужен один пробный nginx — Pod подойдёт. Если нужно приложение, которое должно восстанавливаться после сбоя и обновляться без ручной пересборки, нужен Deployment. Это будет следующая тема.

Что смотреть в первой лабе

Когда создаёшь Pod, проверяй не только Running, но и детали:

kubectl get pods -o wide
kubectl describe pod nginx-pod
kubectl logs nginx-pod

get даёт краткую картину, describe объясняет события и причины, logs показывает вывод контейнера. Эти три команды будут возвращаться почти во всех Kubernetes-курсах.

Создание Pod через команду и через YAML

Для быстрого старта можно использовать:

kubectl run nginx-pod --image=nginx:alpine

Разбор: kubectl run создаёт Pod, nginx-pod — имя Pod'а, --image=nginx:alpine — image контейнера. Эта команда удобна для быстрого эксперимента, но хуже YAML, когда результат нужно сохранить в Git и повторить.

Команда создаёт Pod с одним контейнером. Для повторяемой работы лучше сохранить манифест:

Важная идея: в Kubernetes можно быстро создавать объекты командами, как мы делали в Docker, но в реальной работе приложение обычно описывают YAML-манифестами. Эти файлы хранят в Git, проходят review, применяют через CI/CD и используют как источник правды о том, что должно быть в кластере.

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

Применение:

kubectl apply -f pod.yaml
kubectl get pod nginx-pod -o yaml

apply -f pod.yaml отправляет YAML-файл в API Server и создаёт или обновляет объект. kubectl get pod nginx-pod -o yaml получает конкретный Pod и выводит его в YAML-формате, чтобы увидеть не только краткую таблицу, но и полную спецификацию со status.

Иногда полезно сохранить уже работающий объект в файл:

kubectl get pod nginx-pod -o yaml > nginx-pod.yaml

Так можно разобрать, что реально находится в кластере. Но такой YAML нельзя бездумно класть в Git: в нём будут служебные поля вроде status, resourceVersion, uid, managedFields. Для учебной диагностики это полезно, а для production-манифеста такие поля обычно очищают.

Из чего состоит манифест

Почти каждый Kubernetes-манифест читается сверху вниз одинаково:

apiVersion: v1        # в какой версии Kubernetes API описан объект
kind: Pod             # какой объект мы хотим создать
metadata:             # имя, labels, namespace и служебные данные
  name: nginx-pod
  labels:
    app: nginx
spec:                 # желаемое состояние объекта
  containers:
  - name: nginx
    image: nginx:alpine
    ports:
    - containerPort: 80

apiVersion

apiVersion говорит API Server, через какую версию API нужно читать этот объект.

Для Pod используется:

apiVersion: v1

Это не версия твоего приложения и не версия nginx. Это версия Kubernetes API для ресурса Pod. У других объектов версия может быть другой: например, Deployment обычно использует apps/v1.

kind

kind говорит, какой тип объекта создаётся:

kind: Pod

Kubernetes API содержит много типов объектов: Pod, Deployment, Service, ConfigMap, Secret, Namespace. Поле kind помогает API Server понять, какую схему проверять и какой контроллер потом будет работать с объектом.

metadata

metadata описывает служебную информацию об объекте:

metadata:
  name: nginx-pod
  labels:
    app: nginx

metadata.name - имя объекта. В одном namespace не может быть двух Pod'ов с одинаковым именем.

metadata.labels - пары ключ-значение для выбора и группировки объектов. Сейчас label app: nginx просто помечает Pod, но дальше его смогут использовать Service, Deployment selector и команды вида:

kubectl get pods -l app=nginx

Labels - не комментарии для человека. Это рабочие метки, по которым Kubernetes и инструменты находят нужные объекты.

spec

spec - главная прикладная часть манифеста. Здесь описывается желаемое состояние: что именно должно быть запущено.

Для Pod минимально важен блок containers:

spec:
  containers:
  - name: nginx
    image: nginx:alpine
    ports:
    - containerPort: 80

containers - это список. Даже если контейнер один, он всё равно пишется как элемент списка через -.

name - имя контейнера внутри Pod. Оно нужно для логов, exec и ссылок внутри других настроек.

image - Docker/OCI image, из которого будет запущен контейнер.

ports.containerPort - порт, который приложение слушает внутри контейнера. Это не публикует порт наружу автоматически. Поле полезно как описание для Kubernetes и людей, а стабильный доступ к Pod'ам позже будет делаться через Service.

В более сложных Pod'ах внутри spec.containers также могут быть env, command, args, volumeMounts, resources, readinessProbe, livenessProbe. В первой лабе достаточно понимать name, image и базовую структуру списка.

status

status обычно не пишут руками в манифесте. Его заполняет сам Kubernetes после создания объекта.

Когда ты выполняешь:

kubectl get pod nginx-pod -o yaml

в выводе появятся не только apiVersion, kind, metadata и spec, но и status: фаза Pod'а, IP, node, состояние контейнера, причины ошибок. Это фактическое состояние, которое Kubernetes наблюдает прямо сейчас.

Для Git и обычных манифестов важнее spec: что мы хотим получить. Для диагностики важен status: что получилось на самом деле.

Почему отступы важны

YAML чувствителен к отступам. Эти строки находятся внутри одного контейнера, потому что они вложены под - name: nginx:

  containers:
  - name: nginx
    image: nginx:alpine
    ports:
    - containerPort: 80

Если сломать отступы, YAML может стать невалидным или поменять смысл. Поэтому при написании манифестов важно копировать структуру аккуратно и проверять её через kubectl apply -f.

Логи и exec

Если в Pod один контейнер:

kubectl logs nginx-pod
kubectl exec nginx-pod -- hostname

logs показывает stdout/stderr контейнера. exec запускает команду внутри уже работающего контейнера.

Идея похожа на Docker:

docker exec -it web sh

В Kubernetes команда выглядит так:

kubectl exec -it nginx-pod -- sh

То есть мы тоже "заходим внутрь" контейнера, но путь другой. В Docker CLI обращается к Docker daemon на текущем сервере. В Kubernetes kubectl отправляет запрос в API Server, API Server находит нужный Pod, а kubelet на node запускает команду внутри контейнера.

Разбор kubectl exec -it nginx-pod -- sh:

Часть Что значит
kubectl exec выполнить команду внутри running Pod
-i держать stdin открытым
-t выделить интерактивный терминал
nginx-pod имя Pod'а
-- отделяет аргументы kubectl от команды внутри контейнера
sh команда, которая выполнится внутри контейнера

Разделитель -- важен: всё после него относится уже к команде внутри контейнера. В примере hostname выполняется внутри Pod'а, а не на твоей VM:

kubectl exec nginx-pod -- hostname

Если в Pod несколько контейнеров, нужно явно указать контейнер:

kubectl exec -it nginx-pod -c nginx -- sh
kubectl logs nginx-pod -c nginx

Если Pod завис в Pending, logs может быть недоступен, потому что контейнер ещё не запущен. Тогда сначала смотри describe.

Удаление и временность Pod

kubectl delete pod nginx-pod

Если Pod создан напрямую, после удаления он исчезнет. Если Pod создан Deployment'ом, контроллер создаст новый. Это ключевое различие: в первой лабе Pod нужен для знакомства, дальше почти всегда лучше работать через контроллеры.

Статусы Pod и что они означают

Статус Как читать
Pending Pod принят API, но ещё не запущен на node
Running контейнеры запущены, но это не всегда значит “приложение готово”
Succeeded все контейнеры завершились успешно
Failed контейнер завершился с ошибкой
CrashLoopBackOff контейнер падает, Kubernetes повторяет запуск с паузой
ImagePullBackOff image не удалось скачать

Диагностика:

kubectl get pod nginx-pod
kubectl describe pod nginx-pod
kubectl logs nginx-pod --previous

--previous полезен, если контейнер уже перезапустился: он показывает логи предыдущего запуска.

IP Pod не является стабильным контрактом

kubectl get pod nginx-pod -o wide

IP Pod удобен для диагностики, но не должен быть зашит в конфиг другого приложения. После пересоздания Pod IP изменится. Для стабильного доступа используют Service и DNS.

Материал изучен?

Войдите, чтобы сохранить прогресс и перейти к практической лаборатории.

Войти и продолжить