Django en Tailwind CSS: Multi-stage build strategie voor het verkleinen van Docker-images
Voor backend‑ontwikkelaars is CSS vaak een eeuwigdurend project. Nadat we de tijd hebben doorgebracht met het handmatig schrijven van style.css, waren we dolblij toen we Bootstrap ontdekten. Maar naarmate het onderhoud toeneemt, wordt het aanpassen van Bootstrap steeds pijnlijker.
De huidige trend is onmiskenbaar Tailwind CSS. De utility‑first aanpak, waarbij styling alleen met klassen gebeurt, is een ware redding voor backend‑engineers. Ik heb zelf Bootstrap volledig vervangen door Tailwind in een bestaand project, en het is nu een essentieel onderdeel geworden van moderne webontwikkeling, vooral in Django‑projecten.
In dit artikel deel ik een optimalisatiestrategie voor Docker‑images bij het gebruik van Tailwind CSS in een Django‑project, met name hoe je een multi‑stage build kunt gebruiken om de image licht te houden.
Probleem: Docker‑image wordt te groot
Om Tailwind in een Django‑omgeving te integreren, gebruiken we vaak het pakket django‑tailwind. Het is goed gedocumenteerd en werkt prima in een lokale ontwikkelomgeving.
Het probleem ontstaat echter in de deployment‑fase, vooral binnen een Docker‑container.
In een lokale of bare‑metal server kun je CSS eenvoudig bouwen:
python manage.py tailwind build
Deze stap vereist intern een Node.js‑ en npm‑proces. Daardoor moet Node.js ook in de productie‑Django‑Docker‑image worden geïnstalleerd, wat inefficiënt is.
- Probleem 1: Node.js moet in de runtime‑image zitten, hoewel het slechts een eenmalige build‑taak is.
- Probleem 2: De image wordt onnodig groot.
- Probleem 3: Het opnemen van onnodige binaries is slecht voor de beveiliging.
Onze doelstelling is duidelijk:
"Kopieer alleen de gecompileerde CSS‑bestanden naar een puur Python‑runtime‑image zonder Node.js."
We gebruiken hiervoor een multi‑stage build in Docker.
Oplossing: 3‑stappen multi‑stage build
Met multi‑stage builds kun je de build‑processen netjes scheiden. De stappen zijn:
- Python Builder: Bouw Python‑pakketten als wheels.
- Node Builder: Compileer Tailwind CSS met
npm. - Final Runtime: Kopieer alleen de resultaten naar de uiteindelijke image.
Op deze manier bevat de uiteindelijke image geen Node.js of build‑tools, maar alleen de bestanden die nodig zijn voor de uitvoering.
Dockerfile voorbeeld
Hieronder een voorbeeld‑Dockerfile voor een Django + Tailwind‑project. Pas paden en bestandsnamen aan op basis van je eigen projectstructuur (bijv. app‑naam theme, statische bestanden, etc.).
# -------------------------------------------------------------------
# Stage 1: Python Builder
# Installeer Linux‑afhankelijkheden en bouw Python‑pakketten als wheels
# -------------------------------------------------------------------
FROM python:3.11-slim as python-builder
WORKDIR /app
COPY requirements.txt .
RUN pip install --upgrade pip && \
pip wheel --no-cache-dir --no-deps --wheel-dir /app/wheels -r requirements.txt
# -------------------------------------------------------------------
# Stage 2: Node Builder
# Gebruik een Node‑image om Tailwind CSS te compileren
# -------------------------------------------------------------------
FROM node:20-alpine as node-builder
WORKDIR /app
# Kopieer het volledige project zodat Tailwind JIT alle templates en Python‑bestanden kan scannen
COPY . .
WORKDIR /app/theme/static_src
# Installeer afhankelijkheden en bouw
RUN npm ci
RUN npm run build
# De build‑output bevindt zich bijvoorbeeld in:
# /app/theme/static/css/dist.css
# -------------------------------------------------------------------
# Stage 3: Final Runtime
# De uiteindelijke runtime‑image bevat alleen Python en de statische bestanden
# -------------------------------------------------------------------
FROM python:3.11-slim
WORKDIR /app
# 1. Installeer de Python‑pakketten uit de builder
COPY --from=python-builder /app/wheels /wheels
COPY --from=python-builder /app/requirements.txt .
RUN pip install --no-cache-dir /wheels/*
# 2. Kopieer de applicatie‑code
COPY . .
# 3. Kopieer alleen de gecompileerde CSS‑bestanden
COPY --from=node-builder /app/theme/static/css/dist.css \
/app/theme/static/css/dist.css
# 4. Voer collectstatic uit
RUN python manage.py collectstatic --noinput
# Start de server
CMD ["gunicorn", "--bind", "0.0.0.0:8000", "config.wsgi:application"]
Afhankelijk van je omgeving kun je extra optimalisaties toepassen, zoals:
- Gebruik van
.dockerignoreom onnodige bestanden te negeren. - Instellen van omgevingsvariabelen zoals
PYTHONDONTWRITEBYTECODE=1enPYTHONUNBUFFERED=1. - Selectief installeren van systeem‑bibliotheken in een Alpine‑image.
Kernpunten samengevat
1. Gescheiden rol van de Node‑builder
In Stage 2 richt je je alleen op het bouwen van CSS. Het is belangrijk dat Tailwind’s content‑configuratie alle templates en Python‑bestanden kan scannen. In het voorbeeld kopiëren we het hele project, maar als je project klein is of de content‑paden duidelijk zijn, kun je alleen de noodzakelijke directories kopiëren.
2. Gebruik van het npm run build‑script
django‑tailwind genereert meestal een package.json met een build‑script, bijvoorbeeld:
{
"scripts": {
"build": "tailwindcss -c tailwind.config.js -o ../static/css/dist.css --minify"
}
}
In een multi‑stage build kun je dit script direct gebruiken. Zorg alleen dat je het pad naar de output correct kopieert.
3. Lichtgewicht en veilig eindimage
In Stage 3 ontbreken npm, node en de Tailwind‑CLI. Alleen de noodzakelijke Python‑pakketten en de gecompileerde CSS‑bestanden zijn aanwezig. Dit resulteert in:
- Een kleinere image.
- Een kleinere aanvalsvlak.
- Een container die alleen het noodzakelijke bevat.
Op de lange termijn levert dit voordelen op voor CI/CD‑snelheid, opslagkosten en beveiligingsinspecties.

Afsluiting
Bij het bouwen van Docker‑images neigen we vaak naar een “alles‑in‑één” aanpak. In productie zijn echter image‑grootte, build‑tijd en beveiliging cruciaal. Door de hierboven beschreven 3‑stappen multi‑stage build toe te passen, beperk je Node.js‑afhankelijkheden tot de build‑fase en houd je de runtime‑image puur en licht.
Als je al een draaiend project hebt, probeer het dan bij de volgende deployment‑pipeline. Eenmaal opgezet, kun je het patroon vrijwel zonder aanpassingen hergebruiken voor nieuwe projecten.
댓글이 없습니다.