Mestring av Linux-kommandolinjen og Bash-skripting · leksjon

Slanke Dockerfiles og shell-entrypoint-er

Skriv flertrinns byggeskript og robuste entrypoint-shim-er med signalhåndtering og konfigurasjonsmaler.

Leksjon 1 av 413 trinn

Slanke Dockerfiles og shell-entrypoint-er er en gratis leksjon i Mestring av Linux-kommandolinjen og Bash-skripting på CoddyKit. Dette er leksjon 1 av 4. Du kan lese hele leksjonen gratis nedenfor – og deretter øve praktisk i nettleseren med en innebygd kodeeditor og en AI-veileder som er tilgjengelig døgnet rundt. Den er en del av læringsløpet i Mestring av Linux-kommandolinjen og Bash-skripting, og fremdriften din synkroniseres mellom nettet og CoddyKit-appen. Kurset i Mestring av Linux-kommandolinjen og Bash-skripting inneholder totalt 4 leksjoner.

Hvorfor slanke Dockerfiles er viktige for DevOps

I produksjonsmiljøer har hver megabyte i et Docker-image en kostnad: tregere nedlastinger, større angrepsflater og bortkastet lagringsplass i registeret. Slanke Dockerfiles kombinert med robuste shell-entrypoints er kjennetegn på moden DevOps-praksis.

  • Bygg med flere stadier skiller verktøy som trengs under byggingen, fra det endelige runtime-imaget, og reduserer størrelsen betydelig.
  • Entrypoint-shimskript er små shell-skript som klargjør containere: de lager konfigurasjonsfiler fra maler, validerer miljøvariabler, håndterer signaler og kjører til slutt hovedprosessen med exec.
  • Tilsammen utgjør de ryggraden i pålitelige og portable container-arbeidslaster i Kubernetes-, ECS- og bare-metal-miljøer.

Denne leksjonen dekker begge disiplinene fra ende til annen, med produksjonsklare mønstre som De kan legge direkte inn i pipelineene Deres.

Anatomien til en Dockerfile med flere byggestadier

En Dockerfile med flere byggestadier bruker flere FROM-instruksjoner. Hvert stadium er et isolert sett med lag; De kopierer bare artefaktene De trenger, til neste stadium.

  • Stadium 0 (builder): installerer kompilatorer, testkjørere og byggavhengigheter.
  • Stadium 1 (runtime): starter med et minimalt basisimage (for eksempel alpine, distroless) og kopierer bare kompilerte binærfiler eller app-pakker.
  • Det endelige imaget inneholder aldri gcc, make eller kildekode med mindre De eksplisitt kopierer dem.

Bruk --from=<stage> i COPY for å hente filer på tvers av grensene mellom stadiene. Gi stadiene navn med AS <name> for bedre lesbarhet og målrettet bygging 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 av lag og ugyldiggjøring av hurtigbuffer

Hver RUN-, COPY- og ADD-instruksjon oppretter et nytt lag. Instruksjoner i feil rekkefølge ugyldiggjør bygg-hurtigbufferet unødvendig, noe som gjør CI-pipelineene trege.

  • Kopier avhengighetsmanifestene (package.json, go.mod, requirements.txt) før De kopierer kildekoden, slik at installasjon av avhengigheter hurtigbufres uavhengig.
  • Kjed relaterte kommandoer med &&, og rydd opp i det samme RUN-laget for å unngå å etterlate pakkehurtigbuffer i mellomliggende lag.
  • Bruk --no-cache i pakkebehandlere, og fjern listfiler etter installasjonen.
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-hemmeligheter og SSH-videresending

Private registre, SSH-nøkler og API-tokener må aldri forekomme i imagelag. Docker BuildKit tilbyr to sikre mekanismer:

  • --secret: monterer en hemmelighetsfil i ett enkelt RUN-trinn uten å bygge den inn i et lag. Få tilgang via /run/secrets/<id>.
  • --ssh: videresender SSH-agentens socket fra verten til byggingen, slik at git clone kan autentisere uten å bygge inn private nøkler.

Aktiver BuildKit med DOCKER_BUILDKIT=1 eller via docker buildx build. Direktivet # syntax=docker/dockerfile:1 låser opp disse funksjonene.

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

Slik skriver De et robust entrypoint-shimskript

Et entrypoint-shimskript er et shell-skript som angis som ENTRYPOINT i Dockerfile. Oppgaven er å klargjøre runtime-miljøet før kontrollen overlates til hovedprosessen.

