Linux-kommandolinjen og Bash-scripting på ekspertniveau · Lektion

Skriv slanke Dockerfiles og shell-entrypoints

Skriv multi-stage-buildscripts og robuste entrypoint-shims med signalhåndtering og skabelonbaseret konfiguration.

Lektion 1 af 413 trin

Skriv slanke Dockerfiles og shell-entrypoints er en gratis Linux-kommandolinjen og Bash-scripting på ekspertniveau-lektion på CoddyKit. Dette er lektion 1 af 4. Du kan læse hele lektionen gratis nedenfor — og derefter øve dig praktisk i browseren med en indbygget kodeeditor og en AI-vejleder, der er tilgængelig døgnet rundt. Den er en del af læringsforløbet i Linux-kommandolinjen og Bash-scripting på ekspertniveau, og dine fremskridt synkroniseres på tværs af nettet og CoddyKit-appen. Linux-kommandolinjen og Bash-scripting på ekspertniveau-kurset indeholder 4 lektioner i alt.

Hvorfor slanke Dockerfiler er vigtige for DevOps

I produktionsmiljøer har hver megabyte i et Docker-image en pris: langsommere hentning, større angrebsflader og spildt lagerplads i registre. Slanke Dockerfiler kombineret med robuste shell-startskripter er kendetegnende for moden DevOps-praksis.

  • Bygninger i flere trin adskiller værktøjer til byggetid fra det endelige runtime-image og reducerer størrelsen markant.
  • Startskriptlag er små shell-skripter, der klargør containere: de udfylder konfigurationsfiler fra skabeloner, validerer miljøvariabler, håndterer signaler og udfører til sidst hovedprocessen med exec.
  • Tilsammen udgør de rygraden i pålidelige, portable containerarbejdsbelastninger i Kubernetes, ECS og bare-metal-miljøer.

Denne lektion gennemgår begge discipliner fra ende til anden med mønstre i produktionskvalitet, som du kan indsætte direkte i dine byggeforløb.

Opbygningen af en Dockerfil i flere trin

En Dockerfil i flere trin bruger flere FROM-instruktioner. Hvert trin er et isoleret sæt lag; du kopierer kun de artefakter, du har brug for, til det næste trin.

  • Trin 0 (builder): installerer compilere, testkørsler og byggeafhængigheder.
  • Trin 1 (runtime): starter fra et minimalt grundlag (f.eks. alpine, distroless) og kopierer kun kompilerede binærfiler eller programpakker.
  • Det endelige image indeholder aldrig gcc, make eller kildekode, medmindre du udtrykkeligt kopierer dem.

Brug --from=<stage> i COPY til at hente filer på tværs af trin­grænser. Navngiv trin med AS <name> for bedre læsbarhed og målrettet valg med 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"]

Minimering af lag og ugyldiggørelse af cachen

Hver RUN-, COPY- og ADD-instruktion opretter et nyt lag. Dårligt rækkefølgede instruktioner ugyldiggør byggecachen unødvendigt og gør CI-forløb langsomme.

  • Kopiér afhængighedsmanifester (package.json, go.mod, requirements.txt) før du kopierer kildekoden, så installationen af afhængigheder caches uafhængigt.
  • Kæd relaterede kommandoer sammen med &&, og ryd op i det samme RUN-lag for at undgå at efterlade pakkecache i mellemlag.
  • Brug --no-cache i pakkehåndteringer, og fjern listefiler efter installationen.
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-hemmeligheder og SSH-viderestilling

Private registre, SSH-nøgler og API-tokens må aldrig forekomme i imagelag. Docker BuildKit tilbyder to sikre mekanismer:

  • --secret: monterer en hemmelighedsfil i et enkelt RUN-trin uden at indbygge den i et lag. Få adgang via /run/secrets/<id>.
  • --ssh: viderestiller værtsmaskinens SSH-agent-socket til bygningen, så git clone kan godkende uden at indlejre private nøgler.

Aktivér BuildKit med DOCKER_BUILDKIT=1 eller via docker buildx build. Direktivet # syntax=docker/dockerfile:1 låser op for disse funktioner.

# 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 .

Skrivning af et robust startskript

Startskriptet er et shell-script, der er angivet som ENTRYPOINT i Dockerfilen. Dets opgave er at klargøre runtime-miljøet, før det overdrager kontrollen til hovedprocessen.

Et velstruktureret startskript følger denne rækkefølge:

  • Trin 1: Angiv set -euo pipefail, så enhver fejl afbryder tidligt.
  • Trin 2: Validér påkrævede miljøvariabler, og afbryd hurtigt med en nyttig meddelelse.
  • Trin 3: Udfyld konfigurationsfiler fra miljøvariabler.
  • Trin 4: Registrér signalhåndteringer til kontrolleret nedlukning.
  • Trin 5: exec "$@" — erstat shellen med hovedprocessen, så PID 1 er applikationen og ikke startskriptet.

