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:
- Image (obraz) - szablon tylko do odczytu zawierający aplikację i jej środowisko
- Container (kontener) - działająca instancja obrazu
- Dockerfile - plik tekstowy z instrukcjami do budowy obrazu
- Registry - repozytorium obrazów (np. Docker Hub)
- Volume - mechanizm do persystowania danych
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
- Używaj oficjalnych obrazów bazowych
- Regularnie aktualizuj obrazy
- Uruchamiaj kontenery jako użytkownicy non-root
- Skanuj obrazy pod kątem vulnerabilities
- Używaj secrets dla wrażliwych danych
# .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.