Learning Platform
Глоссарий Troubleshooting
Урок 12.02 · 20 мин
Средний
init containersequentialwait for dependencyresource requestsPod lifecycle

Init containers: sequential setup до main

Init container — это специальный контейнер, который должен завершиться успешно (exit 0) до старта main containers Pod-а. Несколько init containers выполняются последовательно в порядке, в котором они объявлены в манифесте. Каждый следующий стартует только после успешного завершения предыдущего. Это идеальный механизм для setup-задач: дождаться зависимости, сгенерировать конфиг, накатить миграцию.


depends_on + healthcheck: правильная синхронизация сервисов

Базовый пример

apiVersion: v1
kind: Pod
metadata:
  name: app-with-init
spec:
  initContainers:
  - name: wait-for-db
    image: busybox:1.36
    command: ['sh', '-c', 'until nc -z postgres 5432; do echo waiting for db; sleep 2; done']
  - name: run-migrations
    image: myapp/migrations:v1
    command: ['flyway', 'migrate']
  containers:
  - name: app
    image: myapp:v1

Что происходит при старте Pod:

  1. kubelet стартует wait-for-db. Контейнер крутится в цикле, пингуя postgres:5432 через netcat. Когда соединение установлено — nc -z exits 0, цикл until завершается, контейнер exits 0.
  2. kubelet стартует run-migrations. flyway применяет SQL миграции к базе. После успешного завершения exit 0.
  3. kubelet стартует main контейнер app. Только теперь app получает CPU.

Если на любом шаге exit code не 0 — kubelet перезапускает init container согласно restartPolicy Pod (по умолчанию Always). Pod висит в фазе Init:N/M или Init:Error.


5 use cases для init containers

Когда init container — правильный выбор
Wait for dependencyСамый частый use case. App не должен запускаться, пока недоступна БД, message broker или внешний сервис. Init container крутится в цикле until ... do sleep ..., main не стартует пока init exits 0.
Generate configInit container генерирует config-файл (например, из шаблона + env vars, или скачивает с Vault), кладёт в emptyDir, main mount-ит ту же emptyDir и читает готовый конфиг. Полезно когда main image — vendor binary без templating логики.
Initialize volumeInit готовит содержимое PV/emptyDir перед стартом main: разархивирует начальный dataset, клонирует git repo, делает chmod/chown. Главное — main стартует с уже наполненным volume.
Run migrationsDB schema migrations (flyway, liquibase, knex) запускаются как init container перед запуском app. Если миграция failed — Pod в Init:Error, app не получит трафик. Без него возможен race: rolling update создаёт новый Pod с новой схемой, но миграция не запустилась — app crashes.
Pull secretsInit container аутентифицируется в Vault/AWS Secrets Manager/Azure Key Vault, фетчит секреты, складывает в emptyDir (tmpfs). Main контейнер mount-ит emptyDir и читает секреты. Это паттерн для секретов, которые не хочется класть в kube Secret (требуется ротация, audit). Vault Agent Injector работает по этой схеме.

Sequential execution: ordering и failures

Init containers выполняются строго последовательно в порядке объявления. Это критически важно:

spec:
  initContainers:
  - name: step-1            # стартует первым
    image: busybox
    command: ['echo', 'one']
  - name: step-2            # стартует только после step-1 exit 0
    image: busybox
    command: ['echo', 'two']
  - name: step-3            # стартует только после step-2 exit 0
    image: busybox
    command: ['echo', 'three']

Если step-2 падает с ошибкой:

  • step-3 не стартует;
  • Pod в фазе Init:Error или Init:CrashLoopBackOff;
  • kubelet перезапускает только step-2, не весь Pod;
  • step-1 повторно не выполняется (он уже Completed).
WARNING

Init containers не выполняются параллельно. Если у вас 5 независимых setup-шагов, каждый по 30 секунд — total startup будет 5*30 = 150 секунд, не 30. На CKAD это влияет на ResourceQuota и timeouts: если у Pod startup grace period 60 секунд, а суммарное init время 150 — Pod не успеет встать.


Killer момент: resource calculation для Pod

Init containers и main containers имеют разные модели расчёта эффективных ресурсов для scheduling. Поскольку init containers выполняются по очереди, в каждый момент времени запущен только один из них. Поэтому scheduler берёт максимум по init containers, а не сумму.

Effective Pod request для каждого resource (CPU, memory) считается как:

effective = max(
  max(initContainer_i.request),    // максимум по init containers
  sum(container_j.request)         // сумма по main containers (они работают параллельно)
)
Effective Pod request: пример
initContainer AОдин init контейнер: requests memory 200Mi. Выполняется первым, потом завершается.
initContainer BВторой init контейнер: requests memory 500Mi. Выполняется после A, в момент его работы A уже завершён — память освобождена.
container appMain контейнер: requests memory 300Mi. Работает параллельно с другими main.
container sidecarSidecar main контейнер: requests memory 150Mi. Работает параллельно с app.
effective requestmax(max(200,500), sum(300,150)) = max(500, 450) = 500Mi. Scheduler резервирует 500Mi памяти на Node. Если бы init суммировались: 200+500+300+150 = 1150Mi — было бы overcommit. Поскольку init sequential — Pod в любой момент использует не более max.
TIP