Det afsluttende exec er afgørende: uden det videresendes signaler fra Kubernetes eller Docker-runtime ikke til den underordnede 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 "$@"

Konfigurationsskabeloner med envsubst

envsubst (fra GNU-pakken gettext, som findes i Alpine som gettext) erstatter ${VAR}-pladsholdere i en skabelonfil med de aktuelle værdier for miljøvariablerne.

  • Medtag en *.tmpl-konfigurationsskabelon i imaget; startskriptet gengiver den ved opstart.
  • Angiv variabellisten eksplicit til envsubst, så den ikke ved et uheld udvider uvedkommende dollartegn (f.eks. i nginx-regulære udtryk).
  • Skriv den gengivne fil til en skrivbar sti som /tmp eller en dedikeret konfigurationsdiskenhed.
#!/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 "$@"

Signalhåndtering og kontrolleret nedlukning

Containere modtager SIGTERM, når de stoppes af Kubernetes, ECS eller docker stop. Hvis dit startskript er PID 1 og ikke videresender signaler, bliver hovedprocessen dræbt med SIGKILL efter fristen for kontrolleret nedlukning — hvilket kan medføre mistede forespørgsler eller ødelagte data.

  • Brug trap til at opfange SIGTERM og SIGINT i startskriptet.
  • Videresend signalet til den underordnede proces' PID med kill -TERM "$child".
  • Brug wait "$child" til at blokere, indtil den underordnede proces afsluttes, og videregiv derefter dens afslutningskode.
  • Alternativt kan du bruge exec til helt at erstatte shellen — så leverer operativsystemet signaler direkte til den underordnede proces, og der er ikke brug for trap. Dette er det foretrukne mønster i enkle tilfælde.
#!/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 $?

Brug af tini som en minimal init-proces

Når din container opretter underordnede processer (f.eks. en shell, der forgrener arbejdsprocesser), har du brug for en rigtig init-proces til at indsamle zombieprocesser. tini er en lille init-binærfil, der er udviklet specifikt til containere.

  • Føj tini til dit image, og angiv den som wrapper omkring startpunktet.
  • Den indsamler zombieprocesser, videresender signaler korrekt og afslutter med den underordnede proces' statuskode.
  • Docker leveres med en indbygget tini, som aktiveres med docker run --init, men ved at indlejre den i imaget sikrer du ensartet adfærd på tværs af runtime-miljøer (Kubernetes, ECS osv.).
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"]

Kørsel som en ikke-root-bruger

Containere, der kører som root (UID 0), udgør en betydelig sikkerhedsrisiko: et containerudslip giver fuld adgang til værtsmaskinen. Skift altid til en bruger uden privilegier, før du udfører hovedprocessen.

  • Opret en dedikeret systembruger og -gruppe i Dockerfilen med addgroup / adduser (Alpine) eller groupadd / useradd (Debian).
  • Skift ejerskab for programfiler med COPY --chown=appuser:appgroup — det er mere effektivt end et separat RUN chown-lag.
  • Skift til brugeren med USER-instruktionen. Startskriptet og CMD'en arver denne bruger.
  • Kubernetes securityContext.runAsNonRoot: true nægter at starte et image, der stadig kører som root.
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"]

Sundhedstjek og klarhedsprober i imaget

Kubernetes' liveness- og readiness-prober defineres i manifester, men du kan også indbygge et HEALTHCHECK i Dockerfilen til selvstændige docker run- og Docker Compose-miljøer.

  • HEALTHCHECK --interval=10s --timeout=3s --start-period=15s --retries=3 CMD ...
  • Brug curl --fail eller wget -qO- til HTTP-tjenester; for dæmoner, der ikke bruger HTTP, kan du undersøge en socket med /dev/tcp/localhost/PORT.
  • Installér kun det, du har brug for: undgå i distroless-images at tilføje curl kun til sundhedstjek — brug i stedet en specialbygget probe-binærfil eller applikationens egen sundheds-binærfil.
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

Sådan samles det hele: Et startskript til produktion

Det følgende er et komplet startskript i produktionskvalitet, der kombinerer alle mønstrene fra denne lektion: validering af miljøet, konfigurationsskabeloner, signalviderestilling og overdragelse med exec. Dette mønster bruges i virkelige Node.js-, Python- og Go-mikrotjenester, der er implementeret på Kubernetes.

  • Hvert afsnit er kommenteret, så det fungerer som en selvforklarende skabelon.
  • Startskriptet holdes under 50 linjer — startskripter bør være enkle og nemme at kontrollere.
  • Bemærk det afsluttende exec "$@": Når al klargøring er udført, erstattes shellen af applikationsprocessen, så den bliver PID 1 og modtager alle operativsystemets signaler direkte.
