Przejdź do treści głównej
Narzędzia Pythona2026-11-01 · 11 min czytania

Docker dla Pythona w 2026 r.: nowoczesny przewodnik po wieloetapowej kompilacji uv +

Otwórz dowolny pięcioletni samouczek „Dockerize your Python app”, a znajdziesz te same cztery linie: FROM python:3.11-slim, KOPIUJ wymagania.txt ., RUN pip install -r wymagania.txt, COPY . .. To działa. Dostarcza także obraz o wielkości ponad 200 MB, przebudowuje powoli, ponieważ każda instalacja zależności to pełna pamięć podręczna, pomija moment zmiany jednego wiersza kodu aplikacji i cicho przeciąga kompilator C i pliki nagłówkowe, których działający kontener nigdy więcej nie dotknie po zakończeniu kompilacji.

Nic z tego nie jest już potrzebne. Pomiędzy uv (menedżer pakietów oparty na Rust i instalator z Astral), mocowania pamięci podręcznej BuildKit i obrazy bazowe bez dystrybucji, domyślnym kontenerem produkcyjnym Pythona na rok 2026 jest wieloetapowa kompilacja, która zawiera tylko interpreter Pythona, twoje środowisko wirtualne i kod, nic więcej. W tym przewodniku omówiono stary i nowy wzorzec oraz plik Dockerfile, który można już dziś skopiować do prawdziwego projektu.

Stary wzór i dlaczego jest drogi

DOCKERFILE
FROM python:3.13-slim WORKDIR /app COPY requirements.txt . RUN pip install -r requirements.txt COPY . . CMD ["python", "app.py"]

Ten wzór ma trzy koszty, które zwiększają się w miarę rozwoju projektu. Po pierwsze, python:3.13-slim jest już dostarczany w ilości około 150 do 200MB skompresowanej przed dodaniem pojedynczej zależności, ponieważ python:3.13-slim nadal zawiera pełny obszar użytkownika Debiana, apt i rozszerzenia C standardowej biblioteki Pythona. Po drugie, każda warstwa RUN pip install jest buforowana przez skrót zawartości Dockera z poprzednich warstw, więc zmieniamy jedną linię w app.py przed COPY'. . nie unieważnia niczego wcześniej, ale zmiana w pliku requirements.txt unieważnia cały etap instalacji i powoduje ponowne pobranie każdego koła. Po trzecie, jeśli jakakolwiek zależność wymaga natywnej kompilacji (brak koła ManyLinux, nietypowa wersja pakietu), pip cicho pobiera narzędzia do kompilacji, które następnie pozostają w ostatecznym obrazie na zawsze, ponieważ jest tylko jeden etap, w którym można je wyrzucić.

Nowy wzór: etap konstruktora UV plus etap końcowy bez dystrybucji

Poprawka jest kompilacją wieloetapową: jeden etap zawiera kompilator, UV i plik blokujący, a drugi, znacznie mniejszy etap, który kopiuje tylko gotowe środowisko wirtualne i drzewo źródłowe. W ostatnim etapie nigdy nie pojawia się kompilator pip, uv, apt ani C.

DOCKERFILE
FROM python:3.13-slim AS builder COPY --from=ghcr.io/astral-sh/uv:latest /uv /uvx /bin/ ENV UV_COMPILE_BYTECODE=1 UV_LINK_MODE=copy UV_PYTHON_DOWNLOADS=never WORKDIR /app RUN --mount=type=cache,target=/root/.cache/uv \ --mount=type=bind,source=uv.lock,target=uv.lock \ --mount=type=bind,source=pyproject.toml,target=pyproject.toml \ uv sync --frozen --no-install-project --no-dev COPY . /app RUN --mount=type=cache,target=/root/.cache/uv uv sync --frozen --no-dev FROM gcr.io/distroless/python3-debian12 COPY --from=builder /app /app WORKDIR /app ENV PATH="/app/.venv/bin:$PATH" ENTRYPOINT ["/app/.venv/bin/python", "-m", "app"]

gcr.io/distroless/python3-debian12 nie ma powłoki, menedżera pakietów ani pipa, czyli dokładnie tego, czego chcesz w środowisku produkcyjnym: w kontenerze nie pozostało nic, co atakujący, uzyskując wykonanie kodu, mógłby wykorzystać do ściągnięcia większej liczby narzędzi. Ostateczny obraz to pakiety witryn Twojego środowiska wirtualnego oraz kod, który w przypadku typowej usługi FastAPI zajmuje znacznie poniżej 50 MB zamiast ponad 200 MB.

