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:
- kubelet стартует
wait-for-db. Контейнер крутится в цикле, пингуяpostgres:5432через netcat. Когда соединение установлено —nc -zexits 0, циклuntilзавершается, контейнер exits 0. - kubelet стартует
run-migrations. flyway применяет SQL миграции к базе. После успешного завершения exit 0. - 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
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).
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 (они работают параллельно)
)
Это формула важна для 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 container | Native sidecar (GA v1.33) |
|---|---|---|
| Где объявляется | spec.initContainers | spec.initContainers |
restartPolicy контейнера | не указан (default) | Always |
| Когда стартует | sequential до main | до main, не блокирует |
| Когда завершается | сам, exit 0 | вместе с main containers |
| Probes | нет | да |
| Resource в расчёте | max по init | sum (как main) |
Один и тот же манифест initContainers может содержать оба типа — обычные init без restartPolicy и sidecars с restartPolicy: Always. kubelet разруливает по полю restartPolicy.