Docker для Python: рабочий Dockerfile веб-приложения и requirements.txt
Автор: Казачкин Даниил Михайлович · Обновлено
Ниже полный пример Dockerfile для Python с маленьким HTTP-приложением. Сначала обойдёмся стандартной библиотекой: так можно проверить Docker отдельно от загрузки внешних пакетов. Затем добавим вариант с requirements.txt. Все файлы создаются в новой папке python-docker-lab.
Код приложения
Сохраните app.py:
import json
import os
from http.server import BaseHTTPRequestHandler, HTTPServer
class Handler(BaseHTTPRequestHandler):
def do_GET(self):
body = json.dumps({"message": os.getenv("GREETING", "hello-docker")}).encode()
self.send_response(200)
self.send_header("Content-Type", "application/json")
self.send_header("Content-Length", str(len(body)))
self.end_headers()
self.wfile.write(body)
HTTPServer(("0.0.0.0", 8000), Handler).serve_forever()Приложение слушает 0.0.0.0 внутри контейнера. Если привязать его только к 127.0.0.1 контейнера, опубликованный порт не сможет доставить запрос на внешний интерфейс процесса. Этот http.server — учебный пример; для production выбирают подходящий сервер приложения и отдельно решают вопросы таймаутов и конкурентности. Документация Python.
Dockerfile Python-приложения
FROM python:3.13-slim
ENV PYTHONDONTWRITEBYTECODE=1 PYTHONUNBUFFERED=1
WORKDIR /app
COPY --chown=10001:10001 app.py ./app.py
USER 10001:10001
EXPOSE 8000
CMD ["python", "app.py"]Создайте .dockerignore:
.git
.venv
__pycache__
*.pyc
.envВ образ копируется конкретный исходник. USER запускает приложение с непривилегированным UID; Python не обязан создавать системную запись пользователя для этого примера. EXPOSE документирует порт, но не публикует его на компьютере. PYTHONUNBUFFERED помогает сразу видеть вывод процесса в логах. JSON-массив CMD не запускает дополнительную shell. Инструкции Dockerfile.
Сборка, запуск, наблюдение
docker build -t dk-python:1 .
docker run -d --name dk-python-web -p 127.0.0.1:18088:8000 -e GREETING=python-ready dk-python:1
curl http://127.0.0.1:18088/
docker logs --tail 20 dk-python-web
docker exec dk-python-web id
docker stop dk-python-web
docker rm dk-python-webВ PowerShell используйте curl.exe. Ответ должен содержать {"message": "python-ready"}, а id — UID 10001. Перед первым HTTP-запросом дайте процессу запуститься; если запрос слишком ранний, повторите его. Значение GREETING передаётся при запуске; для его изменения сборка нового образа не нужна.
Когда появились внешние зависимости
Создайте requirements.txt с одной строкой packaging==25.0. Версия закреплена для учебного эксперимента, а не объявлена самой новой. Замените Dockerfile на вариант:
FROM python:3.13-slim
ENV PYTHONDONTWRITEBYTECODE=1 PYTHONUNBUFFERED=1
WORKDIR /app
COPY requirements.txt ./requirements.txt
RUN pip install --no-cache-dir -r requirements.txt
COPY --chown=10001:10001 app.py ./app.py
USER 10001:10001
EXPOSE 8000
CMD ["python", "app.py"]Соберите docker build -t dk-python:deps ., затем выполните docker run --rm dk-python:deps python -c "import packaging; print(packaging.__version__)". Ожидается 25.0. Установка зависимостей стоит до копирования исходника: изменение app.py не требует повторять pip install, если предыдущие входы неизменны. Для реального проекта фиксируют весь граф зависимостей и при необходимости хеши, а не только верхнеуровневые пакеты. Python-образ в Docker.
Ошибка и упражнение
Если сервер не открывается, проверьте docker ps -a, logs, адрес bind в Python и обе стороны -p. ModuleNotFoundError требует проверить пакет внутри образа, а не наличие библиотеки в вашей .venv. Ошибка PermissionError означает, что UID процесса не может писать в выбранный путь; не исправляйте её запуском всего приложения от root без разбора.
Добавьте поле path в JSON-ответ, соберите тег :2, запустите его на другом свободном порту и проверьте /lesson. Затем объясните, почему изменения app.py требуют сборки, а изменение GREETING — только нового контейнера с другой конфигурацией. Это две разные причины пересоздания среды.