💻 Примеры: Multistage Dockerfile и Docker Compose
⚡ Два ключевых примера
# Dockerfile.multistage — Python/Flask multistage
FROM python:3.12-slim AS builder
WORKDIR /app
COPY requirements.txt .
RUN pip install --user --no-cache-dir -r requirements.txt
COPY app.py .
FROM python:3.12-slim
WORKDIR /app
COPY --from=builder /root/.local /root/.local
COPY --from=builder /app/app.py .
ENV PATH=/root/.local/bin:$PATH
EXPOSE 5000
CMD ["python", "app.py"]
# docker-compose.yml — web (nginx) + db (postgres)
services:
web:
image: nginx:alpine
ports:
- "8080:80"
volumes:
- ./html:/usr/share/nginx/html
depends_on:
- db
networks:
- appnet
db:
image: postgres:16
environment:
POSTGRES_DB: mydb
POSTGRES_USER: user
POSTGRES_PASSWORD: secret
volumes:
- pgdata:/var/lib/postgresql/data
networks:
- appnet
volumes:
pgdata:
networks:
appnet:
Пример 1: Multistage Dockerfile для Flask-приложения
Сравним два подхода: одностадийный и многостадийный Dockerfile для одного и того же Flask-приложения.
Структура проекта
# терминал
mkdir flask-multistage && cd flask-multistage
Файл приложения:
# app.py
from flask import Flask, jsonify
app = Flask(__name__)
@app.route('/')
def hello():
return jsonify(message="Hello, World!")
if __name__ == '__main__':
app.run(host='0.0.0.0', port=5000)
# requirements.txt
Flask==3.0.3
Werkzeug==3.0.3
Одностадийный Dockerfile (для сравнения)
# Dockerfile.singlestage
FROM python:3.12-slim
WORKDIR /app
# Копируем всё, включая инструменты pip
RUN apt-get update && apt-get install -y build-essential
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY app.py .
EXPOSE 5000
CMD ["python", "app.py"]
Многостадийный Dockerfile
# Dockerfile.multistage
# ---- Стадия builder: установка зависимостей ----
FROM python:3.12-slim AS builder
WORKDIR /app
# build-essential нужен только для компиляции, не попадёт в финальный образ
RUN apt-get update && apt-get install -y --no-install-recommends build-essential \
&& rm -rf /var/lib/apt/lists/*
COPY requirements.txt .
# --user устанавливает в ~/.local — легко скопировать в финальный образ
RUN pip install --user --no-cache-dir -r requirements.txt
COPY app.py .
# ---- Финальный образ: только runtime ----
FROM python:3.12-slim
WORKDIR /app
# Копируем установленные пакеты из стадии builder
COPY --from=builder /root/.local /root/.local
# Копируем только исходник приложения
COPY --from=builder /app/app.py .
# Добавляем ~/.local/bin в PATH, чтобы flask/gunicorn были доступны
ENV PATH=/root/.local/bin:$PATH
EXPOSE 5000
CMD ["python", "app.py"]
Сборка и сравнение размеров
# терминал
# Собираем одностадийный образ
docker build -t myapp:singlestage -f Dockerfile.singlestage .
# Собираем многостадийный образ
docker build -t myapp:multistage -f Dockerfile.multistage .
# Сравниваем размеры
docker images myapp:singlestage
docker images myapp:multistage
Ожидаемый результат: образ с multistage примерно в 2–3 раза меньше по размеру.
Запуск и проверка
# терминал
docker run -d --name multistage_app -p 5000:5000 myapp:multistage
docker run -d --name singlestage_app -p 5001:5001 myapp:singlestage
# Проверка в браузере или curl:
# http://localhost:5000 — multistage
# http://localhost:5001 — singlestage
# Просмотр логов
docker logs multistage_app
docker logs singlestage_app
# Остановка
docker stop multistage_app singlestage_app
docker rm multistage_app singlestage_app
Пример 2: Docker Compose — веб-сервер и база данных
Классическая связка: nginx как фронтенд + PostgreSQL как база данных.
Структура проекта
# терминал
mkdir compose-webdb && cd compose-webdb
mkdir html
# html/index.html
<!DOCTYPE html>
<html>
<head><title>Docker Compose Demo</title></head>
<body>
<h1>Привет от nginx в Docker Compose!</h1>
</body>
</html>
docker-compose.yml
# docker-compose.yml
services:
web:
image: nginx:alpine
ports:
- "8080:80"
volumes:
- ./html:/usr/share/nginx/html:ro
depends_on:
- db
networks:
- appnet
db:
image: postgres:16
environment:
POSTGRES_DB: mydb
POSTGRES_USER: user
POSTGRES_PASSWORD: secret
volumes:
- pgdata:/var/lib/postgresql/data
networks:
- appnet
volumes:
pgdata:
networks:
appnet:
Запуск
# терминал
# Запустить все сервисы в фоне
docker compose up -d
# Посмотреть статус
docker compose ps
# Открыть в браузере: http://localhost:8080
# Логи
docker compose logs web
docker compose logs db
# Остановить и очистить
docker compose down
Пример 3: Docker Compose — Django-приложение с PostgreSQL
Более сложный пример из лекции: кастомная сборка приложения + БД с зависимостью.
# docker-compose.yml
services:
web:
build: ./web # собрать из ./web/Dockerfile
command: python manage.py runserver 0.0.0.0:8000
volumes:
- ./web:/code # bind mount для hot reload
ports:
- "8000:8000"
depends_on:
- db
environment:
- DJANGO_DB_HOST=db
- DJANGO_DB_PORT=5432
- DJANGO_DB_NAME=mydatabase
- DJANGO_DB_USER=myuser
- DJANGO_DB_PASSWORD=mypassword
db:
image: postgres:16
volumes:
- pgdata:/var/lib/postgresql/data
environment:
- POSTGRES_DB=mydatabase
- POSTGRES_USER=myuser
- POSTGRES_PASSWORD=mypassword
volumes:
pgdata:
# web/Dockerfile
FROM python:3.12-slim
WORKDIR /code
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY . .
Запуск с пересборкой
# терминал
# Собрать образы и запустить
docker compose up --build -d
# Выполнить миграции Django
docker compose exec web python manage.py migrate
# Создать суперпользователя
docker compose exec web python manage.py createsuperuser
Пример 4: depends_on с healthcheck
Правильный способ дождаться готовности базы данных перед стартом приложения.
# docker-compose.yml
services:
db:
image: mysql:8.0
environment:
MYSQL_ROOT_PASSWORD: rootsecret
MYSQL_DATABASE: mydb
MYSQL_USER: appuser
MYSQL_PASSWORD: appsecret
volumes:
- mysql_data:/var/lib/mysql
healthcheck:
# Проверяем, что MySQL принимает подключения
test: ["CMD", "mysqladmin", "ping", "-h", "localhost"]
interval: 30s # проверять каждые 30 секунд
timeout: 5s # таймаут одной проверки
retries: 5 # количество попыток до статуса "unhealthy"
app:
build: ./app
ports:
- "5000:5000"
depends_on:
db:
condition: service_healthy # ждать, пока DB пройдёт healthcheck
environment:
DB_HOST: db
DB_USER: appuser
DB_PASSWORD: appsecret
DB_NAME: mydb
volumes:
mysql_data:
# терминал
docker compose up -d
# Сначала запустится db, app дождётся healthcheck
docker compose ps # смотрим статус: "healthy" у db
docker compose logs app # приложение стартует только после healthy