Pod: минимальная единица приложения
В 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.
