# Образ генератора: тот самый «канонический контейнер», внутри которого спека # обещает побайтовую воспроизводимость (раздел 2). Отсюда правило: пакеты # ставятся только из `uv.lock` — второй список зависимостей писал бы на стенде # не те байты, что сторожит `make test`. # # Контекст сборки — корень репозитория, а не `generator/`: в образ едет ещё и # каталог товаров. # База записана здесь и прибита к patch-версии: это канонический контейнер из # раздела 2 спеки, и его база входит в тот же фиксированный набор, что `uv.lock`. # Из `.env` её не берём — иначе описание воспроизводимой сборки распалось бы # между Dockerfile и обвязкой запуска. FROM python:3.14.7-slim COPY --from=ghcr.io/astral-sh/uv:0.11.21 /uv /bin/uv # Раскладка внутри образа повторяет раскладку репозитория: `/app` — его корень. # Так надо не для красоты. Каталог товаров генератор ищет от своего модуля # вверх по дереву (`catalog.py`, `parents[3]`): каталог — часть мира, а не # запуска, и снаружи не настраивается (решение #39). Значит, взаимное # расположение пакета и `data/` обязано сохраниться, иначе первый же импорт # каталога упрётся в отсутствующий файл. WORKDIR /app/generator ENV UV_LINK_MODE=copy \ UV_PYTHON_DOWNLOADS=never # Зависимости — своим слоем: он пересобирается, только когда меняется лок. # `--extra kafka` ставит клиента, которым живёт приёмник Kafka; `--no-dev` # оставляет за бортом pytest, ruff и ty — их дом на машине разработчика. COPY generator/pyproject.toml generator/uv.lock ./ RUN uv sync --frozen --no-dev --no-install-project --extra kafka # Пакет в `.venv` не ставится: он лежит исходником и виден через `PYTHONPATH`. # Поставленный не editable, он переехал бы в `site-packages`, и те же # `parents[3]` указали бы внутрь `.venv` — каталог в образе есть, а модуль его # не находит. Заодно правка кода не трогает слой зависимостей. COPY generator/src ./src COPY data /app/data ENV PATH="/app/generator/.venv/bin:$PATH" \ PYTHONPATH="/app/generator/src" \ PYTHONUNBUFFERED=1 # Не root: генератору хватает права читать своё и писать в сеть. RUN useradd --create-home --uid 1000 generator USER generator # Параметры прогона передаёт зовущий — аргументами или окружением; итог он # читает кодом возврата, а логи идут в стандартный вывод. ENTRYPOINT ["python", "-m", "clickstream_generator"]