Оркестрация многоконтейнерных приложений с помощью Docker Compose
Многоконтейнерные приложения требуют оркестрации. Docker Compose позволяет описывать и запускать сложные приложения одной командой.
Docker Compose — инструмент для определения и запуска многоконтейнерных приложений Docker. Использует YAML-файл для описания:
Преимущества:
docker-compose up)Docker Compose v2 встроен в Docker Desktop и современные версии Docker CLI:
# Проверка версии
docker compose version
# Docker Compose version v2.20.0
# Старый синтаксис (v1, устарел)
docker-compose versionВажно: В Docker Compose v2 используется docker compose (без дефиса).
version: '3.8'
services:
web:
image: nginx:alpine
ports:
- "80:80"
depends_on:
- api
api:
build: ./api
environment:
- DATABASE_URL=postgresql://user:pass@db:5432/myapp
depends_on:
- db
db:
image: postgres:15
environment:
POSTGRES_USER: user
POSTGRES_PASSWORD: pass
POSTGRES_DB: myapp
volumes:
- postgres-data:/var/lib/postgresql/data
volumes:
postgres-data:
networks:
default:
driver: bridgeВерсия формата Compose файла. Для Docker Compose v2 можно не указывать.
version: '3.8' # Опционально для v2Определяет контейнеры приложения.
services:
web:
image: nginx
api:
build: ./api
db:
image: postgresimage — использовать готовый образ:
services:
nginx:
image: nginx:alpine
postgres:
image: postgres:15build — собирать образ из Dockerfile:
services:
api:
build: ./api
# или с контекстом и dockerfile
build:
context: ./api
dockerfile: Dockerfile.prod
args:
- NODE_ENV=productionПубликация портов:
services:
web:
ports:
- "8080:80" # host:container
- "443:443"
- "127.0.0.1:9000:9000" # только localhostПеременные окружения:
services:
api:
environment:
- DATABASE_URL=postgresql://db:5432/myapp
- REDIS_HOST=redis
- DEBUG=true
# Или из файла
env_file:
- .env
- .env.productionФайл .env:
DATABASE_URL=postgresql://db:5432/myapp
REDIS_HOST=redis
DEBUG=true
Монтирование хранилищ:
services:
db:
volumes:
- postgres-data:/var/lib/postgresql/data
- ./init.sql:/docker-entrypoint-initdb.d/init.sql:ro
app:
volumes:
- ./src:/app/src
- /app/node_modules
volumes:
postgres-data:Зависимости между сервисами:
services:
web:
depends_on:
- api
api:
depends_on:
- db
- redisВажно: depends_on гарантирует порядок запуска, но не ожидает готовности сервиса.
Для ожидания готовности используйте health checks:
services:
api:
depends_on:
db:
condition: service_healthy
redis:
condition: service_started
db:
image: postgres:15
healthcheck:
test: ["CMD-SHELL", "pg_isready -U postgres"]
interval: 10s
timeout: 5s
retries: 5Пользовательские сети:
services:
frontend:
networks:
- frontend-network
api:
networks:
- frontend-network
- backend-network
db:
networks:
- backend-network
networks:
frontend-network:
driver: bridge
backend-network:
driver: bridge
internal: true # Нет доступа извне# Запуск в фоновом режиме
docker compose up -d
# Запуск в foreground (логи в терминал)
docker compose up
# Запуск с пересборкой образов
docker compose up -d --build
# Запуск конкретных сервисов
docker compose up -d api db
# Запуск с удалением старых контейнеров
docker compose up -d --force-recreate# Остановка сервисов
docker compose stop
# Остановка и удаление контейнеров
docker compose down
# Остановка, удаление контейнеров и volumes
docker compose down -v
# Остановка, удаление контейнеров и образов
docker compose down --rmi all# Статус сервисов
docker compose ps
# Логи сервисов
docker compose logs
docker compose logs -f # follow
docker compose logs api # конкретный сервис
docker compose logs -f api db # несколько сервисов
# Использование ресурсов
docker compose top# Выполнить команду в сервисе
docker compose exec api bash
docker compose exec db psql -U postgres
# Запустить новую задачу
docker compose run --rm api python manage.py migrate# Пересобрать образы
docker compose build
# Пересобрать без кэша
docker compose build --no-cache
# Пересобрать конкретный сервис
docker compose build apiПрофили позволяют запускать разные конфигурации:
services:
web:
image: nginx
api:
image: myapi
# Сервис только для разработки
mailhog:
image: mailhog/mailhog
profiles:
- dev
# Сервис для тестирования
test-runner:
image: myapp-test
profiles:
- test
# Запуск
docker compose up -d # web, api
docker compose --profile dev up # web, api, mailhog
docker compose --profile test up # web, api, test-runner# Базовая конфигурация
docker-compose.yml
# Переопределение для dev
docker-compose.dev.yml
# Переопределение для prod
docker-compose.prod.yml# docker-compose.yml
services:
api:
image: myapi:latest
ports:
- "8000:8000"
# docker-compose.dev.yml
services:
api:
volumes:
- ./src:/app/src
environment:
- DEBUG=true
# docker-compose.prod.yml
services:
api:
deploy:
replicas: 3
environment:
- DEBUG=false# Запуск с переопределением
docker compose -f docker-compose.yml -f docker-compose.dev.yml upФайлы применяются по порядку, последующие переопределяют предыдущие.
services:
api:
image: myapi:${TAG:-latest}
environment:
- PORT=${PORT:-8000}# Установка переменных
export TAG=v1.0.0
export PORT=9000
docker compose up
# Или через .env файл
# .env автоматически загружается# .env
POSTGRES_VERSION=15
API_TAG=v1.0.0
DEBUG=false
# docker-compose.yml
services:
db:
image: postgres:${POSTGRES_VERSION}
api:
image: myapi:${API_TAG}# ✅ Хорошо
services:
api:
environment:
- DATABASE_URL=${DATABASE_URL}
- SECRET_KEY=${SECRET_KEY}
# .env (добавить в .gitignore)
DATABASE_URL=postgresql://user:pass@db:5432/myapp
SECRET_KEY=super-secret-key# ❌ Плохо — секреты в compose файле
services:
api:
environment:
- DATABASE_URL=postgresql://user:password123@db:5432/myappservices:
db:
image: postgres:15
healthcheck:
test: ["CMD-SHELL", "pg_isready -U postgres"]
interval: 10s
timeout: 5s
retries: 5
start_period: 30s
api:
depends_on:
db:
condition: service_healthyservices:
api:
image: myapi
deploy:
resources:
limits:
cpus: '1'
memory: 512M
reservations:
cpus: '0.5'
memory: 256Mservices:
api:
restart: unless-stopped
db:
restart: always# ✅ Хорошо
services:
db:
volumes:
- postgres-data:/var/lib/postgresql/data
volumes:
postgres-data:
# ❌ Плохо — anonymous volume
services:
db:
volumes:
- /var/lib/postgresql/data# .dockerignore
.env
*.log
__pycache__
node_modules
.git
# Структура проекта
docker-compose.yml # Базовая конфигурация
docker-compose.dev.yml # Разработка
docker-compose.test.yml # Тесты
docker-compose.prod.yml # Production
myapp/
├── frontend/
│ ├── Dockerfile
│ ├── package.json
│ └── src/
├── backend/
│ ├── Dockerfile
│ ├── requirements.txt
│ └── app/
├── docker-compose.yml
└── .env
version: '3.8'
services:
frontend:
build: ./frontend
ports:
- "3000:80"
depends_on:
- backend
networks:
- frontend-network
backend:
build: ./backend
environment:
- DATABASE_URL=postgresql://user:pass@db:5432/myapp
- REDIS_URL=redis://redis:6379
depends_on:
db:
condition: service_healthy
redis:
condition: service_started
networks:
- frontend-network
- backend-network
db:
image: postgres:15-alpine
environment:
POSTGRES_USER: user
POSTGRES_PASSWORD: pass
POSTGRES_DB: myapp
volumes:
- postgres-data:/var/lib/postgresql/data
networks:
- backend-network
healthcheck:
test: ["CMD-SHELL", "pg_isready -U user"]
interval: 10s
timeout: 5s
retries: 5
redis:
image: redis:7-alpine
networks:
- backend-network
volumes:
postgres-data:
networks:
frontend-network:
driver: bridge
backend-network:
driver: bridge
internal: true# Сборка и запуск
docker compose up -d --build
# Проверка статуса
docker compose ps
# Логи
docker compose logs -f backend
# Остановка
docker compose down# Проверить логи
docker compose logs service_name
# Проверить конфигурацию
docker compose config
# Запустить в foreground для отладки
docker compose up service_name# Проверить сеть
docker compose exec service_name ping other_service
# Проверить переменные окружения
docker compose exec service_name env
# Проверить, запущен ли сервис
docker compose ps# Освободить порт
lsof -i :8000
kill <PID>
# Или изменить порт в compose
ports:
- "8001:8000" # Использовать другой порт хостаВместо явного указания файлов используйте переменную окружения:
# .env
COMPOSE_FILE=docker-compose.yml:docker-compose.dev.yml
# или для Windows
COMPOSE_FILE=docker-compose.yml;docker-compose.dev.yml# Запуск без явного указания файлов
docker compose up -dDocker Compose автоматически загружает docker-compose.override.yml если он существует:
# Структура
docker-compose.yml # Базовая конфигурация
docker-compose.override.yml # Автоматически применяется при запуске# docker-compose.override.yml
services:
api:
volumes:
- ./src:/app/src
environment:
- DEBUG=true# Автоматически применит оба файла
docker compose up -dИдеально для: локальной разработки, чтобы не забывать флаги -f.
services:
api:
env_file:
- .env.${ENVIRONMENT:-development}# .env.development
DEBUG=true
LOG_LEVEL=debug
# .env.production
DEBUG=false
LOG_LEVEL=error
# Запуск
ENVIRONMENT=production docker compose up -dservices:
postgres:
image: postgres:15
healthcheck:
test: ["CMD-SHELL", "pg_isready -U postgres"]
interval: 10s
timeout: 5s
retries: 5
start_period: 30s
mysql:
image: mysql:8.0
healthcheck:
test: ["CMD", "mysqladmin", "ping", "-h", "localhost", "-u", "root", "-p$$MYSQL_ROOT_PASSWORD"]
interval: 10s
timeout: 5s
retries: 5
start_period: 60s
redis:
image: redis:7
healthcheck:
test: ["CMD", "redis-cli", "ping"]
interval: 10s
timeout: 5s
retries: 5
web:
image: nginx
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost/health"]
interval: 30s
timeout: 10s
retries: 3
start_period: 40s
api:
build: ./api
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:8000/healthcheck"]
interval: 15s
timeout: 10s
retries: 5
start_period: 60sАльтернатива health checks — утилита wait-for-it.sh или dockerize:
services:
api:
build: ./api
command: >
sh -c "./wait-for-it.sh db:5432 --timeout=60 --strict --
python manage.py migrate &&
python manage.py runserver 0.0.0.0:8000"
depends_on:
- db# wait-for-it.sh (скачать и добавить в образ)
# https://github.com/vishnubob/wait-for-itИли через dockerize:
# Dockerfile
FROM python:3.11
RUN wget https://github.com/jwilder/dockerize/releases/download/v0.6.1/dockerize-linux-amd64-v0.6.1.tar.gz \
&& tar -C /usr/local/bin -xzvf dockerize-linux-amd64-v0.6.1.tar.gz \
&& rm dockerize-linux-amd64-v0.6.1.tar.gz
CMD ["dockerize", "-wait", "tcp://db:5432", "-timeout", "60s", "python", "manage.py", "runserver"]Эмуляция init-контейнеров через сервисы:
services:
db-migrate:
build: ./api
command: python manage.py migrate
environment:
- DATABASE_URL=postgresql://user:pass@db:5432/myapp
depends_on:
db:
condition: service_healthy
restart: "no"
api:
build: ./api
command: python manage.py runserver 0.0.0.0:8000
depends_on:
db-migrate:
condition: service_completed_successfully
environment:
- DATABASE_URL=postgresql://user:pass@db:5432/myapp
db:
image: postgres:15
healthcheck:
test: ["CMD-SHELL", "pg_isready -U postgres"]
interval: 10s
timeout: 5s
retries: 5# docker-compose.prod.yml
services:
api:
image: myapi
secrets:
- db_password
- api_key
secrets:
db_password:
external: true
api_key:
file: ./secrets/api_key.txt# Создание секретов
echo "super-secret-password" | docker secret create db_password -
docker secret create api_key ./secrets/api_key.txt
# Использование в контейнере
docker compose exec api cat /run/secrets/db_passwordservices:
api:
image: myapi:latest
deploy:
replicas: 3
update_config:
parallelism: 1
delay: 10s
order: start-first
rollback_config:
parallelism: 1
delay: 10s
resources:
limits:
cpus: '1'
memory: 512M
reservations:
cpus: '0.5'
memory: 256M
restart_policy:
condition: on-failure
delay: 5s
max_attempts: 3
window: 120s
placement:
constraints:
- node.role == manager
- node.labels.type == api
labels:
- "com.example.description=API Service"# Развёртывание в Swarm
docker stack deploy -c docker-compose.prod.yml myapp
# Проверка
docker stack ps myapp
docker service lsservices:
api:
image: myapi
logging:
driver: "json-file"
options:
max-size: "10m"
max-file: "3"
compress: "true"
# Отправка в syslog
syslog-service:
image: myapp
logging:
driver: syslog
options:
syslog-address: "udp://syslog.example.com:514"
tag: "myapp"
# Отправка в journald
journald-service:
image: myapp
logging:
driver: journald
# Отключение логирования
no-logs:
image: myapp
logging:
driver: noneservices:
api:
build:
context: ./api
dockerfile: Dockerfile
target: production # Multi-stage build target
args:
- VERSION=1.0.0
- BUILD_DATE=2024-01-01
cache_from:
- myapi:cache
extra_hosts:
- "github.com:140.82.121.4"
secrets:
- npm_token
tags:
- myapi:latest
- myapi:1.0.0
platforms:
- linux/amd64
- linux/arm64
secrets:
npm_token:
environment: NPM_TOKENРасширения для переиспользования конфигурации:
x-api-defaults: &api-defaults
restart: unless-stopped
logging:
driver: json-file
options:
max-size: "10m"
max-file: "3"
networks:
- backend
services:
api-v1:
<<: *api-defaults
image: myapi:v1
ports:
- "8001:8000"
api-v2:
<<: *api-defaults
image: myapi:v2
ports:
- "8002:8000"
networks:
backend:services:
debug-tools:
image: busybox
command: top
profiles:
- debug
# Запускается только с --profile debug
# Условное включение через переменную
monitoring:
image: prom/prometheus
deploy:
replicas: ${MONITORING_REPLICAS:-0}
# При MONITORING_REPLICAS=0 сервис не запускаетсяservices:
api:
image: myapi:${API_VERSION:-latest}
# Запуск конкретной версии
# API_VERSION=v1.2.3 docker compose up -d
db:
image: postgres:${POSTGRES_VERSION:-15}
# POSTGRES_VERSION=14-alpine docker compose up -d# docker-compose.ci.yml
services:
test:
build: ./api
environment:
- CI=true
- DATABASE_URL=postgresql://test:test@db:5432/test
depends_on:
db:
condition: service_healthy
command: pytest
db:
image: postgres:15-alpine
environment:
POSTGRES_USER: test
POSTGRES_PASSWORD: test
POSTGRES_DB: test
healthcheck:
test: ["CMD-SHELL", "pg_isready -U test"]
interval: 5s
timeout: 3s
retries: 5# .gitlab-ci.yml
test:
script:
- docker compose -f docker-compose.yml -f docker-compose.ci.yml up --abort-on-container-exit --exit-code-from test
- docker compose down -vservices:
api:
image: myapi
# Не запускать от root
user: "1000:1000"
# Read-only корневая ФС
read_only: true
# Временные ФС
tmpfs:
- /tmp
- /run
# Drop capabilities
cap_drop:
- ALL
cap_add:
- NET_BIND_SERVICE
# Security options
security_opt:
- no-new-privileges:true
# Ограничение ресурсов
deploy:
resources:
limits:
cpus: '1'
memory: 512Mservices:
frontend:
networks:
- public
api:
networks:
- public
- private
db:
networks:
- private
# Нет доступа извне
networks:
public:
driver: bridge
private:
driver: bridge
internal: true # Нет доступа наружуservices:
backup:
image: postgres:15
volumes:
- ./backups:/backups
- postgres-data:/var/lib/postgresql/data:ro
environment:
- PGPASSWORD=secret
command: >
sh -c "pg_dump -h db -U postgres myapp | gzip > /backups/backup-$$(date +%Y%m%d).sql.gz"
depends_on:
- db# Автоматизация через cron
0 2 * * * cd /path/to/project && docker compose run --rm backupservices:
app:
# ❌ Медленно: много мелких файлов
volumes:
- ./node_modules:/app/node_modules
# ✅ Быстро: монтирование всего каталога
volumes:
- .:/app:cached # macOS
- /app/node_modulesservices:
api:
build:
context: .
cache_from:
- myapi:latest
- myapi:cache# Предварительная загрузка кэша
docker pull myapi:latest
docker compose build# Параллельный запуск сервисов
docker compose up -d --scale api=3 --scale worker=2
# Ограничение параллелизма
docker compose up -d --parallel 2.PHONY: up down rebuild logs test
up:
docker compose up -d
down:
docker compose down -v
rebuild:
docker compose build --no-cache
docker compose up -d --force-recreate
logs:
docker compose logs -f
test:
docker compose run --rm test pytest
shell:
docker compose exec api bash
migrate:
docker compose run --rm api python manage.py migrate
backup:
docker compose exec db pg_dump -U postgres myapp > backup.sql
restore:
docker compose exec -T db psql -U postgres myapp < backup.sql# .github/workflows/test.yml
name: Tests
on: [push, pull_request]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Set up Docker Compose
run: |
docker compose -f docker-compose.yml -f docker-compose.test.yml up -d
- name: Run tests
run: |
docker compose run --rm test pytest
- name: Cleanup
run: |
docker compose down -vАвтоматическая синхронизация файлов без пересборки:
services:
api:
build:
context: .
develop:
watch:
- action: sync
path: ./src
target: /app/src
- action: rebuild
path: requirements.txt# Запуск с watch
docker compose watchdocker compose up -dЦель: Научиться запускать многоконтейнерные приложения с Docker Compose.
Задание:
# Создание структуры
mkdir wordpress-compose && cd wordpress-compose
# Создание docker-compose.yml
cat > docker-compose.yml << 'EOF'
version: '3.8'
services:
db:
image: mysql:8.0
environment:
MYSQL_DATABASE: wordpress
MYSQL_USER: wpuser
MYSQL_PASSWORD: wppass
MYSQL_ROOT_PASSWORD: rootpass
volumes:
- wordpress-db-data:/var/lib/mysql
networks:
- backend
wordpress:
image: wordpress:latest
environment:
WORDPRESS_DB_HOST: db
WORDPRESS_DB_USER: wpuser
WORDPRESS_DB_PASSWORD: wppass
WORDPRESS_DB_NAME: wordpress
ports:
- "8080:80"
depends_on:
- db
networks:
- frontend
- backend
volumes:
wordpress-db-data:
networks:
frontend:
backend:
internal: true
EOF
# Запуск
docker compose up -d
# Проверка статуса
docker compose ps
# Проверка логов
docker compose logs -f wordpress
# Откройте http://localhost:8080 в браузере
# Остановка
docker compose downОжидаемый результат: WordPress доступен по адресу http://localhost:8080.
Цель: Научиться использовать несколько Compose файлов для dev/prod.
Задание:
# Базовая конфигурация
cat > docker-compose.yml << 'EOF'
version: '3.8'
services:
app:
image: nginx:alpine
ports:
- "8080:80"
environment:
- ENV=${ENV:-development}
EOF
# Dev переопределение
cat > docker-compose.dev.yml << 'EOF'
version: '3.8'
services:
app:
volumes:
- ./html:/usr/share/nginx/html:ro
environment:
- DEBUG=true
- ENV=development
EOF
# Production переопределение
cat > docker-compose.prod.yml << 'EOF'
version: '3.8'
services:
app:
deploy:
resources:
limits:
cpus: '0.5'
memory: 128M
environment:
- ENV=production
EOF
# Создание тестовой страницы
mkdir -p html
echo "<h1>Dev Mode</h1>" > html/index.html
# Запуск в dev режиме
docker compose -f docker-compose.yml -f docker-compose.dev.yml up -d
# Проверка
docker compose exec app env | grep -E "DEBUG|ENV"
curl http://localhost:8080
# Остановка
docker compose -f docker-compose.yml -f docker-compose.dev.yml downОжидаемый результат: Приложение запускается с разными конфигурациями в зависимости от файлов.
Цель: Научиться использовать health checks для ожидания готовности сервисов.
Задание:
cat > docker-compose.yml << 'EOF'
version: '3.8'
services:
db:
image: postgres:15-alpine
environment:
POSTGRES_USER: test
POSTGRES_PASSWORD: test
POSTGRES_DB: testdb
healthcheck:
test: ["CMD-SHELL", "pg_isready -U test -d testdb"]
interval: 5s
timeout: 3s
retries: 5
start_period: 10s
networks:
- backend
api:
image: python:3.11-alpine
depends_on:
db:
condition: service_healthy
environment:
DATABASE_URL: postgresql://test:test@db:5432/testdb
command: >
sh -c "pip install psycopg2-binary &&
python -c \"
import psycopg2
import time
for i in range(10):
try:
conn = psycopg2.connect('dbname=testdb user=test password=test host=db')
print('Connected to DB!')
conn.close()
break
except Exception as e:
print(f'Waiting for DB... {i}')
time.sleep(2)
\""
networks:
- backend
networks:
backend:
EOF
# Запуск
docker compose up
# Наблюдайте за логами — API ждёт готовности БДОжидаемый результат: API запускается только после того, как PostgreSQL становится healthy.
Цель: Научиться использовать профили для разных сценариев.
Задание:
cat > docker-compose.yml << 'EOF'
version: '3.8'
services:
api:
image: nginx:alpine
ports:
- "8080:80"
# Сервис только для разработки
mailhog:
image: mailhog/mailhog
profiles:
- dev
ports:
- "8025:8025"
# Сервис для тестирования
test-runner:
image: alpine
profiles:
- test
command: echo "Running tests..."
# Сервис для мониторинга
prometheus:
image: prom/prometheus:latest
profiles:
- monitoring
ports:
- "9090:9090"
volumes:
- ./prometheus.yml:/etc/prometheus/prometheus.yml
EOF
# Создание минимального конфига Prometheus
cat > prometheus.yml << 'EOF'
global:
scrape_interval: 15s
scrape_configs:
- job_name: 'prometheus'
static_configs:
- targets: ['localhost:9090']
EOF
# Запуск базового (только api)
docker compose up -d
docker compose ps
# Запуск с dev профилем
docker compose --profile dev up -d
docker compose ps # api + mailhog
# Запуск с test профилем
docker compose --profile test up -d
docker compose logs test-runner
# Запуск с monitoring профилем
docker compose --profile monitoring up -d
docker compose ps # api + prometheus
# Остановка
docker compose --profile dev --profile test --profile monitoring downОжидаемый результат: Разные профили запускают разные наборы сервисов.
Цель: Научиться создавать бэкапы базы данных из запущенного Compose приложения.
Задание:
# Запуск WordPress (из Lab 6.1)
docker compose up -d
# Добавление данных
docker compose exec db mysql -u root -prootpass -e \
"CREATE TABLE backup_test (id INT, data VARCHAR(100));"
docker compose exec db mysql -u root -prootpass -e \
"INSERT INTO backup_test VALUES (1, 'Important data');"
# Создание бэкапа
docker compose exec db mysqldump -u root -prootpass wordpress > backup.sql
# Проверка бэкапа
cat backup.sql | grep backup_test
# Удаление данных
docker compose exec db mysql -u root -prootpass -e "DROP TABLE backup_test;"
# Восстановление
docker compose exec -T db mysql -u root -prootpass wordpress < backup.sql
# Проверка восстановления
docker compose exec db mysql -u root -prootpass -e "SELECT * FROM backup_test;"
# Остановка
docker compose down
rm backup.sqlОжидаемый результат: Данные успешно восстанавливаются из бэкапа.
Цель: Научиться использовать Watch режим для автоматической синхронизации кода.
Задание:
# Создание структуры
mkdir watch-demo && cd watch-demo
# Простое Flask приложение
cat > app.py << 'EOF'
from flask import Flask
import os
app = Flask(__name__)
@app.route('/')
def hello():
return f"<h1>Hello from Watch Demo!</h1><p>Version: {os.getenv('VERSION', 'dev')}</p>"
if __name__ == '__main__':
app.run(host='0.0.0.0', port=5000)
EOF
cat > requirements.txt << 'EOF'
flask==3.0.0
EOF
cat > Dockerfile << 'EOF'
FROM python:3.11-alpine
WORKDIR /app
COPY requirements.txt .
RUN pip install -r requirements.txt
COPY . .
CMD ["python", "app.py"]
EOF
# docker-compose.yml с Watch
cat > docker-compose.yml << 'EOF'
services:
api:
build:
context: .
develop:
watch:
- action: sync
path: ./app.py
target: /app/app.py
- action: rebuild
path: requirements.txt
ports:
- "5000:5000"
environment:
- VERSION=1.0.0
EOF
# Запуск в режиме watch
docker compose watch
# В другом терминале измените app.py
echo "Version: 2.0.0" >> app.py
# Изменения автоматически синхронизируются
curl http://localhost:5000Ожидаемый результат: Изменения в коде автоматически применяются без пересборки.
Цель: Научиться настраивать безопасную production конфигурацию.
Задание:
mkdir prod-secure && cd prod-secure
cat > docker-compose.yml << 'EOF'
services:
api:
image: nginx:alpine
ports:
- "8080:80"
# Не root пользователь
user: "101:101"
# Read-only корневая ФС
read_only: true
# Временные директории
tmpfs:
- /tmp:size=64M
- /var/cache/nginx:size=64M
- /var/run:size=16M
# Drop всех capabilities
cap_drop:
- ALL
cap_add:
- NET_BIND_SERVICE
# Security options
security_opt:
- no-new-privileges:true
# Ограничение ресурсов
deploy:
resources:
limits:
cpus: '0.5'
memory: 128M
reservations:
cpus: '0.25'
memory: 64M
# Логирование с ротацией
logging:
driver: json-file
options:
max-size: "10m"
max-file: "3"
compress: "true"
# Health check
healthcheck:
test: ["CMD", "wget", "-q", "--spider", "http://localhost/health"]
interval: 30s
timeout: 10s
retries: 3
start_period: 10s
# Restart policy
restart: unless-stopped
networks:
default:
driver: bridge
EOF
# Запуск
docker compose up -d
# Проверка безопасности
docker compose exec api whoami
docker compose exec api touch /test # Должно fail с read-only FS
# Проверка ограничений
docker compose top
docker stats
# Остановка
docker compose downОжидаемый результат: Контейнер запускается с ограниченными правами и read-only ФС.
Цель: Научиться использовать YAML extensions для DRY конфигурации.
Задание:
mkdir extensions-demo && cd extensions-demo
cat > docker-compose.yml << 'EOF'
# Переиспользуемые блоки
x-api-defaults: &api-defaults
restart: unless-stopped
logging:
driver: json-file
options:
max-size: "10m"
max-file: "3"
networks:
- backend
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:8000/health"]
interval: 30s
timeout: 10s
retries: 3
x-logging: &logging-json
driver: json-file
options:
max-size: "10m"
max-file: "3"
services:
api-v1:
<<: *api-defaults
image: myapi:v1
ports:
- "8001:8000"
environment:
- VERSION=v1
- LOG_LEVEL=info
api-v2:
<<: *api-defaults
image: myapi:v2
ports:
- "8002:8000"
environment:
- VERSION=v2
- LOG_LEVEL=debug
logging:
<<: *logging-json
api-v3:
<<: *api-defaults
image: myapi:v3
ports:
- "8003:8000"
environment:
- VERSION=v3
- LOG_LEVEL=warning
# Load balancer
nginx:
image: nginx:alpine
ports:
- "80:80"
volumes:
- ./nginx.conf:/etc/nginx/nginx.conf:ro
depends_on:
- api-v1
- api-v2
- api-v3
networks:
- backend
networks:
backend:
driver: bridge
EOF
cat > nginx.conf << 'EOF'
events {
worker_connections 1024;
}
http {
upstream api {
least_conn;
server api-v1:8000 weight=1;
server api-v2:8000 weight=2;
server api-v3:8000 weight=3;
}
server {
listen 80;
location / {
proxy_pass http://api;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}
location /health {
return 200 "OK";
}
}
}
EOF
# Запуск
docker compose up -d
# Проверка load balancing
for i in {1..10}; do curl -s http://localhost/ | grep VERSION; done
# Остановка
docker compose downОжидаемый результат: Несколько версий API работают с переиспользуемой конфигурацией.
Цель: Научиться использовать Docker Compose в CI/CD пайплайнах.
Задание:
mkdir ci-demo && cd ci-demo
# Простое приложение с тестами
cat > app.py << 'EOF'
def add(a, b):
return a + b
def subtract(a, b):
return a - b
EOF
cat > test_app.py << 'EOF'
from app import add, subtract
def test_add():
assert add(2, 3) == 5
assert add(-1, 1) == 0
def test_subtract():
assert subtract(5, 3) == 2
assert subtract(10, 7) == 3
if __name__ == "__main__":
test_add()
test_subtract()
print("All tests passed!")
EOF
cat > Dockerfile << 'EOF'
FROM python:3.11-alpine
WORKDIR /app
COPY . .
CMD ["python", "test_app.py"]
EOF
# Базовая конфигурация
cat > docker-compose.yml << 'EOF'
services:
app:
build: .
EOF
# CI конфигурация
cat > docker-compose.ci.yml << 'EOF'
services:
test:
build: .
environment:
- CI=true
- PYTHONDONTWRITEBYTECODE=1
command: python test_app.py
EOF
# Запуск тестов
docker compose -f docker-compose.yml -f docker-compose.ci.yml run --rm test
# Проверка exit code
echo "Exit code: $?"
# Остановка
docker compose downОжидаемый результат: Тесты запускаются и возвращают корректный exit code.
Цель: Научиться собирать образы для нескольких архитектур.
Задание:
mkdir multiarch-demo && cd multiarch-demo
cat > Dockerfile << 'EOF'
FROM --platform=$TARGETPLATFORM python:3.11-alpine
WORKDIR /app
RUN uname -m > /app/arch.txt
CMD ["cat", "/app/arch.txt"]
EOF
cat > docker-compose.yml << 'EOF'
services:
arch-test:
build:
context: .
platforms:
- linux/amd64
- linux/arm64
EOF
# Создание buildx builder
docker buildx create --use --name multiarch-builder
# Сборка для нескольких платформ
docker compose build
# Проверка образов
docker buildx imagetools inspect $(docker compose images -q)
# Запуск для разных платформ
docker compose run --platform linux/amd64 arch-test
docker compose run --platform linux/arm64 arch-test
# Остановка
docker compose down
docker buildx rm multiarch-builderОжидаемый результат: Образ собирается и запускается на разных архитектурах.
services:
app:
image: nginx
ports:
- "80:80"services:
app:
build: .
ports:
- "8000:8000"
volumes:
- ./src:/app/src
environment:
- DEBUG=true
depends_on:
db:
condition: service_healthy
db:
image: postgres:15
environment:
POSTGRES_PASSWORD: secret
volumes:
- pgdata:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U postgres"]
interval: 10s
timeout: 5s
retries: 5
volumes:
pgdata:services:
app:
image: myapp:v1.0.0 # Конкретная версия
restart: unless-stopped
user: "1000:1000" # Не root
read_only: true # Read-only FS
tmpfs:
- /tmp
cap_drop:
- ALL
security_opt:
- no-new-privileges:true
deploy:
resources:
limits:
cpus: '1'
memory: 512M
logging:
driver: json-file
options:
max-size: "10m"
max-file: "3"
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost/health"]
interval: 30s
timeout: 10s
retries: 3
secrets:
- db_password
secrets:
db_password:
external: true| Ошибка | Причина | Решение |
|---|---|---|
port is already allocated | Порт занят другим процессом | Измените порт в ports: или освободите порт |
service_healthy never reached | Неправильный health check | Проверьте команду health check и таймауты |
cannot access volume | Volume не создан | Добавьте volume в секцию volumes: |
service not found | Сервис не в той сети | Добавьте сервисы в общую сеть |
permission denied | Неправильный пользователь | Используйте user: или исправьте права на volume |
build failed: not found | Файл не в контексте | Проверьте context: и пути в Dockerfile |
env variable not set | Переменная не загружена | Проверьте .env файл или env_file: |
# Валидация конфигурации
docker compose config
# Визуализация зависимостей
docker compose config --services --depends-on
# Конвертация в JSON
docker compose config --format json
# Запуск одноразовой команды
docker compose run --rm service command
# Копирование файлов
docker compose cp service:/path/file.txt ./local/
# Пауза/возобновление сервисов
docker compose pause
docker compose unpause
# Масштабирование
docker compose up -d --scale api=3
# Принудительная пересборка
docker compose up -d --build --force-recreate
# Полная очистка
docker compose down -v --rmi all --remove-orphansВ следующей теме вы изучите многоэтапную сборку для уменьшения размера образов.
Вопросы ещё не добавлены
Вопросы для этой подтемы ещё не добавлены.