Wat je aan het einde hebt
Een Deployment-manifest met een of meer init containers die setuptaken uitvoeren (wachten op een dependency, databasemigraties draaien, configuratie ophalen, filesysteem-permissies fixen) voordat de applicatiecontainer start. Je weet ook hoe je falende init containers debugt en wanneer je beter sidecar containers kunt gebruiken.
Vereisten
- Een draaiend Kubernetes-cluster (v1.28 of nieuwer voor sidecar-container-support; elke v1.6+ cluster voor gewone init containers)
kubectlgeconfigureerd en verbonden met het cluster- Bekendheid met Deployment- en Pod-specs
- Voor resource sizing op init containers, zie Kubernetes resource requests en limits
Wat init containers doen
Init containers zijn speciale containers in de initContainers-array van een pod spec. Ze draaien sequentieel tot voltooiing voordat een applicatiecontainer start. Elke init container moet afsluiten met exit code 0 voor de volgende begint. Faalt er een, dan herstart de kubelet hem volgens het restartPolicy van de pod.
Drie eigenschappen maken ze bruikbaar:
- Ander image. Een init container kan
busybox,mysql-clientofvaultgebruiken zonder de applicatie-image op te blazen. - Blokkeergarantie. De hoofdcontainers starten pas als elke init container geslaagd is. Geen race conditions.
- Sequentiele volgorde. Init containers draaien in declaratievolgorde, dus elke container kan bouwen op de side-effects van de vorige.
Init containers ondersteunen geen livenessProbe, readinessProbe of startupProbe. Ze draaien tot voltooiing; het zijn geen langlopende processen.
Wachten op een dependency
Docker Compose gebruikt depends_on: condition: service_healthy om het starten van een container te blokkeren tot een andere service gezond is. Kubernetes heeft geen equivalent veld op pod-spec-niveau. Als je van Docker Compose migreert, bekijk dan de volledige migratietutorial voor de bredere context.
Een init container met een retry-loop vult dit gat:
apiVersion: apps/v1
kind: Deployment
metadata:
name: api-server
spec:
replicas: 1
selector:
matchLabels:
app: api-server
template:
metadata:
labels:
app: api-server
spec:
initContainers:
- name: wait-for-postgres
image: busybox:1.37
command:
- sh
- -c
- |
until nc -z postgres-service 5432; do
echo "Wachten op PostgreSQL op postgres-service:5432..."
sleep 2
done
containers:
- name: api
image: mycompany/api-server:3.2.0
ports:
- containerPort: 8080
nc -z probeert een TCP-verbinding zonder data te versturen. Zodra PostgreSQL verbindingen accepteert, sluit de init container af met exit 0 en start de api-container.
Voor DNS-checks (wachten tot een Service bestaat in het cluster) gebruik je nslookup:
until nslookup postgres-service.$(cat /var/run/secrets/kubernetes.io/serviceaccount/namespace).svc.cluster.local; do
echo "Wachten op DNS..."
sleep 2
done
Dit werkt alleen binnen een pod. Cross-pod startup ordering (zorgen dat Pod A draait voordat Pod B wordt aangemaakt) vereist externe orkestratie: Helm hooks, Argo CD sync waves, of retry-logica op applicatieniveau.
Databasemigraties draaien
Een init container kan je migratietool draaien voordat de applicatie opstart, zodat het schema gegarandeerd klopt:
initContainers:
- name: db-migrate
image: mycompany/api-migrations:3.2.0 # apart migration-image
command: ["./migrate", "--target=latest"]
env:
- name: DATABASE_URL
valueFrom:
secretKeyRef:
name: db-credentials
key: url
resources:
requests:
cpu: "250m"
memory: "256Mi"
limits:
memory: "512Mi"
De concurrency-val. Wanneer een Deployment opschaalt naar meerdere replica's, draait elke pod zijn init containers onafhankelijk. Drie replica's betekent drie gelijktijdige migratieruns tegen dezelfde database. Migratietools als Atlas, Flyway en Liquibase gebruiken advisory locks om dit veilig te maken, maar niet alle tools doen dat. Ondersteunt jouw migratietool geen locking, gebruik dan een Kubernetes Job met parallelism: 1. Jobs bieden ook betere log-retentie en ontkoppelde rollback.
Voeg een timeout toe om hangende migraties af te vangen:
timeout 300 ./migrate --target=latest
Zonder timeout blokkeert een vastgelopen migratie de pod-startup oneindig. De pod toont dan Init:0/1 tot in de eeuwigheid.
Configuratie ophalen van een externe bron
Init containers kunnen configuratie ophalen uit Vault, AWS Secrets Manager of een remote config-service en die naar een gedeeld volume schrijven. De applicatiecontainer leest bij startup van dat volume.
initContainers:
- name: fetch-config
image: vault:1.18
command:
- sh
- -c
- |
vault login -method=kubernetes role=api-server
vault kv get -field=config secret/api-server > /config/app.conf
env:
- name: VAULT_ADDR
value: "https://vault.internal:8200"
volumeMounts:
- name: config-vol
mountPath: /config
containers:
- name: api
image: mycompany/api-server:3.2.0
volumeMounts:
- name: config-vol
mountPath: /config
readOnly: true
volumes:
- name: config-vol
emptyDir: {}
Voor gevoelige data gebruik je emptyDir met medium: Memory, zodat de config nooit de disk raakt:
volumes:
- name: config-vol
emptyDir:
medium: Memory # RAM-backed tmpfs
Native Kubernetes Secrets zijn base64-encoded, standaard niet versleuteld at rest. Voor multi-tenant clusters of compliance-gevoelige workloads is secrets ophalen via een init container vanuit een dedicated vault een sterker patroon.
Data delen via emptyDir-volumes
Het emptyDir-volume is het standaardmechanisme om data door te geven van een init container naar de applicatiecontainer. De lifecycle is simpel:
- Declareer een
emptyDir-volume in de pod spec. - Mount het in zowel de init container als de applicatiecontainer.
- De init container schrijft bestanden. Hij sluit af.
- De applicatiecontainer leest die bestanden.
- Het volume wordt vernietigd wanneer de pod stopt.
Omdat init containers klaar zijn voordat applicatiecontainers starten, heb je een ingebouwde timinggarantie: de applicatie leest nooit een half geschreven bestand.
Compleet voorbeeld dat content downloadt in een nginx-container:
apiVersion: v1
kind: Pod
metadata:
name: content-init-demo
spec:
initContainers:
- name: download-content
image: busybox:1.37
command:
- wget
- "-O"
- "/work-dir/index.html"
- "http://info.cern.ch"
volumeMounts:
- name: workdir
mountPath: /work-dir
containers:
- name: nginx
image: nginx:1.27
ports:
- containerPort: 80
volumeMounts:
- name: workdir
mountPath: /usr/share/nginx/html
volumes:
- name: workdir
emptyDir: {}
Bron: Kubernetes configure pod initialization tutorial
Filesysteem-permissies fixen
Init containers kunnen als root draaien om ownership of permissies op een volume te corrigeren, ook als de applicatiecontainer als non-root draait:
initContainers:
- name: fix-permissions
image: busybox:1.37
command: ['sh', '-c', 'chown -R 1000:1000 /data']
volumeMounts:
- name: data-vol
mountPath: /data
securityContext:
runAsUser: 0 # root, alleen tijdens init
containers:
- name: app
image: mycompany/api-server:3.2.0
securityContext:
runAsUser: 1000
runAsNonRoot: true
volumeMounts:
- name: data-vol
mountPath: /data
Root-toegang is beperkt tot de init container. De applicatiecontainer draait zonder privileges.
Volgorde en failure handling
Init containers draaien in declaratievolgorde. Geen parallelisme. De kubelet wacht tot elke container afsluit met exit 0 voordat de volgende start.
De pod-status weerspiegelt de voortgang van init containers:
| Status | Betekenis |
|---|---|
Init:0/3 |
Geen init containers afgerond |
Init:1/3 |
Eerste afgerond, tweede draait |
Init:Error |
Een init container sloot af met non-zero exit code |
Init:CrashLoopBackOff |
Een init container faalt herhaaldelijk |
Wat er bij een fout gebeurt, hangt af van het restartPolicy van de pod:
restartPolicy |
Gedrag bij init container failure |
|---|---|
Always (Deployment-standaard) |
Kubelet herstart de falende init container met exponential backoff (10s, 20s, 40s, tot 5 min) |
OnFailure |
Zelfde herstart met backoff |
Never |
Pod wordt Failed, geen herstart |
De kubelet herstart alleen de falende init container, niet de hele reeks. Init containers die al geslaagd zijn, draaien niet opnieuw.
Een pod die vastzit in Init:CrashLoopBackOff is iets anders dan een pod in ContainerCreating. De ContainerCreating-status betekent dat init containers al klaar zijn en de kubelet bezig is met de prerequisites van de hoofdcontainer (volumes, netwerk). Zie je ContainerCreating, bekijk dan pods debuggen die vasthangen in ContainerCreating.
Falende init containers debuggen
Begin met kubectl describe pod om Events en de init container-status te bekijken:
kubectl describe pod api-server-7f8b4c5d6-x9k2m
Zoek naar de Init Containers:-sectie in de output. Daar zie je de state, exit code en restart count van elke init container.
Logs lezen van een specifieke init container:
kubectl logs api-server-7f8b4c5d6-x9k2m -c wait-for-postgres
Als de init container al gecrasht en herstart is, lees dan de logs van de vorige run:
kubectl logs api-server-7f8b4c5d6-x9k2m -c wait-for-postgres --previous
Voeg set -x toe aan shell-based init containers voor verbose command tracing:
command:
- sh
- -c
- |
set -x # print elk commando voor uitvoering
until nc -z postgres-service 5432; do
sleep 2
done
Bron: Kubernetes debug init containers guide
Resource requests en init container scheduling
De Kubernetes-scheduler berekent de effectieve resource-requirements van een pod als:
effective request = max(hoogste enkele init container request, som van alle app container requests)
Dat betekent dat een migratie-init-container die 4 Gi geheugen aanvraagt, de scheduling-footprint van de hele pod opblaast, ook al is dat geheugen maar een paar seconden nodig. De scheduler reserveert genoeg capaciteit voor welke fase (init of run) dan ook de grootste is.
Maak init container resource requests passend. Een init container die alleen nc -z draait heeft geen 512 Mi geheugen nodig. Te grote init container requests kunnen voorkomen dat pods op kleinere nodes worden ingepland.
Meer over hoe requests en limits scheduling, QoS class-toewijzing en overcommit-strategie beinvloeden: Kubernetes resource requests en limits.
Sidecar containers vs. init containers
Kubernetes 1.28 introduceerde native sidecar containers als een speciale vorm van init container. De feature bereikte stable (GA) in Kubernetes 1.33. De feature gate SidecarContainers staat standaard aan sinds Kubernetes 1.29.
Een sidecar is gewoon een init container met restartPolicy: Always:
initContainers:
- name: log-shipper
image: fluentd:v1.17
restartPolicy: Always # dit maakt het een sidecar
volumeMounts:
- name: log-vol
mountPath: /app/logs
containers:
- name: app
image: mycompany/api-server:3.2.0
volumeMounts:
- name: log-vol
mountPath: /app/logs
volumes:
- name: log-vol
emptyDir: {}
De gedragsverschillen zijn flink:
| Gedrag | Gewone init container | Sidecar (restartPolicy: Always) |
|---|---|---|
| Levensduur | Stopt voordat app start | Draait de hele pod-levensduur |
| Blokkeert volgende container | Tot exit 0 | Tot startupProbe slaagt (als gedefinieerd) |
| Probe-support | Geen | livenessProbe, readinessProbe, startupProbe |
| Resource accounting | max(init, app) | Opgeteld bij app container som |
| Job-voltooiing | N.v.t. | Blokkeert Job-voltooiing niet |
Gebruik een gewone init container voor eenmalige setup: dependency waiting, migraties, config ophalen, permissies fixen. Gebruik een sidecar voor processen die naast de applicatie moeten draaien: log shippers, metrics exporters, service mesh proxies (Envoy, Linkerd).
Voor native sidecar-support was het draaien van een log shipper als gewone container in een Job problematisch: de shipper hield de pod in leven nadat de hoofdcontainer van de Job klaar was. Native sidecars blokkeren Job-voltooiing niet.
Resultaat verifieren
Na het applyen van een Deployment met init containers:
# Pods bekijken tijdens startup
kubectl get pods -l app=api-server -w
# Verwacht: Init:0/1 -> Init:1/1 -> Running
# Bevestig dat de init container klaar is
kubectl describe pod <pod-name>
# Zoek naar "Init Containers:" sectie; state moet "Terminated: Completed" zijn
# Check applicatielogs om te bevestigen dat alles correct startte
kubectl logs <pod-name> -c api
Veelvoorkomende problemen
| Symptoom | Waarschijnlijke oorzaak | Oplossing |
|---|---|---|
Pod hangt vast in Init:0/1 |
Init container wacht op een service die niet bestaat of niet klaar is | Controleer de doelservice met kubectl get svc en kubectl get endpoints |
Init:CrashLoopBackOff |
Init container-commando sluit af met non-zero exit code | Lees logs met kubectl logs <pod> -c <init-name> en fix het commando of image |
| Meerdere replica's veroorzaken dubbele migraties | Migratietool gebruikt geen advisory locks | Stap over naar een Job met parallelism: 1 of gebruik een tool met lockingsupport |
| Pod niet inplanbaar na toevoegen init container | Init container resource request te groot voor beschikbare nodes | Pas de resources.requests van de init container aan |
| Init container werkt lokaal maar faalt in het cluster | Image pull error of verkeerde registry credentials | Check Events in kubectl describe pod voor ImagePullBackOff |