TGViewer
About Python [ru] About Python [ru] @python_tesst · 6.44K subscribers
Post #2750 474
Детерминированная сборка Python-контейнера: это когда образ из одного и того же коммита сегодня и через месяц получает один и тот же набор зависимостей.

pip install -r requirements.txt сам по себе такого не обещает. Может уехать транзитивная зависимость. Может появиться другой wheel под вашу платформу. Может внезапно собраться sdist. Может поменяться Python в base image или состояние package index.

Рабочая схема выглядит так:

1. pyproject.toml описывает намерения.
2. uv.lock фиксирует конкретное разрешение зависимостей.
3. wheelhouse фиксирует installable-артефакты.
4. Runtime-стадия ставит зависимости без доступа к индексу.

Пример Dockerfile:

FROM python:3.12-slim AS wheels

COPY --from=ghcr.io/astral-sh/uv:latest /uv /usr/local/bin/uv

WORKDIR /app
COPY pyproject.toml uv.lock ./

RUN uv export \
--frozen \
--no-dev \
--no-hashes \
--format requirements-txt \
-o requirements.txt

RUN python -m pip wheel \
--requirement requirements.txt \
--wheel-dir /wheelhouse \
--only-binary=:all:

FROM python:3.12-slim AS runtime

WORKDIR /app

COPY --from=wheels /wheelhouse /wheelhouse
COPY --from=wheels /app/requirements.txt /requirements.txt

RUN python -m pip install \
--no-index \
--find-links=/wheelhouse \
--requirement /requirements.txt \
&& rm -rf /wheelhouse

COPY . .

CMD ["python", "-m", "app"]


На что я бы тут смотрел в первую очередь.

uv export --frozen не обновляет lock-файл. И это хорошо. Если pyproject.toml и uv.lock разъехались, сборка должна упасть, а не молча «починить» зависимости прямо внутри Docker build.

wheelhouse убирает из runtime-сборки режим «сходить в интернет и скачать что получится». Вместо этого pip ставит заранее подготовленные артефакты. Runtime-слой уже не зависит от PyPI, зеркала, yanked-релизов и сетевых флуктуаций.

--only-binary=:all: тоже не случайная опция. Она запрещает внезапную сборку из sdist. Если пакет требует компиляции, лучше явно вынести это в controlled build-стадию, чем потом ловить разные wheel из-за версии компилятора, системных библиотек или base image.

Что ещё помогает против дрейфа:

- коммитить uv.lock;
- в CI проверять установку через uv sync --locked или сборку через uv export --frozen;
- не запускать uv lock внутри Docker build как часть обычной сборки;
- пиновать base image не только по тегу, но и по digest;
- собирать wheelhouse под тот же Python minor, ABI и семейство образа, что и runtime;
- финальную установку делать с --no-index;
- хранить wheelhouse как CI-артефакт или собирать его строго из lock-файла.

Я это обычно делю на два слоя. Lock-файл защищает resolution layer: какие версии выбрали. Wheelhouse защищает artifact layer: какие именно файлы потом установили. Если нужен не «примерно воспроизводимый» контейнер, а контролируемая сборка, нужны оба уровня.
More from @python_tesst
  1. Sep 29, 2026Claude Sonnet 5.5 — уже не слив, релиз официально состоялся Прайс не трогали: $2 за миллио…
  2. Sep 29, 2026Post #3194
  3. Sep 29, 2026Дженсен Хуанг выкатил NVIDIA Open Agent Safety Platform — и притащил с собой 100+ партнеро…
  4. Sep 29, 2026Стикмену теперь под силу снести любой сайт. Чувак под это навайбкодил целую игруху: грузиш…
  5. Sep 27, 2026OpenAI метит в подписку за 500 баксов Что там в описании тарифа? Пока что от ChatGPT Pro о…
  6. Sep 27, 2026Свежая обложка The Economist подъехала Журналисты: да мы вообще не сгущаем краски Те же жу…
Threads Profile ViewerView any public Threads profile without an account.Open ThreadLook →Writing with AI? Make it sound human.Metric37 rewrites AI drafts so they read naturally. Free AI detector, 1,500 words free.Try Metric37 →