Montaż pamięci podręcznej BuildKit: nigdy nie pobieraj ponownie tego samego koła dwa razy

Linia --mount=type=cache,target=/root/.cache/uv wykonuje tutaj prawdziwą pracę. Bez tego każda „kompilacja dokera” na świeżym runnerze powoduje ponowne pobranie każdego koła z PyPI, ponieważ pamięć podręczna warstw przetrwa tylko wtedy, gdy poprzednie warstwy, w tym „COPY uv.lock”, są identyczne pod względem bajtów. Montowanie pamięci podręcznej BuildKit to trwały wolumin poza wykresem warstwy: przetrwa między kompilacjami na tym samym hoście lub, w przypadku zaplecza pamięci podręcznej rejestru, między modułami CI, nawet gdy plik blokady ulegnie zmianie. Połącz to z uv sync --frozen, które odmawia dotknięcia pliku blokującego i głośno kończy się niepowodzeniem, jeśli pyproject.toml i uv.lock nie są zsynchronizowane, zamiast po cichu rozwiązywać inne drzewo zależności niż to, na którym przeprowadzono testy.

Oddzielenie kroku uv sync --no-install-project od końcowego uv sync to druga połowa sukcesu: pierwsze wywołanie instaluje tylko zależności stron trzecich (w pamięci podręcznej, o ile uv.lock nie ulega zmianie), a drugie wywołanie jest uruchamiane po COPY . /app, instaluje własny projekt bez zmiany pakietów innych firm. Zmień jedną linię kodu aplikacji, a warstwa zależności pozostanie w pamięci podręcznej.

Zależności natywne: numpy, poduszka i problem z wieloma linuksami

Pakiety takie jak numpy i Pillow dostarczają wstępnie zbudowane koła z wieloma linuksami dla popularnych platform, więc na linux/amd64 lub linux/arm64 uv Sync zwykle po prostu pobiera koło i nigdy nie dotyka kompilatora. Tryb awarii pojawia się, gdy korzystasz z nietypowej architektury, przypinasz bardzo nową wersję, zanim koła nadrobią zaległości lub zależysz od pakietu, który dostarcza tylko dystrybucję źródłową. W takim przypadku na etapie konstruktora potrzebne są narzędzia do budowania, na etapie końcowym nie można nigdy zobaczyć:

