← Весь справочник

Kubernetes: Pod, Deployment, StatefulSet, DaemonSet, Job, CronJob

Манифесты рабочих нагрузок — контейнеры, init- и sidecar-контейнеры, реплики, стратегии выкатки, задачи

Рабочая нагрузка (workload) — объект, который запускает контейнеры. Сам контейнер всегда живёт внутри пода (Pod): это минимальная единица, у подов в одном экземпляре общий IP и тома. Поды руками почти не создают — их порождают контроллеры:

ОбъектКогда брать
Deploymentсервис без состояния (веб, API): N одинаковых взаимозаменяемых подов, плавная выкатка новой версии
StatefulSetсервис с состоянием (БД, очереди): у каждого пода постоянное имя имя-0, имя-1 и свой диск
DaemonSetровно один под на каждом узле: агенты логов и мониторинга, сетевые плагины
Jobодноразовая задача, которая должна завершиться успешно: миграция, импорт
CronJobJob по расписанию: бэкап, очистка

Описание пода у всех них одинаковое и лежит в spec.template (у CronJob — в spec.jobTemplate.spec.template).

Синтаксис

apiVersion: apps/v1          # Pod — v1, Deployment/StatefulSet/DaemonSet — apps/v1, Job/CronJob — batch/v1
kind: Deployment
metadata:
  name: web
  labels: {app: web}
spec:
  replicas: 2
  selector:
    matchLabels: {app: web}  # какими подами управляет контроллер
  template:                  # шаблон пода
    metadata:
      labels: {app: web}     # должны подходить под selector
    spec:
      containers:
        - name: web
          image: nginx:1.27
          ports:
            - containerPort: 80

Заготовку проще не набирать, а сгенерировать: kubectl create deployment|job|cronjob … --dry-run=client -o yaml (см. kubectl). Для Pod, StatefulSet и DaemonSet офлайн-генератора нет: kubectl run требует кластер, а StatefulSet и DaemonSet удобно делать из сгенерированного Deployment.

Под: контейнеры

Поля элемента spec.containers[] (и initContainers[]):

ПолеЧто значитПо умолчанию / заметки
nameимя контейнера, уникальное в подеобязательно
imageобраз: nginx:1.27, registry.example.com/app:2.1без тега берётся :latest
imagePullPolicyAlways / IfNotPresent / NeverAlways для :latest и образа без тега, иначе IfNotPresent
commandсписок: заменяет ENTRYPOINT образане задан — берётся из образа
argsсписок: заменяет CMD образа$(VAR) подставляет переменную окружения контейнера
workingDirрабочий каталогиз образа
ports[].containerPortпорт, который слушает приложениесправочное поле: порт доступен и без него; name даёт порту имя для Service и проб
env, envFromпеременные окружениясм. k8s-config
volumeMountsкуда смонтировать тома подасм. k8s-config
resourcesrequests / limits по CPU и памятисм. k8s-probes-resources
livenessProbe, readinessProbe, startupProbeпроверки здоровьясм. k8s-probes-resources
securityContextrunAsUser, runAsNonRoot, readOnlyRootFilesystem, allowPrivilegeEscalation, capabilities

Под: уровень пода

Поля spec пода (в контроллерах — spec.template.spec):

ПолеЧто значитПо умолчанию / заметки
containersосновные контейнеры, запускаются одновременнообязательно, хотя бы один
initContainersзапускаются по очереди до основных; каждый должен завершиться с кодом 0упал — под перезапускает их заново (по restartPolicy)
initContainers[].restartPolicy: Alwayssidecar: стартует в порядке init-контейнеров, но не ждёт завершения и работает рядом с основнымистабильно с Kubernetes 1.33, включено по умолчанию с 1.29
restartPolicyAlways / OnFailure / Never — перезапускать ли упавшие контейнерыAlways; у Deployment/StatefulSet/DaemonSet только Always, у Job — только OnFailure или Never
serviceAccountNameот чьего имени под ходит в APIdefault; см. k8s-rbac
volumesтома, общие для контейнеров подасм. k8s-config
nodeSelectorзапускать только на узлах с такими метками: {disktype: ssd}
tolerationsразрешить узлы с «запрещающими» taint
affinitynodeAffinity, podAntiAffinity — гибкие правила размещения
imagePullSecrets[{name: секрет}] — доступ к приватному рееструсекрет типа kubernetes.io/dockerconfigjson
terminationGracePeriodSecondsсколько ждать после SIGTERM до SIGKILL30
hostNetworkиспользовать сеть узлаfalse
securityContextна весь под: runAsUser, fsGroup, …

