GitLab CI/CD für Django-Projekte: komplette Pipeline aufsetzen
Inhalt
CI/CD ist einer der größten Qualitätshebel in der Softwareentwicklung. Ich baue GitLab-Pipelines seit vielen Jahren und zeige dir hier eine kampferprobte Konfiguration für Django-Projekte.
Grundstruktur der Pipeline
# .gitlab-ci.yml
image: python:3.12-slim
variables:
PIP_CACHE_DIR: "$CI_PROJECT_DIR/.cache/pip"
POSTGRES_DB: test_db
POSTGRES_USER: postgres
POSTGRES_PASSWORD: postgres
DATABASE_URL: "postgres://postgres:postgres@postgres:5432/test_db"
cache:
paths:
- .cache/pip
- venv/
stages:
- test
- quality
- build
- deploy
before_script:
- python -m venv venv
- source venv/bin/activate
- pip install -r requirements.txt -qTest-Stage
test:unit:
stage: test
services:
- postgres:16-alpine
- redis:7-alpine
variables:
REDIS_URL: "redis://redis:6379/0"
script:
- pytest
--cov=myapp
--cov-report=term-missing
--cov-report=xml
--cov-fail-under=80
-v
coverage: '/TOTAL.*\s+(\d+%)$/'
artifacts:
reports:
coverage_report:
coverage_format: cobertura
path: coverage.xml
expire_in: 1 week
test:migrations:
stage: test
services:
- postgres:16-alpine
script:
- python manage.py migrate --check # Fehlschlag wenn Migrations fehlen
- python manage.py migrate
- python manage.py check --deployCode-Qualität: Linting und Type-Checking
quality:lint:
stage: quality
script:
- pip install ruff mypy -q
- ruff check myapp/ # Linting
- ruff format --check myapp/ # Format-Check
- mypy myapp/ --ignore-missing-imports
allow_failure: false
quality:security:
stage: quality
script:
- pip install bandit safety -q
- bandit -r myapp/ -ll # Security-Scan
- safety check # Bekannte Vulnerabilities in Dependencies
allow_failure: true # Warnung, kein FehlerDocker-Build-Stage
build:docker:
stage: build
image: docker:24
services:
- docker:24-dind
variables:
DOCKER_TLS_CERTDIR: "/certs"
before_script:
- docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY
script:
- docker build
--cache-from $CI_REGISTRY_IMAGE:latest
--build-arg BUILDKIT_INLINE_CACHE=1
-t $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA
-t $CI_REGISTRY_IMAGE:latest
.
- docker push $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA
- docker push $CI_REGISTRY_IMAGE:latest
only:
- main
- developDeployment-Stage
deploy:staging:
stage: deploy
image: alpine:latest
before_script:
- apk add --no-cache openssh-client
- eval $(ssh-agent -s)
- echo "$SSH_PRIVATE_KEY" | ssh-add -
- mkdir -p ~/.ssh && chmod 700 ~/.ssh
- ssh-keyscan $STAGING_HOST >> ~/.ssh/known_hosts
script:
- ssh deploy@$STAGING_HOST "
docker pull $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA &&
docker-compose -f /srv/myapp/docker-compose.prod.yml up -d --no-deps web &&
docker exec myapp_web python manage.py migrate --no-input
"
environment:
name: staging
url: https://staging.example.com
only:
- develop
deploy:production:
stage: deploy
extends: .deploy_template
environment:
name: production
url: https://example.com
when: manual # Manueller Trigger für Produktion!
only:
- mainUmgebungsvariablen und Secrets
Secrets gehören nie in die .gitlab-ci.yml. Nutze GitLab CI/CD Variables (unter Settings → CI/CD → Variables):
$DATABASE_URL— Datenbankverbindung pro Umgebung$SSH_PRIVATE_KEY— Deployment-Key (als File-Variable)$CI_REGISTRY_PASSWORD— Automatisch von GitLab gesetzt$SECRET_KEY— Django Secret Key pro Umgebung
Optimierungen für schnelle Pipelines
# Parallele Jobs für mehr Speed
test:parallel:
stage: test
parallel:
matrix:
- TEST_MODULE: [articles, users, orders, payments]
script:
- pytest myapp/$TEST_MODULE/ -v
# Nur relevante Jobs bei Feature-Branches
.only_changes: &only_changes
only:
changes:
- "myapp/**/*"
- "requirements*.txt"
test:unit:
<<: *only_changes
stage: test
script:
- pytest- Docker Layer Caching:
--cache-from latestreduziert Build-Zeit drastisch - Pip-Cache:
PIP_CACHE_DIRzwischen Jobs teilen - Parallele Jobs: Tests nach Modul aufteilen
only: changes: Jobs nur ausführen wenn relevante Dateien geändert wurden
