Docker: від розробки до продакшну
Повний посібник з Docker для контейнеризації застосунків. Dockerfile, Docker Compose, multi-stage збірки та розгортання у продакшні з практичними прикладами.

Docker революціонізує спосіб розробки, тестування та розгортання застосунків. Інкапсулюючи застосунок та його залежності у портативному контейнері, Docker усуває сумнозвісну проблему "у мене працює" та забезпечує узгодженість у всіх середовищах. Цей посібник охоплює повний шлях від першого Dockerfile до розгортання у продакшні.
Docker Desktop 5.x приносить значні покращення продуктивності, включаючи нативну підтримку containerd, оптимізоване управління ресурсами та безшовну інтеграцію з Kubernetes. Мульти-архітектурні образи (ARM/x86) тепер є стандартною практикою.
Основи контейнеризації
Контейнер — це легковісна одиниця програмного забезпечення, яка пакує код, середовище виконання, системні бібліотеки та налаштування. На відміну від віртуальних машин, що віртуалізують апаратне забезпечення, контейнери спільно використовують ядро хост-системи, що робить їх швидшими у запуску та менш ресурсозатратними.
# 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-worldЦя команда завантажує образ hello-world з Docker Hub та запускає контейнер, який відображає підтверджувальне повідомлення.
# 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>Ці базові команди керують життєвим циклом контейнерів та образів.
Створення першого Dockerfile
Dockerfile містить інструкції для побудови Docker-образу. Кожна інструкція створює шар у фінальному образі, забезпечуючи кешування та повторне використання.
# 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"]Порядок інструкцій є критичним для оптимізації кешу. Файли, які рідко змінюються (package.json), слід копіювати перед вихідним кодом.
# 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 shПрапорець -d запускає контейнер у фоновому режимі, -p відображає порт 3000 контейнера на порт 3000 хоста.
Образи Alpine значно менші (приблизно 5 МБ проти 120 МБ для Debian). Однак вони використовують musl libc замість glibc, що може спричинити несумісності з деякими нативними залежностями. При виникненні проблем рекомендується надавати перевагу образам на основі Debian (node:22-slim).
Multi-stage збірки для продакшну
Multi-stage збірки створюють оптимізовані продакшн-образи, відокремлюючи середовище збірки від середовища виконання. У фінальний образ включаються лише необхідні артефакти.
# 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"]Цей підхід значно зменшує розмір фінального образу, виключаючи інструменти збірки, devDependencies та вихідні файли.
# 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 450MBЗменшення розміру може досягати 60-70% залежно від проєкту, покращуючи час розгортання та зменшуючи поверхню атаки.
Docker Compose для локальної оркестрації
Docker Compose спрощує управління багатоконтейнерними застосунками. YAML-файл оголошує всі сервіси, їхні конфігурації та залежності.
# 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: bridgeСервіси спілкуються за допомогою своїх імен (db, cache) через внутрішню мережу Docker. Healthcheck-и гарантують готовність залежностей перед запуском застосунку.
# 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 --buildГотовий до співбесід з DevOps?
Практикуйся з нашими інтерактивними симуляторами, flashcards та технічними тестами.
Управління секретами та змінними середовища
Безпечне управління секретами є критичним у продакшні. Docker пропонує кілька підходів залежно від контексту.
# docker-compose.override.yml (development only)
version: "3.9"
services:
app:
env_file:
- .env.development
environment:
- DEBUG=trueДля продакшну Docker secrets забезпечує вищий рівень безпеки.
# 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.txtКод застосунку зчитує секрети з підмонтованих файлів.
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'),
};Цей підхід запобігає розкриттю секретів у змінних середовища або Docker-образах.
Оптимізація Docker-образів
Декілька технік зменшують розмір образу та покращують продуктивність.
# 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"]Використання dumb-init забезпечує коректну обробку Unix-сигналів, що дозволяє graceful завершення роботи контейнера.
# 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:optimizedОбрази слід регулярно сканувати на наявність вразливостей за допомогою інструментів на кшталт Trivy або Snyk. Базові образи необхідно регулярно оновлювати для включення патчів безпеки.
Розширені мережі Docker
Docker пропонує різні мережеві драйвери для різних сценаріїв використання.
# 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: trueЦя конфігурація ізолює сервіси за принципом мінімальних привілеїв. База даних доступна лише через 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-containerТоми та збереження даних
Томи Docker зберігають дані за межами життєвого циклу контейнера.
# 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: bindРозрізнення між іменованими томами та bind mount-ами є важливим: томи керуються Docker, тоді як bind mount-и безпосередньо використовують файлову систему хоста.
# 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 /dataРозгортання у продакшні
Надійний робочий процес розгортання включає збірку, тестування та push до реєстру.
# 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"Для серверних розгортань окремий продакшн compose-файл адаптує конфігурацію.
# 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"Ця конфігурація визначає розподіл ресурсів, стратегію оновлення та healthcheck-и для надійного розгортання.
# 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 appМоніторинг та налагодження контейнерів
Моніторинг контейнерів є необхідним у продакшні.
# 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.logДля поглибленого налагодження доступні різні техніки.
# 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:Цей стек моніторингу дозволяє збирати та візуалізувати метрики контейнерів.
Висновок
Docker трансформує цикл розробки, гарантуючи узгодженість у всіх середовищах. Контейнеризація приносить портативність, ізоляцію та відтворюваність — ключові якості для сучасних застосунків.
Чеклист Docker для продакшну
- ✅ Multi-stage збірки для оптимізованих образів
- ✅ Не-root користувач у контейнерах
- ✅ Healthcheck-и налаштовані для всіх сервісів
- ✅ Секрети керуються через Docker secrets або безпечні змінні середовища
- ✅ Ліміти ресурсів (CPU, пам'ять) визначені
- ✅ Томи для збереження критичних даних
- ✅ Централізоване логування з ротацією файлів
- ✅ Сканування безпеки образів перед розгортанням
- ✅ Стратегія оновлення без простоїв
- ✅ Ізольована мережа між сервісами
Починай практикувати!
Перевір свої знання з нашими симуляторами співбесід та технічними тестами.
Оволодіння Docker — це фундаментальна навичка кожного сучасного розробника. Від локального середовища до розгортання у продакшні, Docker стандартизує робочі процеси та спрощує операції. Представлені тут концепції формують міцну основу для вивчення Kubernetes та оркестрації контейнерів у великому масштабі.
Теги
Поділитися
Пов'язані статті

Ключові питання на DevOps-співбесіді: повний посібник 2026
Підготовка до DevOps-співбесіди: найважливіші питання про CI/CD, Kubernetes, Docker, Terraform та SRE-практики з розгорнутими відповідями.

Співбесіда з Kubernetes: Pods, Services та Deployments -- повний розбір
Питання на співбесідах про Kubernetes Pods, Services та Deployments -- з прикладами YAML, мережевими механізмами та стратегіями масштабування на 2026 рік.