De Linux-opdrachtregel en Bash-scripting beheersen · Les

Slanke Dockerfiles en shell-entrypoints schrijven

Schrijf multi-stage-buildscripts en robuuste entrypoint-shims met signaalverwerking en configuratietemplating.

Les 1 van 413 stappen

Slanke Dockerfiles en shell-entrypoints schrijven is een gratis De Linux-opdrachtregel en Bash-scripting beheersen-les op CoddyKit. Dit is les 1 van 4. Je kunt de volledige les hieronder gratis lezen en daarna in de browser praktisch oefenen met een ingebouwde code-editor en een AI-begeleider die 24/7 beschikbaar is. Deze les maakt deel uit van het leertraject De Linux-opdrachtregel en Bash-scripting beheersen. Je voortgang wordt gesynchroniseerd op het web en in de CoddyKit-app. De cursus De Linux-opdrachtregel en Bash-scripting beheersen bevat in totaal 4 lessen.

Waarom slanke Dockerfiles belangrijk zijn voor DevOps

In productieomgevingen heeft elke megabyte in een Docker-image een prijs: tragere downloads, een groter aanvalsoppervlak en verspilde opslagruimte in het register. Slanke Dockerfiles in combinatie met robuuste shell-entrypoints zijn kenmerkend voor volwassen DevOps-praktijken.

  • Builds met meerdere fasen scheiden hulpmiddelen voor het bouwen van de uiteindelijke runtime-image, waardoor de omvang sterk afneemt.
  • Entrypoint-shims zijn kleine shellscripts die containers initialiseren: ze vullen configuratiebestanden in op basis van sjablonen, controleren omgevingsvariabelen, verwerken signalen en voeren uiteindelijk het hoofdproces uit.
  • Samen vormen ze de basis van betrouwbare, draagbare containerworkloads in Kubernetes, ECS en omgevingen op fysieke servers.

In deze les behandelt u beide disciplines van begin tot eind, met patronen van productiekwaliteit die u rechtstreeks in uw CI-pijplijnen kunt gebruiken.

Anatomie van een Dockerfile met meerdere fasen

Een Dockerfile met meerdere fasen gebruikt meerdere FROM-instructies. Elke fase is een geïsoleerde verzameling lagen; u kopieert alleen de artefacten die u nodig hebt naar de volgende fase.

  • Fase 0 (builder): installeert compilers, testrunners en afhankelijkheden voor het bouwen.
  • Fase 1 (runtime): begint met een minimale basis (bijvoorbeeld alpine, distroless) en kopieert alleen gecompileerde binaire bestanden of applicatiebundels.
  • De uiteindelijke image bevat nooit gcc, make of broncode, tenzij u deze expliciet kopieert.

Gebruik --from=<stage> in COPY om bestanden over de grenzen van fasen heen te kopiëren. Geef fasen een naam met AS <name> voor betere leesbaarheid en om gericht te kunnen bouwen met docker build --target.

# ---- Stage 0: builder ----
FROM golang:1.22-alpine AS builder
WORKDIR /src
COPY go.mod go.sum ./
RUN go mod download
COPY . .
RUN CGO_ENABLED=0 GOOS=linux go build -trimpath -ldflags='-s -w' -o /app/server ./cmd/server

# ---- Stage 1: runtime ----
FROM gcr.io/distroless/static-debian12
COPY --from=builder /app/server /server
ENTRYPOINT ["/server"]

Lagen beperken en de cache ongeldig maken

Elke RUN-, COPY- en ADD-instructie maakt een nieuwe laag. Instructies in een slechte volgorde maken de buildcache onnodig ongeldig, waardoor CI-pijplijnen traag worden.

  • Kopieer manifesten van afhankelijkheden (package.json, go.mod, requirements.txt) voordat u de broncode kopieert, zodat het installeren van afhankelijkheden onafhankelijk wordt gecachet.
  • Combineer verwante opdrachten met && en ruim in dezelfde RUN-laag op, zodat de pakketcache niet in tussenliggende lagen achterblijft.
  • Gebruik --no-cache in pakketbeheerders en verwijder lijstbestanden na de installatie.
FROM python:3.12-slim AS builder
WORKDIR /app

