Docker + Django: Produktionsreife Entwicklungsumgebung aufsetzen
Inhalt
"Works on my machine" ist in professionellen Teams keine Option. Mit Docker läuft deine Django-Anwendung überall identisch — von der lokalen Entwicklung über CI/CD bis zur Produktion.
Das Dockerfile
FROM python:3.12-slim AS base
ENV PYTHONDONTWRITEBYTECODE=1 \
PYTHONUNBUFFERED=1
WORKDIR /app
RUN apt-get update && apt-get install -y \
libpq-dev gcc \
&& rm -rf /var/lib/apt/lists/*
# Erst requirements kopieren (besseres Caching!)
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
# Dann den Code
COPY . .
RUN python manage.py collectstatic --noinputdocker-compose für die Entwicklung
services:
db:
image: postgres:16-alpine
volumes:
- postgres_data:/var/lib/postgresql/data
environment:
POSTGRES_DB: mydb
POSTGRES_USER: myuser
POSTGRES_PASSWORD: mypassword
redis:
image: redis:7-alpine
web:
build: .
command: python manage.py runserver 0.0.0.0:8000
volumes:
- .:/app # Live-Reload
ports: ["8000:8000"]
env_file: .env
depends_on: [db, redis]
celery:
build: .
command: celery -A myproject worker -l info
volumes: [".:/app"]
env_file: .env
depends_on: [db, redis]
volumes:
postgres_data:Nginx + Gunicorn für Produktion
services:
web:
build: .
command: gunicorn myproject.wsgi:application \
--bind 0.0.0.0:8000 --workers 4 --threads 2 --timeout 120
expose: ["8000"]
volumes: [static_files:/app/staticfiles]
nginx:
image: nginx:alpine
ports: ["80:80", "443:443"]
volumes:
- ./nginx.conf:/etc/nginx/conf.d/default.conf
- static_files:/staticfiles
depends_on: [web]# nginx.conf
upstream django { server web:8000; }
server {
listen 80;
location /static/ {
alias /staticfiles/;
expires 30d;
add_header Cache-Control "public, immutable";
}
location / {
proxy_pass http://django;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}
}Umgebungsvariablen richtig handhaben
# .env (nicht in Git!)
DEBUG=True
SECRET_KEY=dev-secret-key
DATABASE_URL=postgres://myuser:mypassword@db:5432/mydb
REDIS_URL=redis://redis:6379/0
ALLOWED_HOSTS=localhost,127.0.0.1# settings.py
import environ
env = environ.Env(DEBUG=(bool, False))
environ.Env.read_env()
DEBUG = env("DEBUG")
SECRET_KEY = env("SECRET_KEY")
DATABASES = {"default": env.db()}Multi-Stage Builds für kleinere Images
FROM python:3.12-slim AS builder
WORKDIR /app
COPY requirements.txt .
RUN pip install --prefix=/install --no-cache-dir -r requirements.txt
# Runtime-Stage
FROM python:3.12-slim AS runtime
COPY --from=builder /install /usr/local
WORKDIR /app
COPY . .
RUN python manage.py collectstatic --noinput
CMD ["gunicorn", "myproject.wsgi:application", "--bind", "0.0.0.0:8000"]Praxis-Tipps
- Requirements zuerst kopieren — Docker cached Layer-für-Layer; ändert sich
requirements.txtnicht, wirdpip installübersprungen - Non-root User für Produktion:
RUN adduser --disabled-password app && USER app - Healthchecks:
HEALTHCHECK CMD curl -f http://localhost:8000/health/ || exit 1 - Image-Größe minimieren —
apt-getimmer mit&& rm -rf /var/lib/apt/lists/*abschließen - .dockerignore nicht vergessen:
.git,__pycache__,.env
