Sari la conținutul principal
Instrumente Python2026-11-01 · 11 min de citit

Docker pentru Python în 2026: Ghidul de construcție în mai multe etape pentru uv modern

Deschideți orice tutorial vechi de cinci ani „Dockerize your Python app” și veți găsi aceleași patru rânduri: FROM python:3.11-slim, COPY requirements.txt ., RUN pip install -r requirements.txt, COPY . .. Funcționează. De asemenea, furnizează o imagine de peste 200 MB, se reconstruiește lent, deoarece fiecare instalare a dependenței este o pierdere de memorie cache completă în momentul în care se modifică o linie de cod al aplicației și trage în liniște un compilator C și fișiere de antet pe care containerul care rulează nu le va mai atinge niciodată după terminarea construcției.

Nimic din toate acestea nu mai este necesar. Între uv (managerul de pachete bazat pe Rust și instalatorul de la Astral), monturile cache BuildKit și imaginile de bază fără distribuție, implicit 2026 pentru un container Python de producție este o construcție în mai multe etape care furnizează doar un interpret Python, mediul tău virtual și codul tău, nimic altceva. Acest ghid prezintă modelul vechi, modelul nou și un fișier Docker pe care îl puteți copia într-un proiect real astăzi.

Vechiul model și de ce este scump

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

Acest model are trei costuri care se adaugă pe măsură ce un proiect crește. În primul rând, python:3.13-slim este livrat deja la aproximativ 150 până la 200 MB comprimat înainte de a adăuga o singură dependență, deoarece "slim" încă mai conține un userland complet Debian, apt și extensiile C ale bibliotecii standard Python. În al doilea rând, fiecare strat RUN pip install este stocat în cache de hash-ul de conținut Docker al straturilor precedente, deci schimbând o linie în app.py înainte de COPY . . nu invalidează nimic în amonte, dar o modificare a requirements.txt invalidează întregul pas de instalare și re-descărcă fiecare roată. În al treilea rând, dacă vreo dependență are nevoie de o construcție nativă (o roată manylinux lipsă, o versiune de pachet neobișnuită), Pip trage în liniște instrumentele de construcție care apoi rămân în imaginea finală pentru totdeauna, deoarece există doar o etapă în care să o aruncați.

Noul model: o etapă de constructor UV plus o etapă finală fără distribuție

Remedierea este o versiune în mai multe etape: o etapă care are un compilator, uv și fișierul dvs. de blocare și o a doua etapă, mult mai mică, care copiază doar mediul virtual terminat și arborele sursă. Etapa finală nu vede niciodată pip, uv, apt sau un compilator 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 nu are shell, nici manager de pachete și nici pip, ceea ce este exact ceea ce doriți în producție: nu mai este nimic în container pe care un atacator care primește execuția codului l-ar putea folosi pentru a introduce mai multe instrumente. Imaginea finală este pachetele de site ale mediului virtual, plus codul dvs., care pentru un serviciu tipic FastAPI ajunge cu mult sub 50 MB în loc de peste 200 MB.

Suporturi cache BuildKit: nu re-descărcați niciodată aceeași roată de două ori

Linia --mount=type=cache,target=/root/.cache/uv face treaba reală aici. Fără ea, fiecare docker build pe un nou runner re-descărcă fiecare roată din PyPI, deoarece cache-ul stratului supraviețuiește numai dacă straturile precedente, inclusiv COPY uv.lock, sunt identice la octeți. O montare în cache BuildKit este un volum persistent în afara graficului stratului: supraviețuiește peste versiunile de pe aceeași gazdă sau, cu un backend de cache de registry, peste runnerele CI, chiar și atunci când fișierul de blocare se modifică. Asociați-l cu uv sync --frozen, care refuză să atingă fișierul de blocare și eșuează cu voce tare dacă pyproject.toml și uv.lock nu sunt sincronizate, în loc să rezolve în tăcere un arbore de dependență diferit de cel cu care s-au executat testele dvs.

Împărțirea pasului uv sync --no-install-project de la finalul uv sync este cealaltă jumătate a trucului: primul apel instalează numai dependențe de la terți (se păstrează în cache atâta timp cât uv.lock nu se modifică), iar al doilea apel, rulează după COPY . /app, instalează propriul dvs. proiect fără a reatașa pachetele terțe. Schimbați o linie de cod al aplicației și stratul de dependență rămâne în cache.

Dependențe native: numpy, pillow și problema manylinux

