Docker dla programistów: od podstaw do produkcji

Docker zrewolucjonizował sposób, w jaki programiści rozwijają, testują i wdrażają aplikacje. W tym kompletnym przewodniku przejdziemy przez wszystko, co musisz wiedzieć o Docker - od podstawowych konceptów po zaawansowane techniki produkcyjne.

Czym jest Docker?

Docker to platforma konteneryzacji, która pozwala na pakowanie aplikacji wraz z ich zależnościami do przenośnych kontenerów. Kontenery są lekkie, szybkie w uruchamianiu i zapewniają izolację między aplikacjami.

Kluczowe koncepty:

Pierwsze kroki z Docker

Instalacja Docker

Instalacja Docker różni się w zależności od systemu operacyjnego:

# Ubuntu/Debian curl -fsSL https://get.docker.com -o get-docker.sh sudo sh get-docker.sh # macOS - pobierz Docker Desktop z docker.com # Windows - pobierz Docker Desktop z docker.com # Sprawdź instalację docker --version docker run hello-world

Podstawowe komendy Docker

# Pobranie obrazu docker pull nginx # Uruchomienie kontenera docker run -d -p 8080:80 --name moj-nginx nginx # Lista działających kontenerów docker ps # Lista wszystkich kontenerów docker ps -a # Zatrzymanie kontenera docker stop moj-nginx # Usunięcie kontenera docker rm moj-nginx # Lista obrazów docker images # Usunięcie obrazu docker rmi nginx # Przygląd logów kontenera docker logs moj-nginx # Wejście do działającego kontenera docker exec -it moj-nginx bash

Tworzenie własnych obrazów - Dockerfile

Dockerfile to recepta na budowę obrazu Docker. Każda instrukcja tworzy nową warstwę w obrazie.

Przykład: Aplikacja Node.js

# Dockerfile dla aplikacji Node.js FROM node:18-alpine # Ustawienie katalogu roboczego WORKDIR /app # Kopiowanie package.json i package-lock.json COPY package*.json ./ # Instalacja zależności RUN npm ci --only=production # Kopiowanie kodu aplikacji COPY . . # Utworzenie użytkownika non-root RUN addgroup -g 1001 -S nodejs RUN adduser -S nextjs -u 1001 # Zmiana właściciela plików USER nextjs # Eksponowanie portu EXPOSE 3000 # Zdefiniowanie health check HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \ CMD curl -f http://localhost:3000/health || exit 1 # Komenda startowa CMD ["npm", "start"]
# package.json { "name": "moja-app", "version": "1.0.0", "scripts": { "start": "node server.js", "dev": "nodemon server.js" }, "dependencies": { "express": "^4.18.0" } } # server.js const express = require('express'); const app = express(); const PORT = process.env.PORT || 3000; app.get('/', (req, res) => { res.json({ message: 'Witaj w aplikacji Node.js w Docker!' }); }); app.get('/health', (req, res) => { res.status(200).json({ status: 'OK', timestamp: new Date().toISOString() }); }); app.listen(PORT, '0.0.0.0', () => { console.log(`Serwer działa na porcie ${PORT}`); });

Budowanie i uruchamianie obrazu

# Budowanie obrazu docker build -t moja-app:v1.0 . # Uruchomienie kontenera docker run -d -p 3000:3000 --name moja-app-container moja-app:v1.0 # Testowanie aplikacji curl http://localhost:3000

Przykład: Aplikacja Python (Flask)

# Dockerfile dla Flask FROM python:3.11-slim # Ustawienie zmiennych środowiskowych ENV PYTHONDONTWRITEBYTECODE=1 ENV PYTHONUNBUFFERED=1 # Instalacja zależności systemowych RUN apt-get update && apt-get install -y \ gcc \ && rm -rf /var/lib/apt/lists/* # Ustawienie katalogu roboczego WORKDIR /app # Kopiowanie plików z zależnościami COPY requirements.txt . # Instalacja zależności Python RUN pip install --no-cache-dir -r requirements.txt # Kopiowanie kodu aplikacji COPY . . # Utworzenie użytkownika non-root RUN groupadd -r appuser && useradd -r -g appuser appuser RUN chown -R appuser:appuser /app USER appuser # Eksponowanie portu EXPOSE 5000 # Komenda startowa CMD ["gunicorn", "--bind", "0.0.0.0:5000", "--workers", "4", "app:app"]
# requirements.txt Flask==2.3.0 gunicorn==21.2.0 # app.py from flask import Flask, jsonify import os app = Flask(__name__) @app.route('/') def hello(): return jsonify({ 'message': 'Witaj w aplikacji Flask w Docker!', 'version': '1.0.0' }) @app.route('/health') def health(): return jsonify({'status': 'healthy'}), 200 if __name__ == '__main__': app.run(host='0.0.0.0', port=5000, debug=False)

Docker Compose - orkiestracja kontenerów

Docker Compose pozwala definiować i uruchamiać aplikacje wielokontenerowe za pomocą pliku YAML.

