Dlaczego Docker?
Docker to technologia, która pozwala uruchamiać aplikacje w kontenerach – izolowanych środowiskach działających identycznie na każdym serwerze. Zamiast konfigurować zależności ręcznie na każdym serwerze, opisujesz środowisko w pliku Dockerfile, a Docker zajmuje się resztą.
Główne zalety Docker
-
Spójność środowisk – koniec z „u mnie działa". Kontener działa identycznie na maszynie deweloperskiej, serwerze testowym i produkcyjnym.
-
Łatwe wdrażanie – cały stack startuje jednym poleceniem. Nie musisz ręcznie konfigurować Pythona, bibliotek czy bazy danych.
-
Szybsze testy i CI/CD – pipeline działa na identycznym obrazie, co produkcja. Masz pewność, że testy przechodzą w rzeczywistym środowisku.
-
Skalowalność – gotowe pod Kubernetes lub chmurę. Możesz łatwo uruchomić wiele instancji aplikacji na różnych serwerach.
Docker rozwiązuje problem różnic między środowiskami poprzez enkapsulację całego środowiska aplikacji w kontenerze, który działa identycznie na każdym komputerze z zainstalowanym Dockerem.
Tworzenie obrazu aplikacji Python
Zacznij od pliku Dockerfile w katalogu głównym projektu. Dockerfile to przepis na obraz kontenera – opisuje, jak zbudować środowisko dla Twojej aplikacji.
Podstawowy Dockerfile
Dockerfile1 2 3 4 5 6 7 8 9 10 11 12# Dockerfile FROM python:3.12-slim ENV PYTHONUNBUFFERED=1 WORKDIR /app COPY requirements.txt . RUN pip install --no-cache-dir -r requirements.txt COPY . . CMD ["gunicorn", "app.wsgi:application", "--bind", "0.0.0.0:8000"]
Ten plik:
- bazuje na oficjalnym obrazie Pythona (3.12-slim jest lekki i bezpieczny),
- instaluje zależności z
requirements.txt, - kopiuje kod źródłowy aplikacji,
- uruchamia aplikację przez Gunicorn, czyli produkcyjny serwer WSGI (zamiast wbudowanego serwera deweloperskiego Django/Flask).
PYTHONUNBUFFERED=1 zapewnia, że logi są wyświetlane w czasie rzeczywistym, a --no-cache-dir zmniejsza rozmiar obrazu.
Budowanie obrazu
Bash1docker build -t myapp .
Flaga -t nadaje obrazowi nazwę. Kropka na końcu oznacza, że Docker szuka Dockerfile w bieżącym katalogu.
Uruchamianie kontenera
Bash1docker run -p 8000:8000 myapp
Flaga -p 8000:8000 mapuje port 8000 z kontenera na port 8000 hosta, więc aplikacja będzie dostępna pod adresem http://localhost:8000.
Docker Compose – pełna infrastruktura
Większość aplikacji wymaga kilku usług: bazy danych, cache, reverse proxy. Zamiast uruchamiać je ręcznie lub pisać skrypty, zdefiniuj wszystko w jednym pliku docker-compose.yml.
Przykładowa konfiguracja docker-compose.yml
YAML1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47version: '3.9' services: web: build: . ports: - "8000:8000" env_file: - .env depends_on: - db - redis command: gunicorn app.wsgi:application --bind 0.0.0.0:8000 volumes: - .:/app - static_volume:/app/staticfiles db: image: postgres:15 environment: POSTGRES_USER: django POSTGRES_PASSWORD: secret POSTGRES_DB: django_db volumes: - postgres_data:/var/lib/postgresql/data redis: image: redis:7 volumes: - redis_data:/data nginx: image: nginx:latest ports: - "80:80" - "443:443" volumes: - ./nginx.conf:/etc/nginx/conf.d/default.conf - static_volume:/app/staticfiles - ./media:/app/media depends_on: - web volumes: postgres_data: redis_data: static_volume:
Uruchomienie środowiska
Bash1docker-compose up --build
Masz kompletne środowisko: Django/Flask/FastAPI + PostgreSQL + Redis + Nginx — wszystko w jednym poleceniu. Flaga --build wymusza przebudowę obrazów, co jest przydatne po zmianach w Dockerfile.
Docker Compose automatycznie:
- tworzy sieć dla komunikacji między kontenerami,
- uruchamia serwisy w odpowiedniej kolejności (według
depends_on), - zarządza wolumenami dla trwałego przechowywania danych.
Zmienna konfiguracja i tajne dane
Hasła, klucze API czy dane dostępowe nie mogą trafiać do repozytorium. Trzymaj je w pliku .env, który jest ignorowany przez Git (dodaj .env do .gitignore).
Plik .env
INI1 2 3 4 5DEBUG=False SECRET_KEY=super_secret_key_here DATABASE_URL=postgres://django:secret@db:5432/django_db REDIS_URL=redis://redis:6379/0 ALLOWED_HOSTS=example.com,www.example.com
Wczytywanie zmiennych w Pythonie
A następnie wczytaj je w Pythonie (np. przez python-decouple):
Bash1pip install python-decouple
W settings.py (Django) lub konfiguracji aplikacji:
Python1 2 3 4 5 6 7 8 9 10 11from decouple import config SECRET_KEY = config('SECRET_KEY') DEBUG = config('DEBUG', cast=bool, default=False) DATABASE_URL = config('DATABASE_URL') # Parsowanie DATABASE_URL dla Django import dj_database_url DATABASES = { 'default': dj_database_url.config(default=DATABASE_URL) }
W środowisku produkcyjnym używaj zmiennych środowiskowych serwera lub zarządzanych sekretów (np. AWS Secrets Manager, HashiCorp Vault, GitHub Secrets dla CI/CD).
CI/CD – automatyzacja wdrażania
CI/CD (Continuous Integration / Continuous Deployment) to proces, który testuje Twój kod przy każdym commicie, buduje obrazy Dockera i automatycznie wdraża aplikację po przejściu testów. To eliminuje ręczne kroki i zmniejsza ryzyko błędów.
Przykład GitHub Actions workflow
Stwórz plik .github/workflows/deploy.yml:
YAML1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45name: CI/CD Pipeline on: push: branches: [ main ] pull_request: branches: [ main ] jobs: build-test-deploy: runs-on: ubuntu-latest steps: - name: Checkout repository uses: actions/checkout@v4 - name: Set up Python uses: actions/setup-python@v5 with: python-version: 3.12 - name: Install dependencies run: | pip install -r requirements.txt pip install pytest - name: Run tests run: | pytest - name: Run linting run: | pip install flake8 black flake8 . black --check . - name: Build Docker image run: | docker build -t myapp:latest . - name: Deploy container if: github.ref == 'refs/heads/main' && github.event_name == 'push' run: | docker-compose down docker-compose up -d --build
Ten pipeline:
- sprawdza kod z repozytorium,
- uruchamia testy jednostkowe (
pytest), - sprawdza jakość kodu (linting),
- buduje obraz Dockera,
- wdraża go automatycznie na serwer (tylko dla brancha
maini pushów, nie dla pull requestów).
W prawdziwym projekcie dodaj też:
- testy integracyjne,
- etapy dla środowiska staging,
- rollback w przypadku błędów,
- powiadomienia o statusie wdrożenia (Slack, e-mail),
- deployment do chmury (AWS, GCP, Azure).
Dobre praktyki DevOps dla Pythona
Przy projektowaniu systemu wdrożeniowego warto pamiętać o następujących zasadach:
-
Oddziel środowiska – osobne konfiguracje dla developmentu, stagingu i produkcji. Używaj osobnych plików
.envidocker-composedla każdego środowiska (np.docker-compose.dev.yml,docker-compose.prod.yml). -
Używaj .env i secrets – nigdy nie commituj haseł, kluczy API i tokenów. Wszystkie wrażliwe dane powinny być w zmiennych środowiskowych lub zarządzanych sekretach.
-
Buduj lekkie obrazy – używaj
python:slimlubpython:alpinezamiast pełnego obrazu Pythona. To zmniejsza rozmiar obrazu i przyspiesza deployment. -
Cache'uj zależności – optymalizuj
Dockerfile, aby warstwy z zależnościami były cache'owane:
Dockerfile1 2 3 4 5 6# Kopiuj requirements.txt najpierw (częściej zmienia się kod niż zależności) COPY requirements.txt . RUN pip install --no-cache-dir -r requirements.txt # Dopiero potem kopiuj kod COPY . .
-
Uruchamiaj testy automatycznie przed deployem – blokuj deployment, jeśli testy nie przechodzą lub kod nie spełnia standardów jakości.
-
Monitoruj aplikację – używaj narzędzi takich jak Prometheus + Grafana do monitorowania metryk, lub Sentry do śledzenia błędów. To pozwala szybko reagować na problemy w produkcji.
-
Loguj wszystko – dostęp, błędy, metryki, deploye. Centralizowane logowanie (np. ELK Stack, Loki) ułatwia debugowanie i analizę.
-
Używaj multi-stage build – dla bardziej zaawansowanych zastosowań użyj multi-stage builds, aby zmniejszyć rozmiar finalnego obrazu:
Dockerfile1 2 3 4 5 6 7 8 9 10 11 12 13# Stage 1: Build FROM python:3.12 as builder WORKDIR /app COPY requirements.txt . RUN pip install --user -r requirements.txt # Stage 2: Runtime FROM python:3.12-slim WORKDIR /app COPY --from=builder /root/.local /root/.local COPY . . ENV PATH=/root/.local/bin:$PATH CMD ["gunicorn", "app.wsgi:application", "--bind", "0.0.0.0:8000"]
Skalowanie i integracja z chmurą
Docker to świetny wstęp do większej automatyzacji. Kiedy aplikacja rośnie, możesz wykorzystać bardziej zaawansowane narzędzia:
Kubernetes (K8s)
Zarządzanie wieloma kontenerami w klastrze. Kubernetes pozwala na automatyczne skalowanie, load balancing i wysoką dostępność. To następny krok po Docker Compose dla większych aplikacji.
AWS ECS / Google Cloud Run
Serverless uruchamianie kontenerów w chmurze. Cloud Run automatycznie skaluje kontenery w zależności od obciążenia, a płacisz tylko za rzeczywiste użycie zasobów.
GitOps (ArgoCD)
Deklaratywne zarządzanie wdrożeniami. Zmiany w repozytorium automatycznie synchronizują się z środowiskiem produkcyjnym. Git staje się źródłem prawdy dla infrastruktury.
CI/CD z rollbackiem
Wdrożenie z automatycznym cofaniem zmian po błędzie. Zaawansowane pipeline'y mogą automatycznie wykrywać problemy (np. wzrost błędów, spowolnienie) i cofać zmiany bez interwencji człowieka.
Dzięki tym narzędziom aplikacja działa stabilnie niezależnie od ilości użytkowników, a infrastruktura jest zarządzana automatycznie i deklaratywnie.
Podsumowanie
Docker i CI/CD to dziś standard w profesjonalnym wdrażaniu aplikacji Python. Dają powtarzalność, bezpieczeństwo i pełną automatyzację. W połączeniu z testami jednostkowymi i dobrym monitoringiem pozwalają na pewne, szybkie i skalowalne wdrożenia.
Nie ma znaczenia, czy tworzysz mikroserwis w FastAPI, bloga w Django czy API w Flasku — jeśli chcesz wdrażać jak zawodowiec, Docker i CI/CD to obowiązkowy zestaw. Zacznij od prostego Dockerfile i podstawowego pipeline'u CI, a następnie rozbuduj system o zaawansowane funkcje w miarę potrzeb projektu. Pamiętaj, że wdrożenie to proces iteracyjny — zaczynaj od prostych rozwiązań i ulepszaj je systematycznie.



