Что такое под в Kubernetes и зачем его создавать
Под - минимальная единица развёртывания в Kubernetes: один или несколько контейнеров с общим сетевым пространством и общими томами. Контейнеры внутри пода видят друг друга на localhost, делят один IP и один жизненный цикл. Под не равен контейнеру: это обёртка, которая добавляет к контейнерам сеть, хранилище и правила перезапуска.
Создать под можно двумя способами. Императивный: строка в терминале с командой kubectl run. Декларативный: YAML-манифест, который применяется через kubectl apply -f. Первый способ экономит время при отладке, второй даёт воспроизводимый файл для Git и повторных запусков.
Kubernetes умеет выражать, запрашивать, выделять и ограничивать ресурсы рабочих нагрузок, включая специальные аппаратные устройства. Это поведение описано в документации Kubernetes по управлению ресурсами и раскрывается через requests и limits в спецификации контейнера.
Голый под не перезапускается после падения узла, не обновляется при смене образа и не масштабируется. В продакшене подами управляют контроллеры: Deployment, StatefulSet, DaemonSet, Job. Ниже разбираем оба способа создания пода по шагам и показываем переход к контроллерам.
Быстрый старт: создание пода командой kubectl run
Одна строка создаёт под с Nginx:
kubectl run nginx-demo --image=nginx:1.27 --port=80
kubectl загружает образ nginx:1.27, создаёт объект в текущем namespace и запускает контейнер. Флаг --port задаёт порт, который контейнер должен открыть: например, kubectl run hazelcast --image=hazelcast/hazelcast --port=5701. Этот же флаг требуется, если вы создаёте ассоциированный с подом ClusterIP-сервис через --expose.
Если нужен одноразовый под без контроллера, добавьте --restart=Never:
kubectl run nginx-demo --image=nginx:1.27 --port=80 --restart=Never
Флаг --restart влияет на тип создаваемого объекта. С --restart=Never создаётся под, который не перезапускается при выходе процесса: например, kubectl run -i -t busybox --image=busybox --restart=Never. Без этого флага kubectl run запускает развёртывание (Deployment) для контейнера, поэтому процесс будет перезапускаться при завершении, в отличие от запуска голого пода. Для отладки и разовых задач выбирайте Never, иначе удалённый под будет создан контроллером заново.
Проверить, что под появился:
kubectl get pods kubectl get pod nginx-demo -o wide
Флаг -o wide добавляет IP пода и имя узла. Это первое, что стоит смотреть при разборе сетевых проблем. Для первого знакомства с кластером поднимите локальную среду: гайд по запуску сервисов в Kubernetes с минимальной конфигурацией описывает установку minikube и первый деплой.
Полезные флаги kubectl run
| Флаг | Назначение | Пример |
|---|---|---|
| --image | Образ контейнера, без него запуск невозможен | --image=nginx:1.27 |
| --port | Порт, который контейнер должен открыть | --port=80 |
| --env | Переменная окружения, флаг повторяемый | --env="TZ=UTC" |
| --labels | Метки пода для селекторов | --labels=app=nginx,tier=frontend |
| --restart | Never создаёт под без перезапуска; без флага создаётся Deployment | --restart=Never |
| --dry-run=client -o yaml | Печатает манифест вместо создания объекта | kubectl run nginx-demo --image=nginx:1.27 --dry-run=client -o yaml |
| --rm -it | Удаляет под после выхода из контейнера | --rm -it -- sh |
Пример пода-утилиты, который живёт час и ничего не делает:
kubectl run debug --image=busybox --restart=Never -- sleep 3600
Двойной дефис отделяет аргументы контейнера от флагов kubectl. Без него kubectl попытается разобрать sleep как свой параметр и вернёт ошибку.
Флаги покрывают базовые сценарии: образ, порт, переменные окружения, метки. Задать requests и limits, тома, probes или несколько контейнеров в одном поде командой kubectl run не выйдет. Как только понадобился хотя бы один из этих параметров, переходите к манифесту.
Как проверить, что под запустился
kubectl get pods -w kubectl describe pod nginx-demo kubectl logs nginx-demo kubectl exec -it nginx-demo -- sh kubectl get pod nginx-demo -o yaml
Флаг -w держит вывод открытым и показывает смену статусов: Pending, ContainerCreating, Running. Секция Events в kubectl describe pod объясняет задержки и сбои: нехватку ресурсов на узле, неудачную загрузку образа, падение контейнера.
kubectl logs отдаёт stdout контейнера, kubectl exec открывает shell внутри пода. Вывод kubectl get pod -o yaml стоит сравнить с манифестом: там видно фактическое состояние, а не желаемое.
Удалить под: kubectl delete pod nginx-demo. Если объектом управляет контроллер, под вернётся, и удалять нужно сам контроллер.
Декларативный способ: YAML-манифест пода
Манифест описывает желаемое состояние, а kubectl приводит кластер к нему. Готовый файл pod.yaml:
apiVersion: v1 # версия API для Pod
kind: Pod # тип объекта
metadata:
name: nginx-demo # имя пода, обязательно
labels:
app: nginx # метка для селекторов Service и Deployment
tier: frontend
spec:
restartPolicy: Always # Always | OnFailure | Never
containers:
- name: nginx # имя контейнера, обязательно
image: nginx:1.27 # образ, обязательно
ports:
- containerPort: 80 # порт, который слушает контейнер
name: http # имя порта для Service
resources:
requests:
cpu: 100m
memory: 128Mi
limits:
cpu: 500m
memory: 256Mi
env:
- name: TZ
value: "UTC"
Отступы делайте пробелами, табы в YAML запрещены. Подробный разбор структуры apiVersion, kind, metadata и spec с другими примерами собран в гайде по структуре манифестов Kubernetes.
Обязательные и опциональные поля спецификации
| Поле | Обязательность | Комментарий |
|---|---|---|
| apiVersion | обязательно | Для пода значение v1 |
| kind | обязательно | Pod |
| metadata.name | обязательно | Уникальное имя пода в namespace |
| metadata.labels | опционально | Нужны для селекторов Service и Deployment |
| spec.containers | обязательно | Список, минимум один контейнер |
| spec.containers[].name | обязательно | Имя контейнера внутри пода |
| spec.containers[].image | обязательно | Образ с реестром и тегом |
| spec.containers[].ports | опционально | Декларация портов, ничего не открывает |
| spec.containers[].resources | опционально, но рекомендуется | requests и limits для планировщика |
| spec.restartPolicy | опционально | По умолчанию Always |
Уберите metadata.name или spec.containers, и kubectl apply вернёт ошибку валидации, ничего не создав. Опечатка в имени поля обрабатывается мягче: при включённой на API-сервере серверной валидации полей режим warn предупреждает о неизвестных или дублирующихся полях, не блокируя запрос. Предупреждение приходит через стандартный warning-заголовок для каждого неизвестного поля, отброшенного из объекта, и для каждого встреченного дублирующегося поля. Если серверная валидация полей на API-сервере не включена, режим warn ведёт себя как ignore. Проверить файл до применения можно так:
kubectl apply -f pod.yaml --dry-run=server
Режим --dry-run=server прогоняет манифест через API-сервер без создания объекта. Ошибки в отступах и лишние поля всплывут здесь, а не в рабочем кластере.
resources: requests и limits на практике
requests - гарантированный минимум, который планировщик резервирует под контейнер. limits - потолок потребления. В примере выше контейнер просит 100m CPU и 128Mi памяти, а получить может до 500m CPU и 256Mi.
Что происходит при превышении. CPU сжимаемый ресурс: контейнер упирается в limit и начинает тормозить из-за throttling. Память несжимаема: выход за limit приводит к OOMKill и перезапуску контейнера. Отсюда правило: requests задавайте всегда, limits ставьте по замерам нагрузки, а не наугад.
Kubernetes присваивает каждому поду один из QoS-классов - Guaranteed, Burstable или BestEffort - на основе resource requests и limits его контейнеров. От класса зависит порядок вытеснения при нехватке ресурсов на узле.
| QoS-класс | Условие | Порядок вытеснения |
|---|---|---|
| Guaranteed | у каждого контейнера пода есть и memory limit, и memory request (а также CPU request и limit) | вытесняется последним |
| Burstable | requests меньше limits или заданы не для всех ресурсов | средний приоритет |
| BestEffort | у контейнеров нет ни memory, ни CPU limits или requests | вытесняется первым |
Когда узел исчерпывает ресурсы, Kubernetes сначала вытесняет поды BestEffort, затем Burstable и в последнюю очередь Guaranteed. Без limits под попадает в BestEffort и первым уходит под eviction, когда узлу не хватает памяти. Сводка механизмов выделения и ограничения ресурсов, включая специальные устройства вроде GPU, собрана в разделе документации по управлению ресурсами.
ports и сетевые настройки в манифесте
Секция ports ничего не открывает. Это декларация: какой порт слушает контейнер. Kubernetes использует её для читаемости, а Service - для ссылки на именованный порт, например http.
ports:
- containerPort: 80
name: http
protocol: TCP
containerPort обязателен внутри элемента списка. name нужен, если Service ссылается на порт по имени, а не по номеру. protocol по умолчанию TCP.
Под сам по себе недоступен извне кластера. Чтобы связать приложение с другими подами или вывести его наружу, создайте Service:
kubectl expose pod nginx-demo --port=80
Применение манифеста и проверка результата
kubectl apply -f pod.yaml kubectl get pods kubectl get pod nginx-demo -o wide kubectl describe pod nginx-demo kubectl logs nginx-demo kubectl exec -it nginx-demo -- sh kubectl get pod nginx-demo -o yaml
kubectl apply идемпотентна: повторный запуск с тем же файлом ничего не меняет, если объект уже соответствует манифесту. Первая команда отвечает pod/nginx-demo created. Дальше смотрите на столбцы STATUS и READY: Running вместе с 1/1 означает, что контейнеры запущены и проверки пройдены.
Удаление выполняется тем же файлом: kubectl delete -f pod.yaml. Так вы удалите ровно тот объект, который описан в манифесте, и не заденете соседние ресурсы.
Типичные ошибки и как их диагностировать
| Симптом | Причина | Команда диагностики |
|---|---|---|
| ImagePullBackOff, ErrImagePull | Опечатка в имени образа, нет доступа к реестру, не задан imagePullSecret | kubectl describe pod nginx-demo, секция Events |
| CrashLoopBackOff | Процесс в контейнере завершается с ошибкой сразу после старта | kubectl logs nginx-demo и kubectl logs nginx-demo --previous |
| Pending | Планировщик не нашёл узел: мало CPU или памяти, не сходятся nodeSelector и taints | kubectl describe pod nginx-demo, kubectl get nodes |
| Ошибка разбора YAML | Табы вместо пробелов, сбитые отступы, лишний символ | kubectl apply -f pod.yaml --dry-run=server |
| Предупреждение unknown field | Опечатка в имени поля или неверный apiVersion | Текст предупреждения в выводе kubectl apply |
Общую картину по namespace даёт список событий, отсортированный по времени:
kubectl get events --sort-by=.lastTimestamp
Свежие записи внизу. Причины FailedScheduling и FailedMount обычно указаны прямо в тексте события, без похода в документацию.
kubectl run или YAML-манифест: что выбрать
| Критерий | kubectl run | YAML-манифест |
|---|---|---|
| Скорость | одна команда в терминале | файл плюс kubectl apply |
| Воспроизводимость | нет, команда живёт в истории shell | есть, результат одинаков при каждом запуске |
| Хранение в Git | неудобно | естественно, с ревью и историей правок |
| Идемпотентность | повторный запуск создаст новый объект или вернёт ошибку | apply повторяем и приводит состояние к описанному |
| Полнота параметров | образ, порт, env, метки, restart | любые поля PodSpec: resources, volumes, probes, tolerations |
| GitOps и CI/CD | не подходит | основа пайплайна |
Переходный приём: получите манифест из команды и отредактируйте его. Флаг --dry-run=client печатает соответствующие API-объекты без их создания, а комбинация --dry-run=client -o yaml позволяет сгенерировать спецификацию пода и записать её в файл. Дальше остаётся добавить resources и ports.
kubectl run nginx-demo --image=nginx:1.27 --port=80 --dry-run=client -o yaml > pod.yaml
Для всего, что живёт дольше отладочной сессии, берите манифест. Как встроить такие файлы в повторяемый пайплайн с параметризацией тега образа и проверкой rollout, показывает скрипт развёртывания приложения в Kubernetes.
Почему в продакшене поды не создают напрямую
Голый под не переживает падение узла. Планировщик не перенесёт его на другую машину, не пересоздаст после сбоя и не заменит при обновлении образа. Масштабирования тоже нет: вторая реплика требует второй команды и ручного контроля.
Контроллеры закрывают эти пробелы. Deployment отвечает за stateless-приложения: держит заданное число реплик, делает rolling update и откат. StatefulSet даёт подам устойчивые имена и порядок запуска, что нужно базам данных и очередям. DaemonSet запускает по одному поду на каждом узле, его типичная задача - сбор метрик и логов. Job доводит задачу до завершения и не перезапускает её бесконечно. Сравнение этих объектов с примерами манифестов для Nginx, Redis и Node Exporter разобрано в материале о выборе контроллера Kubernetes.
Под остаётся строительным блоком: контроллер создаёт те же поды, только по шаблону и под своим присмотром. Самовосстановление, обновление без простоя и масштабирование берёт на себя контроллер.
Когда голый под всё-таки уместен
Прямое создание пода оправдано в четырёх случаях: отладка, разовая проверка образа, диагностика сети изнутри кластера, запуск утилиты. Пример пода с сетевыми инструментами:
kubectl run netcheck --image=nicolaka/netshoot --restart=Never -- sleep 3600
Внутри такого пода доступны dig, curl, tcpdump и traceroute. Под живёт до удаления, в Git не попадает и после проверки убирается командой kubectl delete pod netcheck.
Переход от Pod к Deployment
Быстрый путь для проверки идеи:
kubectl create deployment nginx --image=nginx:1.27 --replicas=3 kubectl get deployments kubectl get pods -l app=nginx
Для постоянной работы перепишите pod.yaml в deployment.yaml: замените kind на Deployment, apiVersion на apps/v1, добавьте spec.replicas и spec.selector.matchLabels, а шаблон пода перенесите в spec.template.
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx
spec:
replicas: 3
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx:1.27
ports:
- containerPort: 80
Метки пода в spec.template.metadata.labels обязаны совпадать с selector.matchLabels, иначе контроллер не свяжет поды с Deployment. Применение и проверка:
kubectl apply -f deployment.yaml kubectl rollout status deployment/nginx kubectl get pods -l app=nginx
Готовые манифесты с RollingUpdate, livenessProbe, readinessProbe и откатом релиза смотрите в руководстве по Kubernetes Deployment.
Чек-лист: создание пода от команды до продакшена
- Для отладки и разовых задач используйте kubectl run с флагом --restart=Never.
- Для повторяемого создания пишите манифест с обязательными полями metadata.name, spec.containers, container name и image.
- Задайте resources.requests всегда, limits - по результатам замеров.
- Опишите ports.containerPort и дайте порту имя, если на него будет ссылаться Service.
- Проверьте файл через kubectl apply -f pod.yaml --dry-run=server.
- Примените манифест и убедитесь в статусе Running и READY 1/1.
- При сбое смотрите kubectl describe pod, секцию Events, и kubectl logs.
- Для продакшена оберните описание пода в Deployment, StatefulSet, DaemonSet или Job.
- Храните манифесты в Git и применяйте их через CI/CD, а не руками с ноутбука.
Начните с копирования pod.yaml из раздела про манифест, примените его в тестовом namespace и доведите до Running. Дальше перенесите спецификацию контейнера в шаблон Deployment: это тот же YAML, но под управлением контроллера, который сам вернёт упавшую реплику и проведёт обновление образа.