# docker-compose.yml - Pełna aplikacja webowa version: '3.8' services: # Aplikacja webowa web: build: context: . dockerfile: Dockerfile ports: - "3000:3000" environment: - NODE_ENV=production - DATABASE_URL=postgresql://user:password@db:5432/myapp - REDIS_URL=redis://redis:6379 depends_on: - db - redis volumes: - ./uploads:/app/uploads restart: unless-stopped # Baza danych PostgreSQL db: image: postgres:15-alpine environment: - POSTGRES_DB=myapp - POSTGRES_USER=user - POSTGRES_PASSWORD=password volumes: - postgres_data:/var/lib/postgresql/data - ./init.sql:/docker-entrypoint-initdb.d/init.sql restart: unless-stopped # Redis dla cache redis: image: redis:7-alpine command: redis-server --appendonly yes volumes: - redis_data:/data restart: unless-stopped # Nginx jako reverse proxy nginx: image: nginx:alpine ports: - "80:80" - "443:443" volumes: - ./nginx.conf:/etc/nginx/nginx.conf - ./certs:/etc/nginx/certs depends_on: - web restart: unless-stopped volumes: postgres_data: redis_data: networks: default: driver: bridge
# Komendy Docker Compose # Uruchomienie wszystkich serwisów docker-compose up -d # Przebudowa i uruchomienie docker-compose up -d --build # Zatrzymanie wszystkich serwisów docker-compose down # Wyświetlenie logów docker-compose logs -f web # Skalowanie serwisu docker-compose up -d --scale web=3 # Status serwisów docker-compose ps # Usunięcie kontenerów i sieci docker-compose down --volumes --rmi all

Najlepsze praktyki

1. Optymalizacja obrazów

Multi-stage builds

Używaj wieloetapowych buildów do tworzenia minimalnych obrazów produkcyjnych.

# Multi-stage build dla aplikacji Node.js # Etap 1: Build FROM node:18-alpine AS builder WORKDIR /app COPY package*.json ./ RUN npm ci COPY . . RUN npm run build # Etap 2: Produkcja FROM node:18-alpine AS production WORKDIR /app # Kopiowanie tylko niezbędnych plików COPY package*.json ./ RUN npm ci --only=production && npm cache clean --force # Kopiowanie zbudowanej aplikacji COPY --from=builder /app/dist ./dist USER node EXPOSE 3000 CMD ["node", "dist/server.js"]

2. Bezpieczeństwo

# .dockerignore - wykluczanie niepotrzebnych plików node_modules npm-debug.log .env .git .gitignore README.md Dockerfile docker-compose.yml .nyc_output coverage .vscode # Skanowanie obrazu docker scout cves moja-app:v1.0 # Używanie Docker secrets (w swarm mode) echo "supersecretpassword" | docker secret create db_password -

3. Zarządzanie danymi

# Tworzenie i używanie volume'ów docker volume create app-data docker run -d \ --name moja-app \ -v app-data:/app/data \ -v $(pwd)/config:/app/config:ro \ moja-app:v1.0 # Backup volume'u docker run --rm \ -v app-data:/source:ro \ -v $(pwd):/backup \ alpine \ tar czf /backup/backup.tar.gz -C /source .

Deployment w produkcji

Docker Swarm - podstawowa orkiestracja

# Inicjalizacja swarm docker swarm init # Deploy stack docker stack deploy -c docker-compose.prod.yml myapp # Skalowanie serwisu docker service scale myapp_web=5 # Aktualizacja serwisu docker service update --image myapp:v2.0 myapp_web # Rolling update z zero-downtime docker service update \ --update-parallelism 1 \ --update-delay 30s \ --image myapp:v2.0 \ myapp_web

Monitoring i logi

# docker-compose.monitoring.yml version: '3.8' services: app: image: myapp:latest logging: driver: "json-file" options: max-size: "10m" max-file: "3" # Prometheus dla metryki prometheus: image: prom/prometheus ports: - "9090:9090" volumes: - ./prometheus.yml:/etc/prometheus/prometheus.yml # Grafana dla wizualizacji grafana: image: grafana/grafana ports: - "3001:3000" environment: - GF_SECURITY_ADMIN_PASSWORD=admin # ELK Stack dla logów elasticsearch: image: docker.elastic.co/elasticsearch/elasticsearch:8.5.0 environment: - discovery.type=single-node - xpack.security.enabled=false logstash: image: docker.elastic.co/logstash/logstash:8.5.0 volumes: - ./logstash.conf:/usr/share/logstash/pipeline/logstash.conf kibana: image: docker.elastic.co/kibana/kibana:8.5.0 ports: - "5601:5601" environment: - ELASTICSEARCH_HOSTS=http://elasticsearch:9200

Debugging i rozwiązywanie problemów

# Debugging kontenerów # Sprawdzenie zasobów docker stats # Szczegóły kontenera docker inspect moj-kontener # Procesy w kontenerze docker exec moj-kontener ps aux # System events docker events # Czyszczenie systemu docker system prune -a # Analiza wielkości obrazu docker history moja-app:v1.0 # Debug network docker network ls docker network inspect bridge

CI/CD z Docker

# .github/workflows/docker-build.yml name: Build and Push Docker Image on: push: branches: [ main ] pull_request: branches: [ main ] jobs: build: runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - name: Set up Docker Buildx uses: docker/setup-buildx-action@v2 - name: Log in to Docker Hub uses: docker/login-action@v2 with: username: ${{ secrets.DOCKER_USERNAME }} password: ${{ secrets.DOCKER_PASSWORD }} - name: Build and push uses: docker/build-push-action@v3 with: context: . file: ./Dockerfile push: true tags: | myusername/myapp:latest myusername/myapp:${{ github.sha }} cache-from: type=gha cache-to: type=gha,mode=max

Kluczowe wskazówki dla produkcji

  • Używaj wieloetapowych buildów dla minimalnych obrazów
  • Implementuj health checks
  • Używaj proper logging i monitoring
  • Regularnie aktualizuj obrazy bazowe
  • Testuj obrazy przed deploymentem
  • Używaj orchestrators (Kubernetes, Docker Swarm)

Docker to potężne narzędzie, które znacznie upraszcza deployment i skalowanie aplikacji. Opanowanie Dockera jest dzisiaj niezbędne dla każdego programisty pracującego z nowoczesnymi aplikacjami webowymi. Zaczynaj od prostych przykładów i stopniowo wprowadzaj bardziej zaawansowane techniki do swojego workflow.

← Powrót do bloga