#!/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 "$@"

Videnstjek: Signalhåndtering i startskripter

Test din forståelse af signalhåndtering i startskripter til containere.

Opsummering: Slanke Dockerfiler og shell-startskripter

Du har gennemgået hele stakken af forfatterskab til produktionscontainere:

  • Bygninger i flere trin bruger flere FROM-instruktioner til at holde compilere og byggeværktøjer ude af det endelige image, hvilket giver slanke, minimale runtime-lag.
  • Lagrækkefølge — kopiér afhængighedsmanifester før kildekoden — maksimerer cache-træffene og gør CI-forløb hurtigere.
  • BuildKit-hemmeligheder og SSH-monteringer holder legitimationsoplysninger ude af imagehistorikken uden at ødelægge godkendte bygninger.
  • Startskriptlag validerer miljøvariabler, udfylder konfigurationsfiler med envsubst og overdrager kontrollen til applikationen via exec "$@".
  • Signalhåndtering kræver enten exec (så appen er PID 1) eller et eksplicit mønster med trap + kill + wait, når baggrundsjob bruges.
  • tini tilføjer indsamling af zombieprocesser og korrekt signalviderestilling, når containeren opretter flere processer.
  • Ikke-root-brugere og HEALTHCHECK-instruktioner fuldender et sikkert og observerbart image, der er klar til Kubernetes-arbejdsbelastninger i produktion.

Kombinér disse mønstre konsekvent, så bliver dine images mindre, hurtigere at implementere og betydeligt mere robuste under produktionsforhold.

Gratis at komme i gang

Lær Bash med en AI-underviser — gratis

Skriv og kør rigtig kode i din browser, få øjeblikkelig hjælp fra en AI-underviser døgnet rundt, og fortsæt, hvor du slap, på web eller i appen.

Kurser
22
Lektioner
88

Ofte stillede spørgsmål

Er lektionen “Skriv slanke Dockerfiles og shell-entrypoints” gratis?

Ja — hele teksten til “Skriv slanke Dockerfiles og shell-entrypoints” kan læses gratis her på nettet. Hvis du vil øve dig interaktivt med en indbygget kodeeditor og en AI-vejleder døgnet rundt og få adgang til resten af Linux-kommandolinjen og Bash-scripting på ekspertniveau-kurset, skal du opgradere til CoddyKit PRO. Linux-kommandolinjen og Bash-scripting på ekspertniveau-kurset indeholder 4 lektioner i alt.

Hvad lærer jeg i “Skriv slanke Dockerfiles og shell-entrypoints”?

Skriv multi-stage-buildscripts og robuste entrypoint-shims med signalhåndtering og skabelonbaseret konfiguration. Du øver dig i Linux-kommandolinjen og Bash-scripting på ekspertniveau med praktisk kode, som du kører direkte i browseren, og en AI-vejleder døgnet rundt besvarer dine spørgsmål, mens du arbejder dig gennem lektionen.

Skal jeg have erfaring for at begynde på Linux-kommandolinjen og Bash-scripting på ekspertniveau?

Der kræves ingen tidligere erfaring. Linux-kommandolinjen og Bash-scripting på ekspertniveau på CoddyKit er tilrettelagt for både begyndere og øvede, så du kan starte her eller fra begyndelsen og lære i dit eget tempo. Dette er lektion 1 af 4.

Hvor lang tid tager lektionen “Skriv slanke Dockerfiles og shell-entrypoints”?

De fleste CoddyKit-lektioner tager cirka 5–10 minutter. Hver lektion er kort og interaktiv, så du gør løbende fremskridt og kan fortsætte, hvor du slap – på både web og app.

Kan jeg skrive og køre kode i denne Linux-kommandolinjen og Bash-scripting på ekspertniveau-lektion?

Ja. Alle Linux-kommandolinjen og Bash-scripting på ekspertniveau-lektioner har en indbygget kodeeditor, så du kan skrive og køre rigtig kode direkte i din browser og få øjeblikkelig feedback fra AI – uden lokal opsætning.

Alle lektioner i dette kursus

  1. Skriv slanke Dockerfiles og shell-entrypoints
  2. Skabelondannelse af konfigurationer med envsubst og heredocs
  3. Scripting af cloudressourcer via CLI og jq
  4. Health probes, readiness gates og venteløkker
← Tilbage til Linux-kommandolinjen og Bash-scripting på ekspertniveau