Deployment

ПолеЧто значитПо умолчанию / заметки
spec.replicasсколько подов держать1
spec.selector.matchLabelsметки, по которым Deployment находит свои подыобязательно; неизменяемо после создания
spec.selector.matchExpressions[{key, operator: In|NotIn|Exists|DoesNotExist, values}]
spec.template.metadata.labelsметки подовобязаны подходить под selector
spec.strategy.typeRollingUpdate — по одному заменять / Recreate — сначала убить все старыеRollingUpdate
spec.strategy.rollingUpdate.maxSurgeсколько подов можно запустить сверх replicas во время выкатки (число или %)25% (округление вверх)
spec.strategy.rollingUpdate.maxUnavailableсколько подов может быть недоступно во время выкатки25% (округление вниз); вместе с maxSurge не может быть 0
spec.minReadySecondsсколько под должен быть Ready, чтобы считаться доступным0
spec.revisionHistoryLimitсколько старых ReplicaSet хранить для отката10
spec.progressDeadlineSecondsчерез сколько секунд без прогресса выкатка считается зависшей600
spec.pausedприостановить выкаткуfalse

Deployment управляет подами через ReplicaSet: каждая версия шаблона — свой ReplicaSet, откат (kubectl rollout undo) возвращает предыдущий. Новая выкатка начинается при любом изменении spec.template — образа, переменных, меток пода; изменение replicas выкатку не запускает.

StatefulSet

ПолеЧто значитПо умолчанию / заметки
spec.serviceNameимя headless-сервиса (clusterIP: None), который даёт подам DNS-именав схеме 1.32 обязательно; сам Service создаёшь отдельно
spec.replicas, spec.selector, spec.templateкак у Deployment
spec.volumeClaimTemplatesшаблоны PVC: каждому поду — свой дискPVC называется <шаблон>-<statefulset>-<номер>, например data-db-0
spec.podManagementPolicyOrderedReady — поды по одному, по порядку / Parallel — все сразуOrderedReady
spec.updateStrategy.typeRollingUpdate (с последнего номера к нулевому) / OnDelete (обновится только удалённый вручную под)RollingUpdate
spec.updateStrategy.rollingUpdate.partitionобновлять только поды с номером ≥ N (канареечная выкатка)0
spec.persistentVolumeClaimRetentionPolicywhenDeleted / whenScaled: Retain или Deleteоба Retain: диски не удаляются

Поды получают имена db-0, db-1, … и DNS-имена db-0.<serviceName>.<namespace>.svc.cluster.local. Под с тем же номером после пересоздания получает то же имя и тот же PVC.

DaemonSet

ПолеЧто значитПо умолчанию / заметки
spec.selector, spec.templateкак у Deploymentполя replicas нет
spec.template.spec.nodeSelector / affinityна каких узлах запускатьбез них — на всех подходящих
spec.template.spec.tolerationsнужны, чтобы попасть на узлы с taint (например, control-plane)
spec.updateStrategy.typeRollingUpdate / OnDeleteRollingUpdate, maxUnavailable: 1

Job

ПолеЧто значитПо умолчанию / заметки
spec.completionsсколько успешных завершений нужно1
spec.parallelismсколько подов работает одновременно1
spec.backoffLimitсколько неудачных попыток до признания Job проваленным6; паузы между попытками растут: 10 с, 20 с, 40 с… до 6 мин
spec.activeDeadlineSecondsобщий лимит времени; по истечении поды убиваются, Job — Failedбез лимита
spec.ttlSecondsAfterFinishedудалить Job и его поды через N секунд после завершенияне удаляется
spec.completionModeNonIndexed / Indexed — каждый под получает номер в JOB_COMPLETION_INDEXNonIndexed
spec.suspendне создавать поды, пока truefalse
spec.template.spec.restartPolicyNever — при сбое новый под / OnFailure — перезапуск контейнера в том же подеобязательно указать одно из двух