# 1. Install deps first (cached until requirements change)
COPY requirements.txt .
RUN pip install --no-cache-dir --prefix=/install -r requirements.txt

# 2. Copy source (cache busted only when code changes)
COPY src/ ./src/

FROM python:3.12-slim
COPY --from=builder /install /usr/local
COPY --from=builder /app/src /app/src
WORKDIR /app
CMD ["python", "-m", "src.main"]

BuildKit-geheimen en SSH-doorsturen

Privéregisters, SSH-sleutels en API-tokens mogen nooit in imagelagen terechtkomen. Docker BuildKit biedt hiervoor twee veilige mechanismen:

  • --secret: koppelt een geheim bestand alleen tijdens één RUN-stap, zonder het in een laag op te nemen. U krijgt toegang via /run/secrets/<id>.
  • --ssh: stuurt de socket van de SSH-agent op de host door naar de build, zodat git clone kan authenticeren zonder privésleutels in te bouwen.

Schakel BuildKit in met DOCKER_BUILDKIT=1 of via docker buildx build. De instructie # syntax=docker/dockerfile:1 maakt deze functies beschikbaar.

# syntax=docker/dockerfile:1
FROM node:20-alpine AS deps
WORKDIR /app
COPY package*.json ./

# Mount NPM token as a secret — never stored in the image
RUN --mount=type=secret,id=npm_token \
    NPM_TOKEN=$(cat /run/secrets/npm_token) \
    npm ci --prefer-offline

# Usage at build time:
# DOCKER_BUILDKIT=1 docker build \
#   --secret id=npm_token,src=~/.npmrc-token \
#   -t myapp:latest .

Een robuuste entrypoint-shim schrijven

De entrypoint-shim is een shellscript dat in de Dockerfile als ENTRYPOINT wordt ingesteld. Het bereidt de runtimeomgeving voor voordat het de controle overdraagt aan het hoofdproces.

Een goed gestructureerde shim volgt deze volgorde:

  • Stap 1: Stel set -euo pipefail in, zodat elke fout het script vroegtijdig afbreekt.
  • Stap 2: Controleer verplichte omgevingsvariabelen en breek snel af met een duidelijke melding.
  • Stap 3: Vul configuratiebestanden in op basis van omgevingsvariabelen.
  • Stap 4: Registreer signaalverwerkers voor gecontroleerd afsluiten.
  • Stap 5: exec "$@" — vervang de shell door het hoofdproces, zodat PID 1 de applicatie is en niet de shim.

De laatste exec is essentieel: zonder deze opdracht worden signalen van Kubernetes of de Docker-runtime niet doorgestuurd naar het onderliggende proces.

#!/usr/bin/env bash
set -euo pipefail

# Step 2: validate required env vars
REQUIRED_VARS=(DATABASE_URL APP_SECRET PORT)
for var in "${REQUIRED_VARS[@]}"; do
  if [[ -z "${!var:-}" ]]; then
    echo "[entrypoint] ERROR: required env var '$var' is not set" >&2
    exit 1
  fi
done

# Step 3: template config (see next scene)

# Step 4: signal handling (see scene after that)

# Step 5: hand off to CMD
exec "$@"

Configuratiesjablonen met envsubst

envsubst (uit het GNU-pakket gettext, in Alpine beschikbaar als gettext) vervangt ${VAR}-plaatshouders in een sjabloonbestand door de huidige waarden van de bijbehorende omgevingsvariabelen.

  • Neem een configuratiesjabloon met de extensie *.tmpl op in de image; het entrypoint rendert dit bij het opstarten.
  • Geef de variabelenlijst expliciet door aan envsubst, zodat niet per ongeluk andere dollartekens worden uitgebreid (bijvoorbeeld in een nginx-regex).
  • Schrijf het gegenereerde bestand naar een schrijfbaar pad zoals /tmp of een speciaal configuratievolume.
#!/usr/bin/env bash
# Template: /etc/nginx/conf.d/app.conf.tmpl contains:
# server { listen ${NGINX_PORT}; server_name ${SERVER_NAME}; ... }

export NGINX_PORT=${NGINX_PORT:-8080}
export SERVER_NAME=${SERVER_NAME:-localhost}

