Docker: od developmentu do produkcji
Kompletny przewodnik po Dockerze do konteneryzacji aplikacji. Dockerfile, Docker Compose, buildy multi-stage i wdrożenie produkcyjne z praktycznymi przykładami.

Docker rewolucjonizuje sposób tworzenia, testowania i wdrażania aplikacji. Poprzez enkapsulację aplikacji i jej zależności w przenośnym kontenerze, Docker eliminuje niesławny problem "u mnie działa" i zapewnia spójność we wszystkich środowiskach. Ten przewodnik obejmuje pełną drogę od pierwszego pliku Dockerfile do wdrożenia produkcyjnego.
Docker Desktop 5.x przynosi znaczące ulepszenia wydajności, w tym natywne wsparcie containerd, zoptymalizowane zarządzanie zasobami i bezproblemową integrację z Kubernetes. Obrazy wieloarchitekturowe (ARM/x86) są teraz standardową praktyką.
Podstawy konteneryzacji
Kontener to lekka jednostka oprogramowania, która pakuje kod, runtime, biblioteki systemowe i ustawienia. W przeciwieństwie do maszyn wirtualnych, które wirtualizują sprzęt, kontenery współdzielą jądro systemu hosta, co czyni je szybszymi w uruchamianiu i mniej zasobożernymi.
# terminal
# Docker installation on Ubuntu
sudo apt update
sudo apt install -y docker.io
# Add user to docker group (avoids sudo)
sudo usermod -aG docker $USER
# Verify installation
docker --version
# Docker version 26.1.0, build 1234567
# First container: downloads image and runs
docker run hello-worldTo polecenie pobiera obraz hello-world z Docker Hub i uruchamia kontener wyświetlający komunikat potwierdzający.
# terminal
# List running containers
docker ps
# List all containers (including stopped)
docker ps -a
# List downloaded images
docker images
# Remove a container
docker rm <container_id>
# Remove an image
docker rmi <image_name>Te podstawowe polecenia zarządzają cyklem życia kontenerów i obrazów.
Tworzenie pierwszego Dockerfile
Dockerfile zawiera instrukcje do budowania obrazu Docker. Każda instrukcja tworzy warstwę w finalnym obrazie, umożliwiając cache'owanie i ponowne użycie.
# Dockerfile
# Base image: Node.js 22 on Alpine Linux (lightweight)
FROM node:22-alpine
# Set working directory in the container
WORKDIR /app
# Copy dependency files first (cache optimization)
COPY package*.json ./
# Install dependencies
RUN npm ci --only=production
# Copy source code
COPY . .
# Expose port (documentation)
EXPOSE 3000
# Startup command
CMD ["node", "server.js"]Kolejność instrukcji jest kluczowa dla optymalizacji cache. Pliki, które rzadko się zmieniają (package.json), powinny być kopiowane przed kodem źródłowym.
# terminal
# Build image with a tag
docker build -t my-app:1.0 .
# Run the container
docker run -d -p 3000:3000 --name my-app-container my-app:1.0
# Check logs
docker logs my-app-container
# Access container shell
docker exec -it my-app-container shFlaga -d uruchamia kontener w tle, -p mapuje port 3000 kontenera na port 3000 hosta.
Obrazy Alpine są znacznie mniejsze (około 5 MB w porównaniu z 120 MB dla Debiana). Używają jednak musl libc zamiast glibc, co może powodować niekompatybilności z niektórymi natywnymi zależnościami. W przypadku problemów zaleca się stosowanie obrazów opartych na Debianie (node:22-slim).
Buildy multi-stage dla produkcji
Buildy multi-stage tworzą zoptymalizowane obrazy produkcyjne, oddzielając środowisko budowania od środowiska uruchomieniowego. Tylko niezbędne artefakty są włączane do finalnego obrazu.
# Dockerfile.production
# ============================================
# Stage 1: Build
# ============================================
FROM node:22-alpine AS builder
WORKDIR /app
# Copy and install dependencies (including devDependencies)
COPY package*.json ./
RUN npm ci
# Copy source code
COPY . .
# Build the application (TypeScript, bundling, etc.)
RUN npm run build
# ============================================
# Stage 2: Production
# ============================================
FROM node:22-alpine AS production
# Non-root user for security
RUN addgroup -g 1001 -S nodejs && \
adduser -S nodejs -u 1001
WORKDIR /app
# Copy only necessary files from builder stage
COPY /app/dist ./dist
COPY /app/node_modules ./node_modules
COPY /app/package.json ./
# Switch to non-root user
USER nodejs
# Environment variables
ENV NODE_ENV=production
ENV PORT=3000
EXPOSE 3000
# Startup command
CMD ["node", "dist/server.js"]To podejście znacznie zmniejsza rozmiar finalnego obrazu, wykluczając narzędzia budowania, devDependencies i pliki źródłowe.
# terminal
# Build with specific file
docker build -f Dockerfile.production -t my-app:production .
# Compare image sizes
docker images | grep my-app
# my-app production abc123 150MB
# my-app 1.0 def456 450MBRedukcja rozmiaru może osiągnąć 60-70% w zależności od projektu, poprawiając czasy wdrożenia i zmniejszając powierzchnię ataku.
Docker Compose do lokalnej orkiestracji
Docker Compose upraszcza zarządzanie aplikacjami wielokontenerowymi. Plik YAML deklaruje wszystkie usługi, ich konfiguracje i zależności.
# docker-compose.yml
version: "3.9"
services:
# Main application
app:
build:
context: .
dockerfile: Dockerfile
ports:
- "3000:3000"
environment:
- NODE_ENV=development
- DATABASE_URL=postgresql://postgres:secret@db:5432/myapp
- REDIS_URL=redis://cache:6379
volumes:
# Mount source code for hot-reload
- ./src:/app/src
- ./package.json:/app/package.json
depends_on:
db:
condition: service_healthy
cache:
condition: service_started
networks:
- app-network
# PostgreSQL database
db:
image: postgres:16-alpine
environment:
POSTGRES_USER: postgres
POSTGRES_PASSWORD: secret
POSTGRES_DB: myapp
volumes:
# Data persistence
- postgres_data:/var/lib/postgresql/data
# Initialization script
- ./init.sql:/docker-entrypoint-initdb.d/init.sql
ports:
- "5432:5432"
healthcheck:
test: ["CMD-SHELL", "pg_isready -U postgres"]
interval: 5s
timeout: 5s
retries: 5
networks:
- app-network
# Redis cache
cache:
image: redis:7-alpine
ports:
- "6379:6379"
volumes:
- redis_data:/data
command: redis-server --appendonly yes
networks:
- app-network
# Named volumes for persistence
volumes:
postgres_data:
redis_data:
# Dedicated network for isolation
networks:
app-network:
driver: bridgeUsługi komunikują się za pomocą swoich nazw (db, cache) przez wewnętrzną sieć Docker. Healthchecki zapewniają gotowość zależności przed uruchomieniem aplikacji.
# terminal
# Start all services
docker compose up -d
# View logs from all services
docker compose logs -f
# Logs from a specific service
docker compose logs -f app
# Stop and remove containers
docker compose down
# Removal including volumes (caution: data loss)
docker compose down -v
# Rebuild after Dockerfile changes
docker compose up -d --buildGotowy na rozmowy o DevOps?
Ćwicz z naszymi interaktywnymi symulatorami, flashcards i testami technicznymi.
Zarządzanie sekretami i zmiennymi środowiskowymi
Bezpieczne zarządzanie sekretami jest kluczowe w produkcji. Docker oferuje kilka podejść w zależności od kontekstu.
# docker-compose.override.yml (development only)
version: "3.9"
services:
app:
env_file:
- .env.development
environment:
- DEBUG=trueDla produkcji, Docker secrets oferuje większe bezpieczeństwo.
# docker-compose.production.yml
version: "3.9"
services:
app:
build:
context: .
dockerfile: Dockerfile.production
secrets:
- db_password
- api_key
environment:
- NODE_ENV=production
- DATABASE_PASSWORD_FILE=/run/secrets/db_password
- API_KEY_FILE=/run/secrets/api_key
secrets:
db_password:
file: ./secrets/db_password.txt
api_key:
file: ./secrets/api_key.txtKod aplikacji odczytuje sekrety z zamontowanych plików.
const fs = require('fs');
const path = require('path');
// Utility function to read Docker secrets
function readSecret(secretName) {
const secretPath = `/run/secrets/${secretName}`;
// Check if secret file exists
if (fs.existsSync(secretPath)) {
return fs.readFileSync(secretPath, 'utf8').trim();
}
// Fallback to classic environment variables
const envVar = secretName.toUpperCase();
return process.env[envVar];
}
module.exports = {
databasePassword: readSecret('db_password'),
apiKey: readSecret('api_key'),
};To podejście zapobiega ujawnianiu sekretów w zmiennych środowiskowych lub obrazach Docker.
Optymalizacja obrazów Docker
Kilka technik zmniejsza rozmiar obrazu i poprawia wydajność.
# Dockerfile.optimized
FROM node:22-alpine AS base
# Install necessary tools in a single layer
RUN apk add --no-cache \
dumb-init \
&& rm -rf /var/cache/apk/*
# ============================================
# Stage: Dependencies
# ============================================
FROM base AS deps
WORKDIR /app
# Copy only lock files for caching
COPY package.json package-lock.json ./
# Install with mounted npm cache (BuildKit)
RUN \
npm ci --only=production
# ============================================
# Stage: Builder
# ============================================
FROM base AS builder
WORKDIR /app
COPY package.json package-lock.json ./
RUN \
npm ci
COPY . .
RUN npm run build
# ============================================
# Stage: Production
# ============================================
FROM base AS production
# Image metadata
LABEL maintainer="team@example.com"
LABEL version="1.0"
LABEL description="Production-ready Node.js application"
# Non-root user
RUN addgroup -g 1001 -S nodejs && \
adduser -S nodejs -u 1001
WORKDIR /app
# Copy production dependencies
COPY /app/node_modules ./node_modules
# Copy build output
COPY /app/dist ./dist
COPY /app/package.json ./
USER nodejs
ENV NODE_ENV=production
# dumb-init as PID 1 for signal handling
ENTRYPOINT ["dumb-init", "--"]
CMD ["node", "dist/server.js"]Użycie dumb-init zapewnia prawidłową obsługę sygnałów Unix, umożliwiając graceful shutdown kontenera.
# terminal
# Enable BuildKit for advanced features
export DOCKER_BUILDKIT=1
# Build with cache and detailed output
docker build --progress=plain -t my-app:optimized .
# Analyze image layers
docker history my-app:optimized
# Detailed image inspection
docker inspect my-app:optimizedObrazy należy regularnie skanować pod kątem podatności za pomocą narzędzi takich jak Trivy lub Snyk. Obrazy bazowe powinny być regularnie aktualizowane w celu uwzględnienia poprawek bezpieczeństwa.
Zaawansowane sieci Docker
Docker oferuje różne sterowniki sieciowe dla różnych przypadków użycia.
# docker-compose.networking.yml
version: "3.9"
services:
# Publicly accessible frontend
frontend:
build: ./frontend
ports:
- "80:80"
networks:
- frontend-network
- backend-network
# API accessible only from frontend
api:
build: ./api
networks:
- backend-network
- database-network
# No external ports exposed
# Isolated database
database:
image: postgres:16-alpine
networks:
- database-network
# Accessible only by API
networks:
frontend-network:
driver: bridge
backend-network:
driver: bridge
internal: true # No internet access
database-network:
driver: bridge
internal: trueTa konfiguracja izoluje usługi zgodnie z zasadą najmniejszych uprawnień. Baza danych jest dostępna wyłącznie przez API.
# terminal
# Inspect Docker networks
docker network ls
# Details of a specific network
docker network inspect app-network
# Create a custom network
docker network create --driver bridge --subnet 172.28.0.0/16 custom-network
# Connect a container to an existing network
docker network connect custom-network my-containerWolumeny i trwałość danych
Wolumeny Docker zachowują dane poza cyklem życia kontenera.
# docker-compose.volumes.yml
version: "3.9"
services:
app:
image: my-app:latest
volumes:
# Named volume for persistent data
- app_data:/app/data
# Bind mount for development
- ./uploads:/app/uploads:rw
# Read-only mount for configuration
- ./config:/app/config:ro
backup:
image: alpine
volumes:
# Access same volume for backups
- app_data:/data:ro
- ./backups:/backups
command: |
sh -c "tar czf /backups/backup-$$(date +%Y%m%d).tar.gz /data"
volumes:
app_data:
driver: local
driver_opts:
type: none
device: /path/to/host/data
o: bindRozróżnienie między nazwanymi wolumenami a bind mountami jest istotne: wolumeny są zarządzane przez Dockera, podczas gdy bind mounty korzystają bezpośrednio z systemu plików hosta.
# terminal
# List volumes
docker volume ls
# Inspect a volume
docker volume inspect app_data
# Remove orphaned volumes
docker volume prune
# Backup a volume
docker run --rm -v app_data:/data -v $(pwd):/backup alpine \
tar czf /backup/volume-backup.tar.gz /dataWdrożenie produkcyjne
Solidny workflow wdrożeniowy obejmuje budowanie, testowanie i push do rejestru.
# deploy.sh
#!/bin/bash
set -e
# Variables
REGISTRY="registry.example.com"
IMAGE_NAME="my-app"
VERSION=$(git describe --tags --always)
echo "Building version: $VERSION"
# Build production image
docker build \
-f Dockerfile.production \
-t $REGISTRY/$IMAGE_NAME:$VERSION \
-t $REGISTRY/$IMAGE_NAME:latest \
--build-arg BUILD_DATE=$(date -u +"%Y-%m-%dT%H:%M:%SZ") \
--build-arg VERSION=$VERSION \
.
# Security scan
echo "Running security scan..."
docker run --rm -v /var/run/docker.sock:/var/run/docker.sock \
aquasec/trivy image $REGISTRY/$IMAGE_NAME:$VERSION
# Push to registry
echo "Pushing to registry..."
docker push $REGISTRY/$IMAGE_NAME:$VERSION
docker push $REGISTRY/$IMAGE_NAME:latest
echo "Deployment complete: $REGISTRY/$IMAGE_NAME:$VERSION"Dla wdrożeń serwerowych, osobny plik compose produkcyjny dostosowuje konfigurację.
# docker-compose.prod.yml
version: "3.9"
services:
app:
image: registry.example.com/my-app:latest
restart: always
deploy:
replicas: 3
resources:
limits:
cpus: "0.5"
memory: 512M
reservations:
cpus: "0.25"
memory: 256M
update_config:
parallelism: 1
delay: 10s
failure_action: rollback
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:3000/health"]
interval: 30s
timeout: 10s
retries: 3
start_period: 40s
logging:
driver: "json-file"
options:
max-size: "10m"
max-file: "3"Ta konfiguracja definiuje alokację zasobów, strategię aktualizacji i healthchecki dla niezawodnego wdrożenia.
# terminal
# Production deployment with Docker Compose
docker compose -f docker-compose.yml -f docker-compose.prod.yml up -d
# Zero-downtime update (rolling update)
docker compose -f docker-compose.yml -f docker-compose.prod.yml pull
docker compose -f docker-compose.yml -f docker-compose.prod.yml up -d --no-deps app
# Rollback if issues occur
docker compose -f docker-compose.yml -f docker-compose.prod.yml up -d --no-deps \
--scale app=0 && \
docker compose -f docker-compose.yml -f docker-compose.prod.yml up -d --no-deps appMonitoring i debugowanie kontenerów
Monitoring kontenerów jest niezbędny w produkcji.
# terminal
# Real-time statistics for all containers
docker stats
# Statistics for a specific container with custom format
docker stats my-app --format "table {{.Name}}\t{{.CPUPerc}}\t{{.MemUsage}}"
# Inspect processes in a container
docker top my-app
# Real-time Docker events
docker events --filter container=my-app
# Copy files from/to a container
docker cp my-app:/app/logs/error.log ./error.logDo zaawansowanego debugowania dostępnych jest kilka technik.
# terminal
# Interactive shell in a running container
docker exec -it my-app sh
# Execute a single command
docker exec my-app cat /app/config/settings.json
# Start a container in debug mode
docker run -it --rm --entrypoint sh my-app:latest
# Inspect environment variables
docker exec my-app printenv
# Analyze logs with filters
docker logs my-app --since 1h --tail 100 | grep ERROR# docker-compose.monitoring.yml
version: "3.9"
services:
app:
# ... existing configuration
labels:
- "prometheus.scrape=true"
- "prometheus.port=3000"
- "prometheus.path=/metrics"
prometheus:
image: prom/prometheus:latest
ports:
- "9090:9090"
volumes:
- ./prometheus.yml:/etc/prometheus/prometheus.yml
- prometheus_data:/prometheus
command:
- '--config.file=/etc/prometheus/prometheus.yml'
- '--storage.tsdb.retention.time=15d'
grafana:
image: grafana/grafana:latest
ports:
- "3001:3000"
volumes:
- grafana_data:/var/lib/grafana
environment:
- GF_SECURITY_ADMIN_PASSWORD=secret
volumes:
prometheus_data:
grafana_data:Ten stos monitoringowy umożliwia zbieranie i wizualizację metryk kontenerów.
Podsumowanie
Docker transformuje cykl rozwoju, zapewniając spójność we wszystkich środowiskach. Konteneryzacja przynosi przenośność, izolację i powtarzalność — kluczowe cechy dla nowoczesnych aplikacji.
Checklista Docker dla produkcji
- ✅ Buildy multi-stage dla zoptymalizowanych obrazów
- ✅ Użytkownik nie-root w kontenerach
- ✅ Healthchecki skonfigurowane dla wszystkich usług
- ✅ Sekrety zarządzane przez Docker secrets lub bezpieczne zmienne środowiskowe
- ✅ Limity zasobów (CPU, pamięć) zdefiniowane
- ✅ Wolumeny dla trwałości krytycznych danych
- ✅ Scentralizowane logowanie z rotacją plików
- ✅ Skanowanie bezpieczeństwa obrazów przed wdrożeniem
- ✅ Strategia aktualizacji bez przestojów
- ✅ Izolowana sieć między usługami
Zacznij ćwiczyć!
Sprawdź swoją wiedzę z naszymi symulatorami rozmów i testami technicznymi.
Opanowanie Dockera to fundamentalna umiejętność każdego współczesnego programisty. Od lokalnego środowiska po wdrożenie produkcyjne, Docker standaryzuje przepływy pracy i upraszcza operacje. Przedstawione tu koncepcje tworzą solidną podstawę do eksploracji Kubernetes i orkiestracji kontenerów na dużą skalę.
Tagi
Udostępnij
Powiązane artykuły

Kluczowe pytania rekrutacyjne DevOps: kompletny przewodnik 2026
Przygotuj się do rozmowy kwalifikacyjnej DevOps z pytaniami o CI/CD, Kubernetes, Docker, Terraform i praktyki SRE. Szczegółowe odpowiedzi w jednym miejscu.

Rozmowa kwalifikacyjna z Kubernetes: Pods, Services i Deployments w praktyce
Kompletny przewodnik po pytaniach rekrutacyjnych dotyczących Kubernetes Pods, Services i Deployments z przykładami YAML i strategiami skalowania na 2026 rok.