Et godt strukturert shimskript følger denne rekkefølgen:

  • Trinn 1: Angi set -euo pipefail slik at enhver feil avbryter tidlig.
  • Trinn 2: Valider nødvendige miljøvariabler, og avslutt umiddelbart med en nyttig melding ved feil.
  • Trinn 3: Lag konfigurasjonsfiler fra miljøvariabler ved hjelp av maler.
  • Trinn 4: Registrer signalbehandlere for kontrollert avslutning.
  • Trinn 5: exec "$@" — erstatt skallet med hovedprosessen, slik at PID 1 er applikasjonen og ikke shimskriptet.

Det avsluttende exec-kallet er avgjørende: uten det blir ikke signaler som sendes av Kubernetes eller Docker-runtime-miljøet, videresendt til barneprosessen.

#!/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 "$@"

Konfigurasjonsmaler med envsubst

envsubst (fra GNU-pakken gettext, tilgjengelig i Alpine som gettext) erstatter ${VAR}-plassholdere i en malfil med de gjeldende verdiene til miljøvariablene.

  • Legg en *.tmpl-konfigurasjonsmal i imaget; entrypoint-skriptet gjengir den ved oppstart.
  • Angi variabellisten eksplisitt til envsubst, slik at den ikke utilsiktet utvider andre dollartegn (for eksempel i nginx-regulære uttrykk).
  • Skriv den gjengitte filen til en skrivbar bane som /tmp eller et eget konfigurasjonsvolum.
#!/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 kontrollert avslutning

Containere mottar SIGTERM når de stoppes av Kubernetes, ECS eller docker stop. Hvis entrypoint-shimskriptet er PID 1 og ikke videresender signaler, blir hovedprosessen drept med SIGKILL etter fristen for kontrollert avslutning — noe som kan føre til tapte forespørsler eller korrupte data.

  • Bruk trap for å fange SIGTERM og SIGINT i shimskriptet.
  • Videresend signalet til barneprosessen ved hjelp av kill -TERM "$child".
  • Bruk wait "$child" for å vente til barneprosessen avsluttes, og viderefør deretter avslutningskoden.
  • Alternativt kan De bruke exec til å erstatte skallet fullstendig — da leverer operativsystemet signalene direkte til barneprosessen, og De trenger ingen trap. Dette er det foretrukne mønsteret i enkle tilfeller.
#!/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 $?

Bruk av tini som en minimal init-prosess

Når containeren Deres oppretter barneprosesser (for eksempel et shell som forgrener worker-prosesser), trenger De en ekte init-prosess for å ta hånd om zombieprosesser. tini er en liten init-binærfil laget spesielt for containere.

  • Legg tini til i imaget, og angi den som entrypoint-wrapper.
  • Den tar hånd om zombieprosesser, videresender signaler korrekt og avslutter med statuskoden til barneprosessen.
  • Docker leveres med en innebygd tini som aktiveres med docker run --init, men ved å bygge den inn i imaget sikrer De konsistent oppførsel på tvers av 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"]

Kjør som en bruker uten root-privilegier

Containere som kjører som root (UID 0), utgjør en betydelig sikkerhetsrisiko: Et container-escape gir full tilgang til verten. Gå alltid over til en upriviligert bruker før hovedprosessen kjøres.

  • Opprett en egen systembruker og -gruppe i Dockerfile med addgroup / adduser (Alpine) eller groupadd / useradd (Debian).
  • Endre eier av appfilene med COPY --chown=appuser:appgroup — dette er mer effektivt enn et separat RUN chown-lag.
  • Bytt til brukeren med USER-instruksjonen. Entrypoint-skriptet og CMD arver denne brukeren.
  • Kubernetes securityContext.runAsNonRoot: true vil nekte å starte et image som fortsatt kjø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"]

Helsesjekker og readiness-prober i imaget

Kubernetes' liveness- og readiness-prober defineres i manifestene, men De kan også legge inn en HEALTHCHECK i Dockerfile for frittstående docker run- og Docker Compose-miljøer.

  • HEALTHCHECK --interval=10s --timeout=3s --start-period=15s --retries=3 CMD ...
  • Bruk curl --fail eller wget -qO- for HTTP-tjenester. For daemoner som ikke bruker HTTP, kan De kontrollere en socket med /dev/tcp/localhost/PORT.
  • Installer bare det De trenger: I distroless-images bør De unngå å legge til curl bare for helsesjekker — bruk en spesiallaget probe-binærfil eller applikasjonens egen helsesjekk-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

Slik settes alt sammen: Et produksjonsklart entrypoint

Det følgende er et komplett, produksjonsklart entrypoint-shimskript som kombinerer alle mønstrene fra denne leksjonen: validering av miljøvariabler, konfigurasjonsmaler, signalvideresending og overlevering med exec. Dette mønsteret brukes i Node.js-, Python- og Go-mikrotjenester i produksjon, distribuert på Kubernetes.

  • Hver del er kommentert og fungerer som en selvforklarende mal.
  • Shimskriptet holdes under 50 linjer — entrypoint-skript bør være enkle og lette å kontrollere.
  • Legg merke til det avsluttende exec "$@": Når all klargjøring er ferdig, erstattes skallet av applikasjonsprosessen, slik at den blir PID 1 og mottar 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 "$@"

