✅ Решения заданий урока 04
⚡ Ключевые файлы
# Dockerfile.multistage
FROM python:3.12-slim AS builder
WORKDIR /app
RUN apt-get update && apt-get install -y --no-install-recommends build-essential \
&& rm -rf /var/lib/apt/lists/*
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"]
Решение задания 1: Multistage vs Single-stage
Структура проекта
# терминал
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
RUN apt-get update && apt-get install -y build-essential \
&& rm -rf /var/lib/apt/lists/*
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY app.py .
EXPOSE 5000
CMD ["python", "app.py"]
Многостадийный Dockerfile
# Dockerfile.multistage
# Стадия сборки: компилируем зависимости
FROM python:3.12-slim AS builder
WORKDIR /app
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, без build-essential
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 build -t myapp:multistage -f Dockerfile.multistage .
docker build -t myapp:singlestage -f Dockerfile.singlestage .
# Сравнение размеров
docker images myapp:multistage
docker images myapp:singlestage
# Запуск
docker run -d --name multistage_app -p 5000:5000 myapp:multistage
docker run -d --name singlestage_app -p 5001:5001 myapp:singlestage
# Проверка: оба отвечают {"message": "Hello, World!"}
# http://localhost:5000
# http://localhost:5001
# Логи
docker logs multistage_app
docker logs singlestage_app
# Уборка
docker stop multistage_app singlestage_app
docker rm multistage_app singlestage_app
Образ с multistage примерно в 2–3 раза меньше. Это происходит потому, что
build-essential (~200 МБ) остался только в промежуточной стадии builder и не вошёл в финальный слой.
Решение задания 2: MediaWiki + MariaDB
# терминал
mkdir mediawiki-compose && cd mediawiki-compose
# docker-compose.yml
# MediaWiki с MariaDB — современный стиль (без version:, без links:)
services:
mediawiki:
image: mediawiki
restart: always
ports:
- "8080:80"
volumes:
- wiki_images:/var/www/html/images
# После настройки раскомментировать:
# - ./LocalSettings.php:/var/www/html/LocalSettings.php
networks:
- wikinet
depends_on:
database:
condition: service_healthy
database:
image: mariadb:11
restart: always
environment:
MYSQL_DATABASE: my_wiki
MYSQL_USER: wikiuser
MYSQL_PASSWORD: example
MYSQL_RANDOM_ROOT_PASSWORD: 'yes'
volumes:
- wiki_db:/var/lib/mysql
networks:
- wikinet
healthcheck:
test: ["CMD", "healthcheck.sh", "--connect", "--innodb_initialized"]
interval: 30s
timeout: 5s
retries: 5
volumes:
wiki_images:
wiki_db:
networks:
wikinet:
# терминал
docker compose up -d
docker compose ps # дождаться "healthy" у database
docker compose logs mediawiki
# Открыть http://localhost:8080 — настроить MediaWiki
# После настройки скачать LocalSettings.php,
# раскомментировать volume в compose, перезапустить:
docker compose restart mediawiki
Решение задания 3: WordPress + MySQL
# терминал
mkdir wordpress-compose && cd wordpress-compose
# docker-compose.yml
# WordPress + MySQL — современный стиль с healthcheck
services:
db:
image: mysql:8.0
restart: always
environment:
MYSQL_ROOT_PASSWORD: rootsecret
MYSQL_DATABASE: wordpress
MYSQL_USER: wordpress
MYSQL_PASSWORD: wordpress
volumes:
- db_data:/var/lib/mysql
networks:
- wpnet
healthcheck:
test: ["CMD", "mysqladmin", "ping", "-h", "localhost",
"-u", "root", "-prootsecret"]
interval: 30s
timeout: 5s
retries: 5
wordpress:
image: wordpress:latest
restart: always
ports:
- "8081:80"
depends_on:
db:
condition: service_healthy
environment:
WORDPRESS_DB_HOST: db:3306
WORDPRESS_DB_USER: wordpress
WORDPRESS_DB_PASSWORD: wordpress
WORDPRESS_DB_NAME: wordpress
volumes:
- wp_data:/var/www/html
networks:
- wpnet
volumes:
db_data:
wp_data:
networks:
wpnet:
# терминал
docker compose up -d
# Наблюдаем: wordpress ждёт healthcheck от db
docker compose ps
# После healthy у db — WordPress стартует
# Открыть http://localhost:8081 — установить WordPress
# Проверка сохранности данных:
docker compose down
docker compose up -d
# Данные сохранились в томах db_data и wp_data
Порт 8081 использован вместо 80, чтобы не конфликтовать с другими сервисами на хосте.