Ga naar hoofdinhoud

Multistage builds

Hoe werken Multi-Stage Builds?

Een multi-stage build gebruikt meerdere FROM-instructies, wat betekent dat je in verschillende fasen van de Dockerfile verschillende basisimages kunt gebruiken. Elke FROM creëert een nieuwe "build-fase". De belangrijkste voordelen van multi-stage builds zijn:

  1. Opgeschoonde eindimages: Je kunt zware tools, dependencies en buildbestanden in de eerste fasen gebruiken zonder dat ze in de uiteindelijke image worden opgenomen.
  2. Efficiënter bouwen: Door herbruikbare tussenstappen te creëren, kun je de laadtijd verbeteren door caching goed te benutten.

Voorbeeld van een Multi-Stage Build

Laten we eens kijken naar een voorbeeld waarbij we een Go-applicatie bouwen. Golang is een programmeertaal waar het uiteindelijke resultaat van de code een enkele binary is die je kan runnen om de applicatie op te starten. In de eerste fase gebruiken we een volledige build-image (met alle benodigde tools), en in de tweede fase gebruiken we een lichte image voor het draaien van de applicatie.

De Dockerfile

# Fase 1: Bouw de Go applicatie
FROM golang:1.17 AS builder

# Stel de werkdirectory in
WORKDIR /app

# Kopieer de Go code
COPY . .

# Installeer dependencies en bouw de applicatie
RUN go mod tidy && go build -o myapp

# Fase 2: De productie-image
FROM alpine:latest

# Stel de werkdirectory in
WORKDIR /app

# Kopieer alleen de gebouwde binary van de 'builder' fase
COPY --from=builder /app/myapp .

# Start de applicatie
CMD ["./myapp"]
  1. Fase 1 - builder:

    • Basisimage: We beginnen met de golang:1.17 image, die alle tools bevat die nodig zijn om een Go-applicatie te bouwen (zoals de Go compiler).
    • Werkdirectory: We stellen /app in als de werkdirectory waarin we de applicatiecode kopiëren.
    • Kopieer de broncode: De broncode van de applicatie wordt naar de container gekopieerd.
    • Bouw de applicatie: We installeren de Go-modules met go mod tidy en bouwen de applicatie met go build -o myapp.
  2. Fase 2 - Productieomgeving:

    • Basisimage: We starten een nieuwe fase met de lichte alpine:latest image, die geen buildtools bevat en dus veel kleiner is.
    • Kopieer de gebouwde applicatie: We gebruiken de COPY --from=builder instructie om de output van de builder-fase naar de productie-image te kopiëren. Alleen de gebouwde myapp binary wordt overgezet, geen van de build-tools of source code.
    • Start de applicatie: Het startcommando (CMD) voert de applicatie uit.

Wat gebeurt er achter de schermen?

  • In de eerste fase (golang:1.17) wordt de volledige applicatie opgebouwd. Dit omvat het installeren van dependencies, het compileren van de code, enzovoort.
  • In de tweede fase (alpine) wordt alleen de noodzakelijke output (de myapp binary) van de eerste fase overgezet. Alle tijdelijke buildbestanden en tools worden achtergelaten in de eerste fase, die niet in de uiteindelijke image komen.

Voordelen van Multi-Stage Builds

  1. Kleinere eindimage:

    • De eindimage bevat alleen de applicatie en zijn dependencies, zonder de buildtools en source code. Dit zorgt voor een veel kleinere en efficiëntere image.
  2. Veiligheid:

    • Omdat je geen buildtools in de productiecontainer hebt, vermijd je dat gevoelige tools of bestanden per ongeluk in de image terechtkomen.
  3. Flexibiliteit:

    • Multi-stage builds maken het mogelijk om verschillende fases te creëren voor verschillende doeleinden (bijvoorbeeld het bouwen van een app, het draaien van tests, enzovoort) en alleen het noodzakelijke resultaat naar de eindcontainer te kopiëren.
  4. Betere caching:

    • Docker zal de lagen cachen die niet veranderen (bijvoorbeeld de dependencies). Dit betekent dat je build sneller kan zijn, omdat de RUN-instructies in de builder-fase maar één keer hoeven te worden uitgevoerd.

Complexer Voorbeeld: Python Applicatie met Tests

In sommige gevallen kan het handig zijn om een fase voor tests toe te voegen en alleen de applicatiecode en dependencies naar de productie-image te kopiëren.

De Dockerfile

# Fase 1: Installeren van buildtools en uitvoeren van tests
FROM python:3.9 AS builder

WORKDIR /app

COPY . .

# Installeer afhankelijkheden en voer tests uit
RUN pip install -r requirements.txt
RUN pytest tests/

# Fase 2: Productieomgeving
FROM python:3.9-slim

WORKDIR /app

COPY --from=builder /app /app

# Start de applicatie
CMD ["python", "app.py"]

In dit voorbeeld:

  • Fase 1 voert de tests uit (met de volledige Python-image) en installeert de vereiste dependencies.
  • Fase 2 gebruikt de python:3.9-slim image (een lichtere versie) en kopieert alleen de benodigde bestanden naar de productiecontainer.

Samenvatting

Multi-stage builds stellen je in staat om verschillende fases van de build in één Dockerfile te beheren. Het stelt je in staat om zware bouwtools en tijdelijke bestanden uit de eindimage te houden, wat resulteert in kleinere en efficiëntere Docker-images. Dit is vooral handig voor:

  • Het scheiden van build- en productieomgevingen.
  • Het optimaliseren van de image-grootte en laadtijd.
  • Het verhogen van de veiligheid door onnodige tools uit de productiecontainer te houden.

Multi-stage builds zijn dus een krachtige techniek voor het bouwen van schone en efficiënte Docker-images, vooral bij complexere toepassingen zoals microservices of applicaties met uitgebreide buildprocessen.