Dockerfile
Chapter objectivesβ
- Understand the Dockerfile syntax
- Master the main instructions
- Optimize your images
- Apply best practices
1 - What is a Dockerfile?β
Definitionβ
A Dockerfile is a text file containing the instructions to automatically build a Docker image.
# Exemple simple
FROM node:18-alpine
WORKDIR /app
COPY package*.json ./
RUN npm install
COPY . .
EXPOSE 3000
CMD ["node", "server.js"]
Build processβ
βββββββββββββββββββββββββββββββββββββββββββββββββββββ
β Dockerfile β
βββββββββββββββββββββββ¬ββββββββββββββββββββββββββββββ
β docker build
βΌ
βββββββββββββββββββββββββββββββββββββββββββββββββββββ
β Docker Engine β
β β
β FROM β RUN β COPY β ... β CMD β
β β β β β β
β βΌ βΌ βΌ βΌ β
β Layer Layer Layer Layer β
βββββββββββββββββββββββ¬ββββββββββββββββββββββββββββββ
β
βΌ
βββββββββββββββββββββββββββββββββββββββββββββββββββββ
β Image Docker β
βββββββββββββββββββββββββββββββββββββββββββββββββββββ
2 - Basic instructionsβ
FROM - Base imageβ
# Image officielle
FROM ubuntu:22.04
# Image Alpine (légère)
FROM node:18-alpine
# Image scratch (vide)
FROM scratch
# Multi-stage : nommer l'Γ©tape
FROM golang:1.21 AS builder
WORKDIR - Working directoryβ
# DΓ©finir le rΓ©pertoire de travail
WORKDIR /app
# Tous les chemins relatifs seront basΓ©s sur /app
COPY . . # Copie dans /app
RUN ls # ExΓ©cutΓ© dans /app
COPY and ADDβ
# COPY - Copier des fichiers
COPY source destination
COPY package.json /app/
COPY . /app/
COPY --chown=user:group files /app/
# ADD - Copier avec fonctionnalitΓ©s supplΓ©mentaires
ADD archive.tar.gz /app/ # Extrait automatiquement
ADD https://example.com/file /app/ # TΓ©lΓ©charge depuis URL
Best practice
Prefer COPY over ADD unless you need automatic extraction.
RUN - Run commandsβ
# Format shell
RUN apt-get update && apt-get install -y nginx
# Format exec (recommandΓ©)
RUN ["apt-get", "update"]
# Commandes multiples (optimisΓ©)
RUN apt-get update && \
apt-get install -y \
nginx \
curl \
vim && \
rm -rf /var/lib/apt/lists/*
3 - Execution instructionsβ
CMD - Default commandβ
# Format exec (recommandΓ©)
CMD ["node", "server.js"]
# Format shell
CMD node server.js
# Paramètres pour ENTRYPOINT
CMD ["--help"]
ENTRYPOINT - Entry pointβ
# DΓ©finit l'exΓ©cutable principal
ENTRYPOINT ["node"]
CMD ["server.js"]
# docker run mon-image β node server.js
# docker run mon-image app.js β node app.js
CMD vs ENTRYPOINT differenceβ
| Aspect | CMD | ENTRYPOINT |
|---|---|---|
| Overridable | Yes, easily | No (except --entrypoint) |
| Usage | Default parameters | Main executable |
| Combination | Can be arguments for ENTRYPOINT | Receives CMD as arguments |
# Exemple combinΓ©
ENTRYPOINT ["python"]
CMD ["app.py"]
# docker run mon-image β python app.py
# docker run mon-image test.py β python test.py
4 - Configurationβ
ENV - Environment variablesβ
# DΓ©finir des variables
ENV NODE_ENV=production
ENV APP_HOME=/app \
APP_PORT=3000
# Utiliser les variables
WORKDIR $APP_HOME
EXPOSE $APP_PORT
ARG - Build argumentsβ
# Argument avec valeur par dΓ©faut
ARG NODE_VERSION=18
# Utilisation
FROM node:${NODE_VERSION}-alpine
# Argument obligatoire
ARG API_KEY
# Passer des arguments au build
docker build --build-arg NODE_VERSION=20 .
docker build --build-arg API_KEY=secret .
EXPOSE - Document the portsβ
# Documenter les ports exposΓ©s
EXPOSE 80
EXPOSE 443
EXPOSE 3000/tcp
EXPOSE 5000/udp
Important
EXPOSE is informational. You always need -p at runtime to publish the ports.
5 - Advanced instructionsβ
USER - Change userβ
# CrΓ©er et utiliser un utilisateur non-root
RUN addgroup -S appgroup && adduser -S appuser -G appgroup
USER appuser
# Revenir Γ root si nΓ©cessaire
USER root
VOLUME - Mount pointsβ
# DΓ©clarer un volume
VOLUME /data
VOLUME ["/data", "/logs"]
HEALTHCHECK - Health checkβ
# VΓ©rifier que l'application rΓ©pond
HEALTHCHECK --interval=30s --timeout=10s --retries=3 \
CMD curl -f http://localhost:3000/health || exit 1
# DΓ©sactiver le healthcheck
HEALTHCHECK NONE
LABEL - Metadataβ
LABEL maintainer="[email protected]"
LABEL version="1.0"
LABEL description="Mon application web"
# Format OCI
LABEL org.opencontainers.image.source="https://github.com/user/repo"
6 - Multi-stage buildsβ
Conceptβ
Multi-stage builds make it possible to create lightweight images by separating the build from the execution.
# Γtape 1: Build
FROM node:18 AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build
# Γtape 2: Production
FROM node:18-alpine AS production
WORKDIR /app
COPY --from=builder /app/dist ./dist
COPY --from=builder /app/node_modules ./node_modules
EXPOSE 3000
CMD ["node", "dist/index.js"]
Go exampleβ
# Build stage
FROM golang:1.21 AS builder
WORKDIR /app
COPY go.mod go.sum ./
RUN go mod download
COPY . .
RUN CGO_ENABLED=0 GOOS=linux go build -o main .
# Production stage
FROM scratch
COPY --from=builder /app/main /main
ENTRYPOINT ["/main"]
Benefitsβ
| Without multi-stage | With multi-stage |
|---|---|
| 1.2 GB image | 50 MB image |
| Build tools included | Only the executable |
| Large attack surface | Minimal surface |
7 - Build an imageβ
The docker build commandβ
# Build simple
docker build .
# Avec tag
docker build -t mon-app:v1 .
# Depuis un Dockerfile spΓ©cifique
docker build -f Dockerfile.prod -t mon-app:prod .
# Avec arguments
docker build --build-arg VERSION=1.0 -t mon-app:v1 .
# Sans cache
docker build --no-cache -t mon-app:v1 .
# Afficher la sortie des RUN
docker build --progress=plain -t mon-app:v1 .
The .dockerignore fileβ
# .dockerignore
node_modules
npm-debug.log
.git
.gitignore
.env
.env.local
Dockerfile*
docker-compose*
README.md
.DS_Store
*.log
coverage
.nyc_output
8 - Complete examplesβ
Node.js applicationβ
FROM node:18-alpine
# CrΓ©er un utilisateur non-root
RUN addgroup -S nodejs && adduser -S nodejs -G nodejs
WORKDIR /app
# Copier les fichiers de dΓ©pendances
COPY package*.json ./
# Installer les dΓ©pendances
RUN npm ci --only=production
# Copier le code source
COPY --chown=nodejs:nodejs . .
# Changer d'utilisateur
USER nodejs
# Exposer le port
EXPOSE 3000
# Healthcheck
HEALTHCHECK --interval=30s --timeout=10s --retries=3 \
CMD wget --no-verbose --tries=1 --spider http://localhost:3000/health || exit 1
# Commande de dΓ©marrage
CMD ["node", "server.js"]
Python applicationβ
FROM python:3.11-slim
# Variables d'environnement
ENV PYTHONDONTWRITEBYTECODE=1 \
PYTHONUNBUFFERED=1
WORKDIR /app
# Installer les dépendances système
RUN apt-get update && \
apt-get install -y --no-install-recommends gcc && \
rm -rf /var/lib/apt/lists/*
# Installer les dΓ©pendances Python
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
# Copier le code
COPY . .
# Utilisateur non-root
RUN useradd -m appuser && chown -R appuser:appuser /app
USER appuser
EXPOSE 8000
CMD ["gunicorn", "--bind", "0.0.0.0:8000", "app:app"]
Java application (multi-stage)β
# Build stage
FROM maven:3.9-eclipse-temurin-17 AS builder
WORKDIR /app
COPY pom.xml .
RUN mvn dependency:go-offline
COPY src ./src
RUN mvn package -DskipTests
# Runtime stage
FROM eclipse-temurin:17-jre-alpine
WORKDIR /app
COPY --from=builder /app/target/*.jar app.jar
RUN addgroup -S spring && adduser -S spring -G spring
USER spring
EXPOSE 8080
ENTRYPOINT ["java", "-jar", "app.jar"]
9 - Dockerfile best practicesβ
Order of instructionsβ
# β Mauvais - Cache invalidΓ© Γ chaque changement de code
FROM node:18
COPY . .
RUN npm install
# β
Bon - DΓ©pendances en cache
FROM node:18
COPY package*.json ./
RUN npm install
COPY . .
Minimize layersβ
# β Plusieurs couches
RUN apt-get update
RUN apt-get install -y nginx
RUN apt-get clean
# β
Une seule couche
RUN apt-get update && \
apt-get install -y nginx && \
apt-get clean && \
rm -rf /var/lib/apt/lists/*
Use lightweight imagesβ
# β Image lourde (1 Go+)
FROM ubuntu:22.04
# β
Image légère (~5 Mo)
FROM alpine:3.18
# β
Version slim (~100 Mo)
FROM python:3.11-slim
Securityβ
# β
Ne pas exΓ©cuter en root
USER appuser
# β
Copier uniquement le nΓ©cessaire
COPY --chown=appuser:appuser app.py /app/
# β
Utiliser des versions fixes
FROM node:18.19.0-alpine3.18
Summaryβ
| Instruction | Description |
|---|---|
FROM | Base image |
WORKDIR | Working directory |
COPY | Copy files |
RUN | Run a command |
ENV | Environment variable |
ARG | Build argument |
EXPOSE | Document a port |
CMD | Default command |
ENTRYPOINT | Entry point |
USER | Change user |
Key points
- Order the instructions to optimize the cache
- Use multi-stage builds for lightweight images
- Do not run as root
- Create a .dockerignore file
Hands-on exercisesβ
- Create a Dockerfile for a "Hello World" application in Python
- Optimize a Dockerfile by reordering the instructions
- Create a multi-stage build for a Go application
- Add a healthcheck to your image