Container image architectuur concepten

Container image architectuur concepten

Container images zijn de bouwstenen van moderne applicaties. Ze bevatten alles wat een applicatie nodig heeft: de code, de runtime, de bibliotheken en de configuratie. Dit artikel legt uit hoe images zijn opgebouwd uit lagen, hoe ze worden opgeslagen in registries en hoe de container runtime ze uitvoert als geïsoleerde processen.

Van applicatie naar container: het basismodel

Een container is een geïsoleerd proces dat draait op een gedeelde Linux-kernel. Het is geen virtuele machine — er is geen eigen kernel, geen volledig gesimuleerde hardware. De isolatie wordt bereikt via twee Linux-kernelmechanismen:

  • Namespaces — isoleren van processen, netwerk, bestandssysteem en gebruikers zodat een container zijn eigen wereld ziet
  • cgroups — begrenzen van CPU, geheugen en I/O gebruik, zodat een container niet meer resources kan gebruiken dan toegestaan

Image lagen: efficiënt en gedeeld

Een container image bestaat uit lagen (layers). Elke instructie in een Dockerfile die het bestandssysteem wijzigt, maakt een nieuwe laag aan. Lagen zijn alleen-lezen en kunnen worden gedeeld tussen meerdere images en containers.

# Dockerfile — elke RUN/COPY/ADD = nieuwe laag
FROM ubuntu:22.04          # Laag 1: basis OS (van Ubuntu repository)
RUN apt-get update && \    # Laag 2: pakketlijst bijwerken
    apt-get install -y python3 pip
COPY requirements.txt .    # Laag 3: requirements bestand
RUN pip install -r requirements.txt  # Laag 4: Python packages
COPY . /app                # Laag 5: applicatiecode
CMD ["python3", "/app/main.py"]  # Geen nieuwe laag (metadata)

Wanneer je een container start vanuit deze image, voegt de runtime een dunne schrijfbare laag toe bovenop de alleen-lezen image-lagen. Alle wijzigingen die de container maakt (logs, tijdelijke bestanden) gaan naar deze laag. Wanneer de container wordt verwijderd, verdwijnt ook deze laag.

Efficiënte Dockerfiles schrijven

# SLECHT: elke stap = aparte laag, cache wordt snel ongeldig
FROM python:3.12-slim
COPY . /app
RUN pip install -r /app/requirements.txt

# GOED: dependencies apart kopiëren voor betere caching
FROM python:3.12-slim
WORKDIR /app
COPY requirements.txt .          # Laag 1: alleen requirements
RUN pip install -r requirements.txt  # Laag 2: packages (gecached tenzij requirements wijzigt)
COPY . .                         # Laag 3: applicatiecode (verandert het meest)

# Multi-stage build: kleinere productie-image
FROM python:3.12 AS builder
COPY requirements.txt .
RUN pip install --target=/install -r requirements.txt

FROM python:3.12-slim            # Kleinere basis voor productie
COPY --from=builder /install /usr/local/lib/python3.12/site-packages
COPY . /app
CMD ["python3", "/app/main.py"]

Image naming en tagging

# Volledige image-naam structuur:
# [registry]/[namespace]/[naam]:[tag]

nginx                              # Shorthand voor docker.io/library/nginx:latest
nginx:1.25.3                       # Specifieke versie (aanbevolen!)
harbor.voorbeeld.nl/mijn-team/app:v2.1.0  # Private registry
registry.redhat.io/ubi9/ubi:latest  # Red Hat UBI

# Image bouwen en taggen
docker build -t mijn-app:1.0.0 .
docker tag mijn-app:1.0.0 harbor.voorbeeld.nl/productie/mijn-app:1.0.0

# Pushen naar registry
docker push harbor.voorbeeld.nl/productie/mijn-app:1.0.0

# Pullen
docker pull harbor.voorbeeld.nl/productie/mijn-app:1.0.0

# Image-digest (onveranderlijk, aanbevolen voor productie)
docker pull nginx@sha256:abc123def456...

Container runtimes: CRI-O en containerd

Kubernetes communiceert met container runtimes via de Container Runtime Interface (CRI). De twee meest gebruikte zijn:

  • CRI-O — ontwikkeld door Red Hat, standaard in OpenShift. Lichtgewicht, alleen CRI implementeren, geen extra functies
  • containerd — ontwikkeld door Docker, standaard in upstream Kubernetes (EKS, GKE, AKS). Breder ecosysteem
# CRI-O commando's (op een OpenShift/RHEL node)
crictl images                      # Geïnstalleerde images
crictl ps                          # Draaiende containers
crictl logs <container-id>         # Container-logs

# containerd (via ctr of nerdctl)
ctr images list
nerdctl ps
nerdctl run nginx:latest

OCI-standaard

De Open Container Initiative (OCI) definieert open standaarden voor container images en runtimes. Dankzij OCI zijn images die je bouwt met Docker, Podman of Buildah compatible met elke OCI-conforme runtime (CRI-O, containerd). Je hoeft niet Docker te gebruiken om containers te draaien.

# Podman — rootless container tool (goed alternatief voor Docker)
podman build -t mijn-app:1.0.0 .
podman push harbor.voorbeeld.nl/mijn-app:1.0.0
podman run -d -p 8080:80 nginx:1.25

# Buildah — OCI image bouwen zonder daemon
buildah from ubuntu:22.04
buildah run mijn-container -- apt-get install -y nginx
buildah commit mijn-container mijn-nginx:1.0

ImagePullSecrets in Kubernetes

# Secret aanmaken voor private registry
kubectl create secret docker-registry registry-secret \
  --docker-server=harbor.voorbeeld.nl \
  --docker-username=mijn-robot \
  --docker-password=MijnToken123

# In een pod gebruiken
spec:
  imagePullSecrets:
  - name: registry-secret
  containers:
  - name: mijn-app
    image: harbor.voorbeeld.nl/mijn-team/app:1.0.0

# Globaal koppelen aan een ServiceAccount
kubectl patch serviceaccount default \
  -p '{"imagePullSecrets":[{"name":"registry-secret"}]}'

Relevantie voor certificeringen

  • CKA/CKAD — ImagePullSecrets, image-naming, container runtime troubleshooting
  • EX280/EX380 — OpenShift image registry, S2I builds, imagestream tags
  • CGOA/CAPA — image-tags als bron voor GitOps-automatisering (Argo CD Image Updater, Renovate)