Это формула важна для capacity planning. На CKAD вопрос может быть: «у Pod 2 init container на 1Gi памяти каждый и 2 main по 500Mi. Сколько резервирует scheduler?» — Ответ: max(max(1Gi, 1Gi), 500Mi+500Mi) = max(1Gi, 1Gi) = 1Gi. Для native sidecar (initContainer с restartPolicy: Always, GA v1.33) правило другое — sidecar считается «всегда работающим», его resource добавляются к main части.


Probes: init containers их НЕ имеют

Init containers не могут использовать probes (livenessProbe, readinessProbe, startupProbe). Это by design:

  • init container — это короткоживущий процесс, который должен просто завершиться с exit 0;
  • если init упадёт — kubelet рестартанёт его по restartPolicy;
  • нет понятия «init container Ready», есть только «exited 0 или нет».

Если ваш init container — это long-running процесс, который не завершается (например, веб-сервер), вы делаете что-то не то. Это либо main container, либо native sidecar (init container с restartPolicy: Always, GA v1.33).


Логи init containers

Логи init containers видны через специальный флаг -c:

# Логи всех контейнеров Pod (только main по умолчанию)
kubectl logs my-pod

# Логи конкретного контейнера, включая init
kubectl logs my-pod -c wait-for-db
kubectl logs my-pod -c run-migrations

# Список контейнеров в Pod (с типом)
kubectl get pod my-pod -o jsonpath='{.spec.initContainers[*].name}'
kubectl describe pod my-pod | grep -A2 'Init Containers'

После того как init container завершился, его логи доступны до удаления Pod через kubectl logs -c <name>. Это критично для отладки: если миграция failed — вы хотите видеть её output до того, как Pod пересоздан.


Init containers vs sidecar

СвойствоInit containerNative sidecar (GA v1.33)
Где объявляетсяspec.initContainersspec.initContainers
restartPolicy контейнеране указан (default)Always
Когда стартуетsequential до mainдо main, не блокирует
Когда завершаетсясам, exit 0вместе с main containers
Probesнетда
Resource в расчётеmax по initsum (как main)

Один и тот же манифест initContainers может содержать оба типа — обычные init без restartPolicy и sidecars с restartPolicy: Always. kubelet разруливает по полю restartPolicy.


Проверка знанийKnowledge check
Pod имеет 3 init containers по 500Mi памяти каждый и 2 main containers по 200Mi. Сколько памяти резервирует scheduler?
ОтветAnswer
500Mi. Effective Pod request для memory = max(max(initContainers), sum(containers)) = max(max(500,500,500), 200+200) = max(500, 400) = 500Mi. Init containers sequential — в любой момент работает один. Main containers параллельно — их requests суммируются.
Проверка знанийKnowledge check
Init container exit 1. Что делает kubelet?
ОтветAnswer
Перезапускает только этот init container (не Pod целиком и не предыдущие, уже завершившиеся init containers) согласно restartPolicy Pod (по умолчанию Always). Pod висит в фазе Init:Error, при повторных падениях — Init:CrashLoopBackOff с экспоненциальной задержкой. Следующий init container в очереди не стартует. Main containers не стартуют.
Проверка знанийKnowledge check
Можно ли указать livenessProbe для init container?
ОтветAnswer
Нет. Init containers не поддерживают probes (liveness, readiness, startup). Они должны просто завершиться с exit 0 — нет понятия Ready. Если ваш init container — это long-running процесс, вы используете wrong tool. Long-running процесс рядом с main — это native sidecar (init container с restartPolicy: Always, GA v1.33, и у такого probes уже можно).
Проверка знанийKnowledge check
Где посмотреть логи failed init container?
ОтветAnswer
kubectl logs <pod> -c <init-container-name>. Без флага -c kubectl показывает логи main containers (или просит уточнить, какой). Логи init container хранятся пока существует Pod — после удаления Pod они пропадают. Это причина использовать persistent logging (Loki, ELK) для production: чтобы отладить миграцию, которая failed и Pod был удалён, нужны логи в центральном хранилище.

Проверьте понимание

Результат: 0 из 0
Прикладной
Вопрос 1 из 5. Pod имеет 3 init containers по 500Mi памяти каждый и 2 main containers по 200Mi. Сколько памяти резервирует scheduler для Pod?

Закончили урок?

Отметьте его как пройденный, чтобы отслеживать свой прогресс

Войдите чтобы оценить урок

Прогресс модуля
0 из 5