Pachete precum numpy și Pillow ship au prefabricat multe roți Linux pentru platformele obișnuite, deci pe linux/amd64 sau linux/arm64 uv sync în mod normal descarcă o roată și nu atinge niciodată un compilator. Modul de eșec apare atunci când vă aflați pe o arhitectură neobișnuită, fixați o versiune foarte nouă înainte ca roțile să ajungă din urmă sau depindeți de un pachet care livrează doar o distribuție sursă. În acest caz, etapa de constructor are nevoie de instrumente de construcție, etapa finală nu trebuie să vadă niciodată:

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 (anteturi gcc, make, libc) trăiește numai în stadiul de constructor. Etapa finală fără distribuție copiază .venv deja compilat, niciodată compilatorul care l-a construit. Dacă doriți să exersați construirea și livrarea unui serviciu real containerizat cap la cap, cursul FastAPI parcurge exact acest tip de API, de la primul punct final până la o implementare în formă de producție.

Python gestionat prin UV: nu mai depindeți de interpretul livrat de imaginea de bază

python:3.13-slim vă oferă orice versiune de patch-uri pe care Debian a decis să livreze în acea săptămână. în schimb, uv poate gestiona interpretul în sine: uv python install 3.13.1 descarcă o versiune CPython specifică, reproductibilă din proiectul python-build-standalone și o fixează în .python-version, astfel încât etapa de constructor și laptopul rulează exact același interpret până la versiunea de corecție. Combinat cu UV_PYTHON_DOWNLOADS=never din Dockerfile, deci o versiune greșit fixată eșuează construcția cu voce tare în loc să descarce în tăcere ceva diferit, acest lucru închide golul „funcționează pe mașina mea, eșuează pe runner” pe care o etichetă de imagine de bază plutitoare o lasă deschisă.

Depanarea unui container fără shell

Singurul cost real al distroless: nu există un shell, așa că docker exec -it container sh nu funcționează. Pentru depanare locală, construiți același Dockerfile cu o țintă care se oprește în etapa de generare (docker build --target=builder -t myapp:debug .), care are încă userlandul complet al lui python:3.13-slim și un shell. Păstrați acea țintă de depanare în afara conductei de implementare; există pentru terminalul dvs., nu pentru producție.

Șablonul cu un singur fișier

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"]

Copiați acest lucru, schimbați numele modulului ENTRYPOINT pentru a se potrivi cu aspectul proiectului dvs. și aveți o construcție mai mică, mai rapidă la build-uri repetate și mai greu de atacat decât modelul pip install -r requirements.txt pe care încă îl predau majoritatea tutorialelor. Ești gata să exersezi backend-ul care merge în interiorul acestui container? Începeți cu FastAPI.

Întrebări frecvente

Am nevoie de BuildKit activat pentru ca montarea cache-ului să funcționeze?

Da, --mount=type=cache este o caracteristică doar pentru BuildKit. Acesta a fost generatorul implicit în Docker Desktop și versiunile recente ale Motorului Docker, dar pe aplicațiile CI mai vechi este posibil să aveți nevoie de DOCKER_BUILDKIT=1 setat în mod explicit sau docker buildx build în loc de docker build simplu.

De ce nu folosiți pur și simplu python:3.13-alpine pentru o imagine mai mică?

Musl libc de la Alpine încalcă ipoteza roții manylinux pe care se bazează numpy, Pillow și cele mai multe pachete compilate, așa că pierdeți roțile preconstruite și fie compilați din sursă, fie găsiți erori subtile de rulare. distroless-glibc evită această capcană.

Uv înlocuiește complet pip și Poetry?

Pentru majoritatea proiectelor noi, da. uv se rezolvă și se instalează mai repede, citește același pyproject.toml și generează un fișier de blocare, dar dacă o echipă depinde deja de pluginurile Poetry sau un flux de lucru uv nu acoperă încă, nu este urgent să migrezi la mijlocul proiectului. Încercați mai întâi uv pe un serviciu nou.

Ce se întâmplă dacă imaginea mea de bază are nevoie de un patch de securitate după ce am construit deja imaginea?

Reconstrui. Fixați un rezumat, nu doar o etichetă, pentru python:3.13-slim în stadiul de constructor și reconstruiți după un program, săptămânal este rezonabil pentru majoritatea echipelor, așa că o imagine de bază corespunzatoare curge într-o versiune nouă în loc să stea învechit luni de zile.

Pot folosi acest model pentru un proiect Django în loc de FastAPI?

Da, lui Dockerfile nu îi pasă ce cadru rulați; se modifică doar ENTRYPOINT final (de exemplu gunicorn myproject.wsgi:application). Cursul FastAPI este o modalitate rapidă de a exersa tiparul de la capăt la capăt dacă începeți un nou serviciu.

Este gata de producție distroless sau este încă experimental?

Este utilizat pe scară largă în producție; Google îl întreține și multe echipe îl livrează ca bază implicită Python. Compensația de a accepta intrarea este lipsa unui shell, care este o alegere deliberată de securitate, nu o eroare.

Ești gata să-l încerci?

15 lecții gratuite după o înscriere gratuită — fără card. Eșantionați platforma, apoi decideți.

Începe gratuit →

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.