selector у Job не пиши: Kubernetes генерирует его сам.

CronJob

ПолеЧто значитПо умолчанию / заметки
spec.scheduleрасписание в формате cron: '*/15 * * * *', '0 3 * * 1', @hourly, @dailyобязательно; см. cron
spec.timeZoneчасовой пояс: Europe/Moscowпояс kube-controller-manager, обычно UTC
spec.jobTemplate.specописание Job (все поля из таблицы выше)обязательно
spec.concurrencyPolicyAllow — запускать параллельно / Forbid — пропустить запуск, пока идёт прошлый / Replace — убить прошлый и запустить новыйAllow
spec.startingDeadlineSecondsнасколько можно опоздать с запуском; позже — запуск пропускаетсябез лимита
spec.successfulJobsHistoryLimitсколько успешных Job хранить3
spec.failedJobsHistoryLimitсколько проваленных Job хранить1
spec.suspendприостановить расписаниеfalse

Примеры

# заготовки манифестов без кластера
kubectl create deployment web --image=nginx:1.27 --replicas=3 --port=80 \
  --dry-run=client -o yaml > deploy.yaml
kubectl create job migrate --image=busybox:1.36 \
  --dry-run=client -o yaml -- sh -c 'echo migrating' > job.yaml
kubectl create cronjob cleanup --image=busybox:1.36 --schedule='0 3 * * *' \
  --restart=OnFailure --dry-run=client -o yaml -- sh -c 'echo cleanup' > cron.yaml

# доработать через yq: стратегия выкатки и параметры Job
yq -i '.spec.strategy = {"type": "RollingUpdate", "rollingUpdate": {"maxSurge": 1, "maxUnavailable": 0}}' deploy.yaml
yq -i '.spec.backoffLimit = 2 | .spec.ttlSecondsAfterFinished = 600' job.yaml
yq -i '.spec.concurrencyPolicy = "Forbid" | .spec.timeZone = "Europe/Moscow"' cron.yaml

# DaemonSet из Deployment: сменить kind, убрать replicas, strategy и status
# (status у DaemonSet другой — с пустым status: {} схема не пройдёт)
kubectl create deployment node-agent --image=busybox:1.36 --dry-run=client -o yaml -- sleep infinity \
  | yq '.kind = "DaemonSet" | del(.spec.replicas) | del(.spec.strategy) | del(.status)' > ds.yaml

# проверить всё по схемам
kubeconform -strict -summary deploy.yaml job.yaml cron.yaml ds.yaml

# одно поле — посмотреть, что получилось
yq '.spec.jobTemplate.spec.template.spec.restartPolicy' cron.yaml

# в реальном кластере (здесь не сработают):
# kubectl apply -f deploy.yaml
# kubectl rollout status deploy/web
# kubectl get pods -l app=web
# kubectl create job manual-run --from=cronjob/cleanup

Под с init-контейнером и sidecar:

apiVersion: v1
kind: Pod
metadata:
  name: app
  labels: {app: app}
spec:
  initContainers:
    - name: wait-db                 # обычный init: ждёт и завершается
      image: busybox:1.36
      command: ["sh", "-c", "until nc -z db 5432; do sleep 2; done"]
    - name: log-shipper             # sidecar: работает всё время жизни пода
      image: busybox:1.36
      restartPolicy: Always
      command: ["sh", "-c", "tail -F /logs/app.log"]
      volumeMounts:
        - {name: logs, mountPath: /logs}
  containers:
    - name: app
      image: registry.example.com/app:1.4.2
      command: ["/app/server"]      # вместо ENTRYPOINT образа
      args: ["--port", "8080"]      # вместо CMD образа
      ports:
        - {name: http, containerPort: 8080}
      volumeMounts:
        - {name: logs, mountPath: /var/log/app}
  volumes:
    - name: logs
      emptyDir: {}