envsubst '${NGINX_PORT} ${SERVER_NAME}' \
  < /etc/nginx/conf.d/app.conf.tmpl \
  > /etc/nginx/conf.d/app.conf

echo "[entrypoint] nginx config rendered:"
grep -E 'listen|server_name' /etc/nginx/conf.d/app.conf

exec "$@"

Signaalverwerking en gecontroleerd afsluiten

Containers ontvangen SIGTERM wanneer ze worden gestopt door Kubernetes, ECS of docker stop. Als uw entrypoint-shim PID 1 is en signalen niet doorstuurt, wordt het hoofdproces na de respijtperiode beëindigd met SIGKILL — met verloren aanvragen of beschadigde gegevens als gevolg.

  • Gebruik trap om SIGTERM en SIGINT in de shim op te vangen.
  • Stuur het signaal door naar de PID van het onderliggende proces met kill -TERM "$child".
  • Gebruik wait "$child" om te wachten totdat het onderliggende proces afsluit en geef vervolgens de afsluitcode ervan door.
  • U kunt ook exec gebruiken om de shell volledig te vervangen — dan levert het besturingssysteem signalen rechtstreeks aan het onderliggende proces en is geen trap nodig. Dit is het aanbevolen patroon voor eenvoudige gevallen.
#!/usr/bin/env bash
set -euo pipefail

# Start main process in background
"$@" &
child=$!

# Forward SIGTERM and SIGINT to the child
trap 'echo "[entrypoint] caught SIGTERM, forwarding..."; kill -TERM "$child"' TERM
trap 'echo "[entrypoint] caught SIGINT, forwarding...";  kill -INT  "$child"' INT

# Wait for child to exit and capture its exit code
wait "$child"
exit $?

tini gebruiken als minimaal init-proces

Wanneer uw container onderliggende processen start (bijvoorbeeld een shell die werkers fork’t), hebt u een echt init-proces nodig om zombieprocessen op te ruimen. tini is een klein init-binaire bestand dat speciaal voor containers is ontworpen.

  • Voeg tini toe aan uw image en stel het in als wrapper voor het entrypoint.
  • Het ruimt zombieprocessen op, stuurt signalen correct door en sluit af met de statuscode van het onderliggende proces.
  • Docker levert een ingebouwde tini die u activeert met docker run --init, maar door deze in de image op te nemen blijft het gedrag consistent tussen runtimes (Kubernetes, ECS enzovoort).
FROM node:20-alpine

# Install tini for proper signal handling and zombie reaping
RUN apk add --no-cache tini

WORKDIR /app
COPY --chown=node:node . .
RUN npm ci --omit=dev

USER node

# tini wraps CMD; forwards SIGTERM and reaps zombies
ENTRYPOINT ["/sbin/tini", "--"]
CMD ["node", "server.js"]

Uitvoeren als niet-rootgebruiker

Containers die als root (UID 0) worden uitgevoerd, vormen een groot beveiligingsrisico: bij een ontsnapping uit de container krijgt een aanvaller volledige toegang tot de host. Schakel altijd over naar een gebruiker zonder verhoogde rechten voordat u het hoofdproces uitvoert.

  • Maak in de Dockerfile een speciale systeemgebruiker en -groep aan met addgroup / adduser (Alpine) of groupadd / useradd (Debian).
  • Wijzig het eigendom van applicatiebestanden met COPY --chown=appuser:appgroup — dit is efficiënter dan een aparte RUN chown-laag.
  • Schakel over naar de gebruiker met de USER-instructie. Het entrypoint en CMD nemen deze gebruiker over.
  • securityContext.runAsNonRoot: true in Kubernetes weigert een image te starten die nog als root wordt uitgevoerd.
FROM python:3.12-slim

# Create non-root user
RUN groupadd --gid 1001 appgroup && \
    useradd --uid 1001 --gid appgroup --shell /bin/bash --create-home appuser

WORKDIR /app

COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt

# Copy app files with correct ownership in a single layer
COPY --chown=appuser:appgroup src/ ./src/
COPY --chown=appuser:appgroup entrypoint.sh /entrypoint.sh
RUN chmod +x /entrypoint.sh

