Scrivere Dockerfile essenziali ed entrypoint shell
Scriva script di build multistadio e shim entrypoint robusti, con gestione dei segnali e templating della configurazione.
Scrivere Dockerfile essenziali ed entrypoint shell è una lezione Linux Command Line & Bash Scripting Mastery gratuita su CoddyKit. Questa è la lezione 1 di 4. Puoi leggere la lezione completa qui gratuitamente — poi esercitati direttamente nel browser con un editor di codice integrato e un tutor IA disponibile 24/7. Fa parte del percorso di apprendimento Linux Command Line & Bash Scripting Mastery, e i tuoi progressi si sincronizzano tra il web e l'app CoddyKit. Il corso Linux Command Line & Bash Scripting Mastery include 4 lezioni in totale.
Perché i Dockerfile snelli sono importanti per il DevOps
Negli ambienti di produzione, ogni megabyte contenuto in un'immagine Docker ha un costo: download più lenti, superfici di attacco più ampie e spazio sprecato nel registry. Dockerfile snelli, combinati con entrypoint shell robusti, sono un segno distintivo di una pratica DevOps matura.
- Le build multi-stage separano gli strumenti necessari durante la build dall'immagine runtime finale, riducendone drasticamente le dimensioni.
- Gli shim di entrypoint sono piccoli script shell che inizializzano i container: generano i file di configurazione dai template, convalidano le variabili d'ambiente, gestiscono i segnali e infine eseguono il processo principale con exec.
- Insieme costituiscono la base per workload containerizzati affidabili e portabili in Kubernetes, ECS e negli ambienti bare metal.
Questa lezione tratta entrambe le discipline dall'inizio alla fine, con pattern adatti alla produzione che può inserire direttamente nelle pipeline.
Anatomia di un Dockerfile multi-stage
Un Dockerfile multi-stage utilizza più istruzioni FROM. Ogni stage è un insieme isolato di layer; nel passaggio allo stage successivo vengono copiati solo gli artifact necessari.
- Stage 0 (builder): installa compilatori, test runner e dipendenze di build.
- Stage 1 (runtime): parte da una base minimale (ad es.
alpine,distroless) e copia solo i binari compilati o i bundle dell'applicazione. - L'immagine finale non contiene mai
gcc,makeo il codice sorgente, a meno che non vengano copiati esplicitamente.
Utilizzi --from=<stage> in COPY per trasferire file oltre i confini tra gli stage. Assegni un nome agli stage con AS <name> per migliorare la leggibilità e selezionare uno stage specifico con 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"]Ridurre i layer e invalidare la cache
Ogni istruzione RUN, COPY e ADD crea un nuovo layer. Un ordine inadeguato delle istruzioni invalida inutilmente la cache di build, rallentando le pipeline CI.
- Copi i manifest delle dipendenze (
package.json,go.mod,requirements.txt) prima di copiare il codice sorgente, così l'installazione delle dipendenze viene memorizzata nella cache in modo indipendente. - Concateni i comandi correlati con
&&ed esegua la pulizia nello stesso layerRUNper evitare di lasciare la cache dei pacchetti nei layer intermedi. - Utilizzi
--no-cachenei package manager e rimuova i file degli indici dopo l'installazione.
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"]Secret BuildKit e forwarding SSH
Registry privati, chiavi SSH e token API non devono mai comparire nei layer dell'immagine. Docker BuildKit offre due meccanismi sicuri:
--secret: monta un file contenente un secret all'interno di un singolo passaggioRUN, senza incorporarlo in un layer. È possibile accedervi tramite/run/secrets/<id>.--ssh: inoltra il socket dell'agent SSH dell'host nella build, consentendo agit clonedi autenticarsi senza incorporare chiavi private.
Abiliti BuildKit con DOCKER_BUILDKIT=1 oppure tramite docker buildx build. La direttiva # syntax=docker/dockerfile:1 abilita queste funzionalità.
# 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 .Scrivere uno shim di entrypoint robusto
Lo shim di entrypoint è uno script shell impostato come ENTRYPOINT nel Dockerfile. Il suo compito è preparare l'ambiente runtime prima di cedere il controllo al processo principale.
Uno shim ben strutturato segue questo ordine:
- Passaggio 1: imposti
set -euo pipefailin modo che qualsiasi errore interrompa subito l'esecuzione. - Passaggio 2: convalidi le variabili d'ambiente obbligatorie e interrompa subito l'esecuzione con un messaggio utile in caso di errore.
- Passaggio 3: generi i file di configurazione a partire dalle variabili d'ambiente.
- Passaggio 4: registri gli handler dei segnali per un arresto ordinato.
- Passaggio 5:
exec "$@"— sostituisca la shell con il processo principale, così il PID 1 è l'applicazione e non lo shim.
Il comando exec finale è fondamentale: senza di esso, i segnali inviati da Kubernetes o dal runtime Docker non vengono inoltrati al processo figlio.
#!/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 "$@"Generare configurazioni dai template con envsubst
envsubst (fornito dal pacchetto GNU gettext, presente in Alpine come gettext) sostituisce i segnaposto ${VAR} in un file template con i valori correnti delle variabili d'ambiente.
- Inserisca nell'immagine un template di configurazione
*.tmpl; l'entrypoint lo renderizza all'avvio. - Passi esplicitamente a
envsubstl'elenco delle variabili, così non espanderà accidentalmente altri simboli del dollaro (ad es. nelle regex di nginx). - Scriva il file renderizzato in un percorso scrivibile come
/tmpo in un volume di configurazione dedicato.
#!/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 "$@"Gestione dei segnali e arresto ordinato
I container ricevono SIGTERM quando vengono arrestati da Kubernetes, ECS o docker stop. Se lo shim di entrypoint è il PID 1 e non inoltra i segnali, il processo principale viene terminato con SIGKILL al termine del periodo di tolleranza, causando richieste perse o corruzione dei dati.
- Utilizzi
trapper intercettareSIGTERMeSIGINTnello shim. - Inoltri il segnale al PID figlio utilizzando
kill -TERM "$child". - Utilizzi
wait "$child"per attendere la terminazione del processo figlio, quindi propaghi il relativo codice di uscita. - In alternativa, utilizzi
execper sostituire completamente la shell: in questo modo il sistema operativo consegna i segnali direttamente al processo figlio e non serve alcun trap. Questo è il pattern preferito nei casi semplici.
#!/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 $?Utilizzare tini come processo init minimale
Quando il container genera processi figli (ad es. una shell che crea processi worker), è necessario un vero init che raccolga i processi zombie. tini è un binario init minimale progettato appositamente per i container.
- Aggiunga
tiniall'immagine e lo imposti come wrapper dell'entrypoint. - Raccoglie i processi figli zombie, inoltra correttamente i segnali e termina con il codice di uscita del processo figlio.
- Docker include un tini integrato, attivabile con
docker run --init, ma incorporarlo nell'immagine garantisce un comportamento coerente tra i diversi runtime (Kubernetes, ECS ecc.).
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"]Eseguire il processo come utente non root
I container eseguiti come root (UID 0) rappresentano un grave rischio per la sicurezza: una fuga dal container concede l'accesso completo all'host. Passi sempre a un utente senza privilegi prima di eseguire il processo principale.
- Crei un utente e un gruppo di sistema dedicati nel Dockerfile con
addgroup/adduser(Alpine) oppuregroupadd/useradd(Debian). - Modifichi il proprietario dei file dell'applicazione con
COPY --chown=appuser:appgroup, più efficiente rispetto a un layerRUN chownseparato. - Passi all'utente con l'istruzione
USER. L'entrypoint e CMD ereditano questo utente. securityContext.runAsNonRoot: truedi Kubernetes rifiuterà di avviare un'immagine che continua a essere eseguita come 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"]Health check e probe di readiness nell'immagine
Le probe di liveness e readiness di Kubernetes sono definite nei manifest, ma è anche possibile includere un HEALTHCHECK nel Dockerfile per gli ambienti standalone con docker run e Docker Compose.
HEALTHCHECK --interval=10s --timeout=3s --start-period=15s --retries=3 CMD ...- Utilizzi
curl --failowget -qO-per i servizi HTTP; per i demoni non HTTP, esegua il probe su un socket con/dev/tcp/localhost/PORT. - Installi solo ciò che serve: nelle immagini distroless eviti di aggiungere
curlsolo per i health check; utilizzi invece un binario di probe dedicato o il binario di health check dell'applicazione.
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 80Riunire tutti gli elementi: un entrypoint per la produzione
Di seguito è riportato uno shim di entrypoint completo e adatto alla produzione, che combina tutti i pattern di questa lezione: convalida delle variabili d'ambiente, generazione della configurazione dai template, inoltro dei segnali e passaggio del controllo con exec. Questo pattern viene utilizzato in microservizi Node.js, Python e Go reali distribuiti su Kubernetes.
- Ogni sezione è commentata per fungere da template autoesplicativo.
- Lo shim è mantenuto al di sotto delle 50 righe: gli entrypoint dovrebbero essere semplici e facilmente verificabili.
- Noti il comando
exec "$@"finale: completata tutta la configurazione, la shell viene sostituita dal processo dell'applicazione, che diventa il PID 1 e riceve direttamente tutti i segnali del sistema operativo.
#!/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 "$@"Verifica delle conoscenze: gestione dei segnali negli entrypoint
Verifichi la propria comprensione della gestione dei segnali negli script di entrypoint dei container.
Riepilogo: Dockerfile snelli ed entrypoint shell
Ha esaminato tutti gli aspetti della creazione di container per la produzione:
- Le build multi-stage utilizzano più istruzioni
FROMper tenere compilatori e strumenti di build fuori dall'immagine finale, producendo layer runtime snelli e minimali. - L'ordine dei layer — copiare i manifest delle dipendenze prima del codice sorgente — massimizza i riscontri nella cache e accelera le pipeline CI.
- I secret e i mount SSH di BuildKit mantengono le credenziali fuori dalla cronologia dell'immagine senza compromettere le build autenticate.
- Gli shim di entrypoint convalidano le variabili d'ambiente, generano i file di configurazione con
envsubste cedono il controllo all'applicazione tramiteexec "$@". - La gestione dei segnali richiede
exec(in modo che l'applicazione sia il PID 1) oppure un pattern esplicitotrap+kill+waitquando vengono utilizzati job in background. - tini aggiunge la raccolta dei processi zombie e il corretto inoltro dei segnali quando il container genera più processi.
- Gli utenti non root e le istruzioni
HEALTHCHECKcompletano un'immagine sicura e osservabile, pronta per i workload di produzione su Kubernetes.
Combinando questi pattern in modo coerente, le immagini saranno più piccole, più rapide da distribuire e molto più robuste in condizioni di produzione.
Domande Frequenti
La lezione «Scrivere Dockerfile essenziali ed entrypoint shell» è gratuita?
Sì — il testo completo di «Scrivere Dockerfile essenziali ed entrypoint shell» è gratuito qui sul web. Per esercitarvi in modo interattivo (un editor di codice integrato e un tutor IA 24/7) e sbloccare il resto del corso Linux Command Line & Bash Scripting Mastery, passa a CoddyKit PRO. Il corso Linux Command Line & Bash Scripting Mastery include 4 lezioni in totale.
Cosa imparerò in «Scrivere Dockerfile essenziali ed entrypoint shell»?
Scriva script di build multistadio e shim entrypoint robusti, con gestione dei segnali e templating della configurazione. Eserciti Linux Command Line & Bash Scripting Mastery con codice pratico che esegui direttamente nel browser, e un tutor IA 24/7 risponde alle tue domande mentre lavori sulla lezione.
Ho bisogno di esperienza per iniziare Linux Command Line & Bash Scripting Mastery?
Non è richiesta alcuna esperienza precedente. Linux Command Line & Bash Scripting Mastery su CoddyKit è strutturato per principianti e studenti avanzati, quindi puoi iniziare da qui o dall'inizio e procedere al tuo ritmo. Questa è la lezione 1 di 4.
Quanto tempo richiede la lezione «Scrivere Dockerfile essenziali ed entrypoint shell»?
La maggior parte delle lezioni CoddyKit richiede circa 5–10 minuti. Ogni lezione è breve e interattiva, quindi fai progressi costanti e riprendi esattamente da dove hai lasciato su web e app.
Posso scrivere ed eseguire codice in questa lezione Linux Command Line & Bash Scripting Mastery?
Sì. Ogni lezione Linux Command Line & Bash Scripting Mastery include un editor di codice integrato, quindi scrivi ed esegui codice reale direttamente nel tuo browser e ricevi feedback istantaneo dall'IA — nessuna configurazione locale necessaria.
Tutte le lezioni di questo corso
- Scrivere Dockerfile essenziali ed entrypoint shell
- Creare template di configurazione con envsubst e heredoc
- Gestire risorse cloud tramite CLI e jq
- Probe di salute, controlli di readiness e cicli di attesa