DOCKERFILE
FROM python:3.13-slim AS builder RUN apt-get update && apt-get install -y --no-install-recommends build-essential \ && rm -rf /var/lib/apt/lists/* # uv + sync steps as in the stage above

build-essential (nagłówki gcc, make, libc) istnieje tylko na etapie konstruktora. Końcowy etap bez dystrybucji kopiuje już skompilowany plik .venv, a nie kompilator, który go zbudował. Jeśli chcesz przećwiczyć budowanie i dostarczanie od początku do końca prawdziwej usługi kontenerowej, kurs FastAPI omawia dokładnie ten rodzaj interfejsu API, od pierwszego punktu końcowego po wdrożenie w środowisku produkcyjnym.

Python zarządzany przez UV: zatrzymaj się w zależności od interpretera dostarczonego obrazu bazowego

python:3.13-slim zawiera dowolną wersję łatki, którą Debian zdecydował się wydać w tym tygodniu. uv może zamiast tego zarządzać samym interpreterem: uv python install 3.13.1 pobiera konkretną, odtwarzalną kompilację CPython z projektu python-build-standalone i przypina ją do .python-version, dzięki czemu etap budowania i twój laptop uruchamiają dokładnie ten sam interpreter aż do wersji łatki. W połączeniu z „UV_PYTHON_DOWNLOADS=never” w pliku Dockerfile, więc błędnie przypięta wersja powoduje głośne niepowodzenie kompilacji zamiast cichego pobierania czegoś innego, zamyka to lukę „działa na moim komputerze, kończy się niepowodzeniem na runnerze”, którą pozostawia otwarty tag pływającego obrazu podstawowego.

Debugowanie kontenera bez powłoki

Jedyny prawdziwy koszt distroless: nie ma powłoki, więc docker exec -it Container sh nie działa. W przypadku lokalnego debugowania zbuduj ten sam plik Dockerfile z elementem docelowym, który zatrzymuje się na etapie konstruktora (docker build --target=builder -t myapp:debug .), który nadal zawiera pełny obszar użytkownika i powłokę python:3.13-slim. Trzymaj ten cel debugowania poza potokiem wdrażania; istnieje dla twojego terminala, a nie dla produkcji.

Szablon jednoplikowy

DOCKERFILE
FROM python:3.13-slim AS builder COPY --from=ghcr.io/astral-sh/uv:latest /uv /uvx /bin/ ENV UV_COMPILE_BYTECODE=1 UV_LINK_MODE=copy UV_PYTHON_DOWNLOADS=never WORKDIR /app RUN --mount=type=cache,target=/root/.cache/uv \ --mount=type=bind,source=uv.lock,target=uv.lock \ --mount=type=bind,source=pyproject.toml,target=pyproject.toml \ uv sync --frozen --no-install-project --no-dev COPY . /app RUN --mount=type=cache,target=/root/.cache/uv uv sync --frozen --no-dev FROM gcr.io/distroless/python3-debian12 COPY --from=builder /app /app WORKDIR /app ENV PATH="/app/.venv/bin:$PATH" ENTRYPOINT ["/app/.venv/bin/python", "-m", "app"]

Skopiuj to, zmień nazwę modułu ENTRYPOINT, aby pasowała do układu projektu, a otrzymasz kompilację, która jest mniejsza, szybsza przy powtarzanych kompilacjach i trudniejsza do ataku niż wzorzec pip install -r wymagania.txt, którego uczy większość samouczków. Gotowy do przećwiczenia backendu znajdującego się w tym kontenerze? Zacznij od FastAPI.

Często zadawane pytania

Czy muszę mieć włączoną wersję BuildKit, aby montowanie pamięci podręcznej działało?

Tak, --mount=type=cache jest funkcją dostępną tylko w BuildKit. Był to domyślny kreator w Docker Desktop i najnowszych wydaniach Docker Engine, ale w starszych modułach CI może być konieczne jawne ustawienie DOCKER_BUILDKIT=1 lub docker buildx build zamiast zwykłej docker buildx build.

Dlaczego nie użyć po prostu Pythona:3.13-alpine do mniejszego obrazu?

Biblioteka musl libc firmy Alpine łamie założenie koła wielu linuksów, na którym opierają się numpy, Pillow i większość skompilowanych pakietów, więc tracisz gotowe koła i albo kompilujesz ze źródła, albo napotykasz subtelne błędy w czasie wykonywania. distroless-glibc pozwala uniknąć tej pułapki.

Czy UV całkowicie zastępuje pip i poezję?

W przypadku większości nowych projektów tak. uv rozwiązuje i instaluje szybciej, czyta ten sam plik pyproject.toml i generuje plik blokujący, ale jeśli zespół już korzysta z wtyczek Poetry lub przepływ pracy nie jest jeszcze objęty uv, migracja w trakcie projektu nie jest pilna. Wypróbuj najpierw uv w nowej usłudze.

Co się stanie, jeśli mój obraz podstawowy będzie wymagał poprawki zabezpieczeń po tym, jak już go zbudowałem?

Odbudować. Przypnij podsumowanie, a nie tylko tag, dla python:3.13-slim na etapie konstruktora i przebudowuj zgodnie z harmonogramem, co tydzień jest rozsądne dla większości zespołów, więc poprawiony obraz podstawowy trafia do nowej kompilacji, zamiast czekać miesiące.

Czy mogę użyć tego wzorca w projekcie Django zamiast FastAPI?

Tak, plik Dockerfile nie dba o to, który framework uruchomisz; zmienia się tylko końcowy ENTRYPOINT (na przykład gunicorn myproject.wsgi:application). Kurs FastAPI to szybki sposób na przećwiczenie wzorca od początku do końca, jeśli rozpoczynasz nową usługę.

Czy dystrybucja jest już gotowa do produkcji, czy nadal jest w fazie eksperymentalnej?

Jest szeroko stosowany w produkcji; Google go utrzymuje i wiele zespołów dostarcza go jako domyślną bazę Pythona. Kompromisem związanym z zaakceptowaniem wejścia jest brak powłoki, co jest świadomym wyborem bezpieczeństwa, a nie błędem.

Gotowy, aby spróbować?

15 lekcji gratis po darmowej rejestracji – bez karty. Wypróbuj platformę i podejmij decyzję.

Zacznij bezpłatnie →

Or unlock every lesson

Pro — $12/mo, unlimited AI, cancel any time. Refund within 14 days worldwide.

See Pro plans →

Get one Python lesson + one career idea every Friday

No spam, no "buy our course now". Three bullets, every Friday. Unsubscribe with one click.