USER appuser

ENTRYPOINT ["/entrypoint.sh"]
CMD ["python", "-m", "src.main"]

Statuscontroles en gereedheidsprobes in de image

De liveness- en readiness-probes van Kubernetes worden in manifesten gedefinieerd, maar u kunt ook een HEALTHCHECK in de Dockerfile opnemen voor zelfstandige docker run- en Docker Compose-omgevingen.

  • HEALTHCHECK --interval=10s --timeout=3s --start-period=15s --retries=3 CMD ...
  • Gebruik curl --fail of wget -qO- voor HTTP-services; controleer voor niet-HTTP-daemons een socket met /dev/tcp/localhost/PORT.
  • Installeer alleen wat u nodig hebt: voeg in distroless-images niet alleen voor statuscontroles curl toe — gebruik een speciaal probe-binaire bestand of het eigen status-binaire bestand van de applicatie.
FROM nginx:1.27-alpine

COPY nginx.conf /etc/nginx/nginx.conf
COPY dist/ /usr/share/nginx/html/

# Lightweight health check using bash TCP pseudo-device
# (no curl needed — works on any image with bash)
HEALTHCHECK --interval=15s --timeout=5s --start-period=10s --retries=3 \
  CMD bash -c 'exec 3<>/dev/tcp/localhost/80 && echo -e "GET /health HTTP/1.0\r\n" >&3 && cat <&3 | grep -q "200 OK"' || exit 1

EXPOSE 80

Alles samenbrengen: een productie-entrypoint

Hieronder staat een complete entrypoint-shim van productiekwaliteit waarin alle patronen uit deze les worden gecombineerd: validatie van omgevingsvariabelen, configuratiesjablonen, het doorsturen van signalen en overdracht met exec. Dit patroon wordt gebruikt in echte Node.js-, Python- en Go-microservices die op Kubernetes worden geïmplementeerd.

  • Elke sectie bevat commentaar en dient als zelfdocumenterende sjabloon.
  • De shim blijft onder de 50 regels — entrypoints moeten eenvoudig en controleerbaar zijn.
  • Let op de laatste exec "$@": nadat alle voorbereidingen zijn getroffen, wordt de shell vervangen door het applicatieproces, zodat dit PID 1 wordt en alle signalen van het besturingssysteem rechtstreeks ontvangt.
#!/usr/bin/env bash
# entrypoint.sh — production-grade container entrypoint shim
set -euo pipefail

# ── 1. Validate required environment variables ──────────────────
REQUIRED=(DATABASE_URL APP_SECRET PORT LOG_LEVEL)
for var in "${REQUIRED[@]}"; do
  [[ -n "${!var:-}" ]] || { echo "[entrypoint] FATAL: $var is not set" >&2; exit 1; }
done

# ── 2. Set safe defaults for optional variables ─────────────────
export HOST=${HOST:-0.0.0.0}
export WORKERS=${WORKERS:-2}

# ── 3. Render config template ───────────────────────────────────
if [[ -f /etc/app/app.conf.tmpl ]]; then
  envsubst '${DATABASE_URL} ${PORT} ${LOG_LEVEL} ${HOST}' \
    < /etc/app/app.conf.tmpl \
    > /etc/app/app.conf
  echo "[entrypoint] config rendered at /etc/app/app.conf"
fi

# ── 4. Wait for dependent services (optional, fast) ─────────────
if [[ -n "${WAIT_FOR_HOST:-}" ]]; then
  echo "[entrypoint] waiting for ${WAIT_FOR_HOST}:${WAIT_FOR_PORT:-5432}..."
  until bash -c "exec 3<>/dev/tcp/${WAIT_FOR_HOST}/${WAIT_FOR_PORT:-5432}" 2>/dev/null; do
    sleep 1
  done
  echo "[entrypoint] dependency ready"
fi

# ── 5. Hand off to CMD (PID 1 becomes the application) ──────────
echo "[entrypoint] starting: $*"
exec "$@"

Kennistoets: signaalverwerking in entrypoints

Test uw kennis van signaalverwerking in container-entrypointscripts.

Samenvatting: slanke Dockerfiles en shell-entrypoints