StatefulSet с headless-сервисом и своим диском у каждого пода:

apiVersion: v1
kind: Service
metadata:
  name: db
spec:
  clusterIP: None            # headless: DNS-имена db-0.db, db-1.db
  selector: {app: db}
  ports:
    - {name: pg, port: 5432}
---
apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: db
spec:
  serviceName: db
  replicas: 2
  selector:
    matchLabels: {app: db}
  template:
    metadata:
      labels: {app: db}
    spec:
      containers:
        - name: postgres
          image: postgres:16
          ports:
            - {name: pg, containerPort: 5432}
          volumeMounts:
            - {name: data, mountPath: /var/lib/postgresql/data}
  volumeClaimTemplates:      # PVC data-db-0, data-db-1
    - metadata:
        name: data
      spec:
        accessModes: ["ReadWriteOnce"]
        resources:
          requests:
            storage: 1Gi

Job, который обрабатывает 10 частей по 3 одновременно:

apiVersion: batch/v1
kind: Job
metadata:
  name: batch
spec:
  completions: 10
  parallelism: 3
  completionMode: Indexed    # номер части — в $JOB_COMPLETION_INDEX
  backoffLimit: 4
  activeDeadlineSeconds: 3600
  template:
    spec:
      restartPolicy: Never
      containers:
        - name: worker
          image: busybox:1.36
          command: ["sh", "-c", "echo part $JOB_COMPLETION_INDEX"]

Подводные камни

  • Селектор и метки пода не совпадают. Для Deployment API-сервер такой манифест отвергнет (selector does not match template labels), а kubeconform пропустит — он проверяет только структуру. Совпадение меток проверяй глазами.
  • selector нельзя поменять у существующего Deployment, StatefulSet и DaemonSet: только удалить и создать заново. Меняешь метки — добавляй новые в шаблон пода, а selector не трогай.
  • restartPolicy: Always в Job API-сервер не примет (нужно Never или OnFailure), но схема это не ловит: kubeconform скажет valid.
  • command заменяет ENTRYPOINT целиком. Задал command: ["--debug"] — Kubernetes попробует запустить файл --debug. Аргументы к стандартной команде образа пиши в args.
  • command: ["sh", "-c", "a && b"] — вся строка оболочки идёт одним элементом. command: ["sh", "-c", "echo", "hi"] выведет пустую строку: hi станет $0, а не аргументом echo.
  • Отступы в containers. - name: и image: должны быть на одном уровне. Сдвинутый image превращается в поле другого объекта или в ошибку разбора.
  • :latest и imagePullPolicy. С :latest образ скачивается при каждом старте пода, а без тега тоже берётся :latest. Для воспроизводимости указывай точный тег.
  • maxSurge: 0 и maxUnavailable: 0 одновременно запрещены: выкатке некуда двигаться.
  • replicas в манифесте и HPA. Если репликами управляет HorizontalPodAutoscaler, каждый kubectl apply с replicas: 2 сбросит их обратно. Убери replicas из манифеста (см. k8s-probes-resources).
  • StatefulSet не создаёт Service из serviceName — его нужно описать отдельно, с clusterIP: None.
  • Диски StatefulSet остаются после удаления и уменьшения replicas: PVC из volumeClaimTemplates по умолчанию не удаляются. Это защита данных, но и источник «потерянных» томов.
  • CronJob и часовой пояс. Без timeZone расписание считается в поясе контроллера, обычно UTC: 0 3 * * * — это 06:00 по Москве.
  • concurrencyPolicy: Allow по умолчанию. Если задача идёт дольше интервала, запуски накладываются друг на друга. Для бэкапов и миграций ставь Forbid.
  • Init-контейнер, который не завершается, держит под в статусе Init:0/1 навсегда. Долгоживущий вспомогательный контейнер делай sidecar (restartPolicy: Always) или обычным контейнером.

См. также

kubectl · kubeconform · yq · k8s-networking · k8s-config · k8s-probes-resources · k8s-rbac · cron · docker-compose