Dockerfiles
Dockerfiles zijn cruciale bouwstenen om te werken met containers. Het zijn tekstbestanden die beschrijven hoe de container opgebouwd wordt.
Elke lijn die daarin voorkomt creëert een extra laag in de container. Die lagen kwamen al eerder aan bod toen we docker inspect image commando uitvoerden.
FROM ubuntu:22.04
RUN apt-get update
RUN apt-get install apache2 -y
RUN apt-get install apache2-utils -y
RUN apt-get clean
EXPOSE 80
CMD [“apache2ctl”, “-D”, “FOREGROUND”]
Bovenstaand voorbeeld toont een Dockerfile waarbij een container wordt aangemaakt op basis van Ubuntu. Met FROM geef je aan welke base image je wil gebruiken. Hier wordt Ubuntu gekozen, maar als je mikt op een extreem kleine en snelle image, kan je ook opteren voor bijvoorbeeld Alpine.
met RUN worden daarna een aantal acties uitgevoerd op de container. Let wel, zoals eerder vermeld zal elke lijn in het bestand een extra laag aanmaken in de container. Het zou dus efficiënter zijn om de verschillende apt-get install commando's te combineren.
Met EXPOSE geef je aan welke poort(en) moeten zichtbaar zijn voor de buitenwereld.
Tenslotte wordt CMD gebruikt om aan te geven welk proces moet gestart worden.
EXPOSE is in de Dockerfile eerder informatief en zorgt niet echt voor een open poort. Met "docker run -p" kunnen sowieso andere poorten effectief gekoppeld worden. EXPOSE laat eigenlijk, aan de gebruiker die de container zal starten, weten op welke poort(en) de applicatie luistert. Wel kan je met "docker run -P" (let op de hoofdletter) alle EXPOSE poorten automatisch laten mappen met een random poort op de host (meestal niet zo handig).
De mogelijkheden voor zo'n bestand zijn uiteraard veel ruimer dan wat hierboven beschreven werd. Daarvoor verwijzen we naar enkele externe bronnen:
Best practices
Enkele best practices bij het maken van Dockerfiles
- Combineer gelijkaardige commando's in één regel om het aantal lagen beperkt te houden (bijvoorbeeld de installatie van verschillende pakketten in één regel in plaats van meerdere)
- Zet commando's die vaak veranderen (vb source files kopiëren) relatief onderaan je Dockerfile, dat komt de snelheid bij het builden ten goede omdat cache (de verschillende lagen) dan optimaal kan gebruikt worden.
- Vermeld steeds de tags van base images zodat je zeker bent dat je image ook op lange termijn stabiel blijft. (vb
FROM ubuntu:22.04ipvFROM ubuntu) - Voeg een
.dockerignore-bestand toe aan je project om bestanden en mappen uit te sluiten van de build context (bv.node_modules/,.git/, lokale log-bestanden). Dit verkleint de image en vermijdt dat gevoelige bestanden per ongeluk in de container belanden. - Gebruik een non-root user in je Dockerfile voor productie-images. Voeg toe
RUN useradd -m appuserenUSER appuserom de container als niet-bevoorrechte gebruiker te draaien.
Multi-stage builds
Een krachtige techniek is het gebruik van multi-stage builds. Hiermee kan je in dezelfde Dockerfile meerdere FROM-stappen definiëren. Een typisch gebruik is een aparte build-stage (met alle compiler-tools) en een afzonderlijke runtime-stage die enkel de gecompileerde output bevat. Dit levert veel kleinere en veiligere productie-images op.
# Stage 1 — build
FROM node:20-alpine AS build
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build
# Stage 2 — productie
FROM nginx:alpine
COPY --from=build /app/dist /usr/share/nginx/html
In de finale image zit enkel de output van de build, niet de volledige Node.js-omgeving. Meer info: Multi-stage builds.
De container bouwen
Eens de Dockerfile klaar is, kan daarvan een image gemaakt worden met docker build
# bouw een image op basis van de Dockerfile in de huidige map
docker build .
# bouw een image met meteen een tag aan gekoppeld
docker build . -t roelvs/dockie
De . (punt) na docker build is de build context: de map waarvan de inhoud naar de Docker-daemon gestuurd wordt. Alles in die map is beschikbaar voor instructies zoals COPY en ADD in de Dockerfile. Een punt betekent dus de huidige map.
Andere opties:
| Syntax | Betekenis |
|---|---|
docker build . | Build context = huidige map, Dockerfile = ./Dockerfile |
docker build /pad/naar/map | Build context = opgegeven map |
docker build -f ./pad/MijnDockerfile . | Gebruik een Dockerfile met een andere naam of locatie |
docker build -t naam:tag . | Ken meteen een naam en tag toe aan de gebouwde image |
docker build --no-cache . | Negeer gecachte lagen en bouw volledig opnieuw |
docker build --target build . | Stop na een specifieke stage (handig bij multi-stage builds) |
Houd de build context zo klein mogelijk. Voeg een .dockerignore-bestand toe om overbodige mappen (zoals node_modules/ of .git/) uit te sluiten. Hoe kleiner de context, hoe sneller de build.
Bijhorende Labs
Maak een container op basis van Alpine waarin je Git installeert:
De verschillende instructies gedemonstreerd:
- Lab #2: ADD instruction
- Lab #3: COPY instruction
- Lab #4: CMD instruction
- Lab #5: ENTRYPOINT instruction
- Lab #6: WORKDIR instruction
- Lab #7: RUN instruction
- Lab #8: ARG instruction
- Lab #9: ENV instruction
- Lab #10: VOLUME instruction
- Lab #11: EXPOSE instruction
- Lab #12: LABEL instruction
- Lab #16: Entrypoint Vs RUN
- Writing Dockerfile with Hello Python Script Added