U hebt de volledige productiestack voor het maken van containers behandeld:

  • Builds met meerdere fasen gebruiken meerdere FROM-instructies om compilers en bouwgereedschappen uit de uiteindelijke image te houden, wat slanke, minimale runtime-lagen oplevert.
  • Volgorde van lagen — kopieer manifesten van afhankelijkheden vóór de broncode — maximaliseert cachetreffers en versnelt CI-pijplijnen.
  • BuildKit-geheimen en SSH-koppelingen houden inloggegevens buiten de imagegeschiedenis zonder builds waarvoor authenticatie nodig is te verstoren.
  • Entrypoint-shims controleren omgevingsvariabelen, vullen configuratiebestanden in met envsubst en dragen de uitvoering met exec "$@" over aan de applicatie.
  • Signaalverwerking vereist óf exec (zodat de applicatie PID 1 is) óf een expliciet patroon met trap + kill + wait wanneer achtergrondtaken worden gebruikt.
  • tini zorgt voor het opruimen van zombieprocessen en het correct doorsturen van signalen wanneer de container meerdere processen start.
  • Niet-rootgebruikers en HEALTHCHECK-instructies maken een veilige, observeerbare image compleet die klaar is voor productieworkloads op Kubernetes.

Combineer deze patronen consequent en uw images worden kleiner, sneller te implementeren en aanzienlijk robuuster onder productieomstandigheden.

Gratis beginnen

Leer Bash met een AI-tutor — gratis

Schrijf echte code en voer die uit in je browser, krijg direct hulp van een AI-tutor die 24/7 beschikbaar is en ga verder waar je gebleven bent op het web of in de app.

Cursussen
22
Lessen
88

Veelgestelde vragen

Is de les “Slanke Dockerfiles en shell-entrypoints schrijven” gratis?

Ja — de volledige tekst van “Slanke Dockerfiles en shell-entrypoints schrijven” kun je hier gratis op het web lezen. Als je interactief wilt oefenen met een ingebouwde code-editor en een AI-begeleider die 24/7 beschikbaar is, en de rest van de cursus De Linux-opdrachtregel en Bash-scripting beheersen wilt ontgrendelen, kun je upgraden naar CoddyKit PRO. De cursus De Linux-opdrachtregel en Bash-scripting beheersen bevat in totaal 4 lessen.

Wat leer ik in “Slanke Dockerfiles en shell-entrypoints schrijven”?

Schrijf multi-stage-buildscripts en robuuste entrypoint-shims met signaalverwerking en configuratietemplating. Je oefent met De Linux-opdrachtregel en Bash-scripting beheersen door code rechtstreeks in de browser uit te voeren. Een AI-begeleider die 24/7 beschikbaar is beantwoordt je vragen terwijl je de les doorwerkt.

Heb ik ervaring nodig om met De Linux-opdrachtregel en Bash-scripting beheersen te beginnen?

Ervaring vooraf is niet nodig. De Linux-opdrachtregel en Bash-scripting beheersen op CoddyKit is opgebouwd voor beginners tot gevorderden, zodat je hier of bij het begin kunt starten en in je eigen tempo kunt leren. Dit is les 1 van 4.

Hoe lang duurt de les “Slanke Dockerfiles en shell-entrypoints schrijven”?

De meeste lessen van CoddyKit duren ongeveer 5–10 minuten. Elke les is kort en interactief, zodat je gestaag vooruitgaat en op het web en in de app precies verdergaat waar je was gebleven.

Kan ik code schrijven en uitvoeren in deze les over De Linux-opdrachtregel en Bash-scripting beheersen?

Ja. Elke les over De Linux-opdrachtregel en Bash-scripting beheersen bevat een ingebouwde code-editor, zodat je rechtstreeks in je browser echte code kunt schrijven en uitvoeren en direct feedback van AI krijgt — lokale installatie is niet nodig.

Alle lessen in deze cursus

  1. Slanke Dockerfiles en shell-entrypoints schrijven
  2. Configuraties templaten met envsubst en heredocs
  3. Cloudresources scripten met CLI en jq
  4. Health probes, readiness gates en wachtlussen
← Terug naar De Linux-opdrachtregel en Bash-scripting beheersen