Kunnskapssjekk: Signalhåndtering i entrypoint-skript

Test forståelsen Deres av signalhåndtering i containerens entrypoint-skript.

Oppsummering: Slanke Dockerfiles og shell-entrypoint-skript

De har gått gjennom hele stakken for utvikling av produksjonsklare containere:

  • Bygg med flere stadier bruker flere FROM-instruksjoner for å holde kompilatorer og byggverktøy ute av det endelige imaget, slik at det produseres slanke og minimale runtime-lag.
  • Rekkefølge på lag — kopier avhengighetsmanifestene før kildekoden — maksimerer treff i hurtigbufferet og gjør CI-pipelineene raskere.
  • BuildKit-hemmeligheter og SSH-monteringer holder legitimasjonsopplysninger ute av imagehistorikken uten å hindre autentiserte bygg.
  • Entrypoint-shimskript validerer miljøvariabler, lager konfigurasjonsfiler fra maler med envsubst og overlater kontrollen til applikasjonen via exec "$@".
  • Signalhåndtering krever enten exec (slik at appen er PID 1) eller et eksplisitt trap + kill + wait-mønster når bakgrunnsjobber brukes.
  • tini sørger for håndtering av zombieprosesser og korrekt signalvideresending når containeren oppretter flere prosesser.
  • Brukere uten root-privilegier og HEALTHCHECK-instruksjoner fullfører et sikkert og observerbart image som er klart for produksjonsarbeidslaster i Kubernetes.

Kombiner disse mønstrene konsekvent, så blir imagene Deres mindre, raskere å distribuere og betydelig mer robuste under produksjonsforhold.

Gratis å komme i gang

Lær deg Bash med en AI-veileder – gratis

Skriv og kjør ekte kode i nettleseren, få umiddelbar hjelp fra en AI-veileder som er tilgjengelig døgnet rundt, og fortsett der du slapp – på nettet eller i appen.

Kurs
22
Leksjoner
88

Ofte stilte spørsmål

Er leksjonen «Slanke Dockerfiles og shell-entrypoint-er» gratis?

Ja – hele teksten i «Slanke Dockerfiles og shell-entrypoint-er» er gratis å lese her på nettet. For å øve interaktivt med en innebygd kodeeditor og en AI-veileder som er tilgjengelig døgnet rundt, og for å låse opp resten av Mestring av Linux-kommandolinjen og Bash-skripting-kurset, kan du oppgradere til CoddyKit PRO. Kurset i Mestring av Linux-kommandolinjen og Bash-skripting inneholder totalt 4 leksjoner.

Hva lærer jeg i «Slanke Dockerfiles og shell-entrypoint-er»?

Skriv flertrinns byggeskript og robuste entrypoint-shim-er med signalhåndtering og konfigurasjonsmaler. Du øver på Mestring av Linux-kommandolinjen og Bash-skripting med praktisk kode som du kjører direkte i nettleseren, mens en AI-veileder som er tilgjengelig døgnet rundt, svarer på spørsmålene dine mens du jobber deg gjennom leksjonen.

Trenger jeg erfaring for å begynne med Mestring av Linux-kommandolinjen og Bash-skripting?

Ingen tidligere erfaring er nødvendig. Mestring av Linux-kommandolinjen og Bash-skripting på CoddyKit er lagt opp for både nybegynnere og viderekomne, så De kan begynne her eller helt fra start og lære i Deres eget tempo. Dette er leksjon 1 av 4.

Hvor lang tid tar leksjonen «Slanke Dockerfiles og shell-entrypoint-er»?

De fleste CoddyKit-leksjoner tar omtrent 5–10 minutter. Hver leksjon er kort og interaktiv, slik at De gjør jevne fremskritt og kan fortsette akkurat der De slapp – både på nettet og i appen.

Kan jeg skrive og kjøre kode i denne Mestring av Linux-kommandolinjen og Bash-skripting-leksjonen?

Ja. Alle Mestring av Linux-kommandolinjen og Bash-skripting-leksjoner har en innebygd kodeeditor, slik at De kan skrive og kjøre ekte kode direkte i nettleseren og få umiddelbar tilbakemelding fra AI – uten lokal konfigurering.

Alle leksjonene i dette kurset

  1. Slanke Dockerfiles og shell-entrypoint-er
  2. Maler for konfigurasjoner med envsubst og heredoc-er
  3. Skripting av skyressurser med CLI og jq
  4. Helsetester, readiness-sperrer og venteløkker
← Tilbake til Mestring av Linux-kommandolinjen og Bash-skripting