Μετάβαση στο κύριο περιεχόμενο
Python Tooling2026-11-01 · 11 λεπτά ανάγνωσης

Docker για Python το 2026: Οδηγός κατασκευής σύγχρονου uv + πολλαπλών σταδίων

Ανοίξτε οποιονδήποτε πενταετές σεμινάριο "Dockerize your Python app" και θα βρείτε τις ίδιες τέσσερις γραμμές: "FROM python:3.11-slim", "COPY requires.txt .", "RUN pip install -r requirements.txt", "COPY . .`. Λειτουργεί. Αποστέλλει επίσης μια εικόνα 200 MB και πλέον, ανακατασκευάζεται αργά, επειδή κάθε εγκατάσταση εξάρτησης είναι μια πλήρης μνήμη cache που χάνεται τη στιγμή που αλλάζει μια γραμμή κώδικα εφαρμογής και σέρνει αθόρυβα σε έναν μεταγλωττιστή C και αρχεία κεφαλίδας που το τρέχον κοντέινερ δεν θα αγγίξει ποτέ ξανά μετά την ολοκλήρωση της κατασκευής.

Τίποτα από αυτά δεν είναι πλέον απαραίτητο. Μεταξύ του uv (ο διαχειριστής πακέτων που βασίζεται σε Rust και του προγράμματος εγκατάστασης από την Astral), των μονταρισμάτων προσωρινής μνήμης BuildKit και των εικόνων βάσης χωρίς διανομή, η προεπιλογή του 2026 για ένα κοντέινερ Python παραγωγής είναι μια έκδοση πολλαπλών σταδίων που αποστέλλει μόνο έναν διερμηνέα Python, το εικονικό σας περιβάλλον και τον κώδικά σας, τίποτα άλλο. Αυτός ο οδηγός περιγράφει το παλιό μοτίβο, το νέο μοτίβο και ένα αρχείο Docker που μπορείτε να αντιγράψετε σε ένα πραγματικό έργο σήμερα.

Το παλιό μοτίβο και γιατί είναι ακριβό

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

Αυτό το μοτίβο έχει τρία κόστη που επιδεινώνονται καθώς μεγαλώνει ένα έργο. Πρώτον, το "python:3.13-slim" αποστέλλεται ήδη σε συμπιεσμένα περίπου 150 έως 200 MB προτού προσθέσετε μία εξάρτηση, επειδή το "slim" εξακολουθεί να φέρει μια πλήρη χώρα χρήστη Debian, apt και τις επεκτάσεις C της τυπικής βιβλιοθήκης Python. Δεύτερον, κάθε επίπεδο "RUN pip install" αποθηκεύεται προσωρινά από τον κατακερματισμό περιεχομένου του Docker των προηγούμενων επιπέδων, επομένως αλλάζοντας μία γραμμή στο "app.py" πριν από το "COPY . Το . δεν ακυρώνει τίποτα ανάντη, αλλά μια αλλαγή σε requirements.txt` ακυρώνει ολόκληρο το βήμα εγκατάστασης και κατεβάζει ξανά κάθε τροχό. Τρίτον, εάν οποιαδήποτε εξάρτηση χρειάζεται μια εγγενή κατασκευή (ένας τροχός πολλών Linux που λείπει, μια ασυνήθιστη έκδοση πακέτου), το pip τραβάει αθόρυβα το εργαλείο κατασκευής που στη συνέχεια παραμένει στην τελική εικόνα για πάντα, επειδή υπάρχει μόνο ένα στάδιο για να το πετάξετε.

Το νέο μοτίβο: ένα στάδιο δημιουργίας υπεριώδους ακτινοβολίας συν ένα τελικό στάδιο χωρίς διανομή

Η επιδιόρθωση είναι μια κατασκευή πολλών σταδίων: ένα στάδιο που έχει μεταγλωττιστή, uv και το lockfile σας και ένα δεύτερο, πολύ μικρότερο στάδιο που αντιγράφει μόνο το τελειωμένο εικονικό περιβάλλον και το δέντρο προέλευσης. Το τελικό στάδιο δεν βλέπει ποτέ pip, uv, apt ή μεταγλωττιστή 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 δεν έχει κέλυφος, διαχειριστή πακέτων και pip, κάτι που ακριβώς θέλετε στην παραγωγή: δεν υπάρχει τίποτα στο κοντέινερ που θα μπορούσε να χρησιμοποιήσει ένας εισβολέας που λαμβάνει εκτέλεση κώδικα για να αντλήσει περισσότερα εργαλεία. Η τελική εικόνα είναι τα πακέτα ιστοτόπων του εικονικού σας περιβάλλοντος συν τον κώδικά σας, ο οποίος για μια τυπική υπηρεσία FastAPI βρίσκεται πολύ κάτω από 50 MB αντί για 200 MB πλέον.

Βάσεις κρυφής μνήμης BuildKit: μην επαναλάβετε ποτέ τον ίδιο τροχό δύο φορές

Η γραμμή --mount=type=cache,target=/root/.cache/uv κάνει την πραγματική δουλειά εδώ. Χωρίς αυτό, κάθε «κατασκευή docker» σε ένα νέο runner κατεβάζει ξανά κάθε τροχό από το PyPI, επειδή η κρυφή μνήμη στρώματος επιβιώνει μόνο εάν τα προηγούμενα επίπεδα, συμπεριλαμβανομένου του «COPY uv.lock», είναι πανομοιότυπα byte. Μια προσάρτηση προσωρινής μνήμης BuildKit είναι ένας μόνιμος τόμος εκτός του γραφήματος επιπέδων: επιβιώνει σε όλες τις εκδόσεις στον ίδιο κεντρικό υπολογιστή ή, με ένα backend κρυφής μνήμης μητρώου, σε όλους τους δρομείς CI, ακόμη και όταν αλλάζει το αρχείο κλειδώματος. Συνδυάστε το με το "uv sync --frozen", το οποίο αρνείται να αγγίξει το lockfile και αποτυγχάνει δυνατά εάν τα "pyproject.toml" και "uv.lock" δεν είναι συγχρονισμένα, αντί να επιλύσετε σιωπηλά ένα διαφορετικό δέντρο εξάρτησης από αυτό που αντιμετώπισαν οι δοκιμές σας.

Ο διαχωρισμός του βήματος «uv sync --no-install-project» από το τελικό «uv sync» είναι το άλλο μισό του κόλπου: η πρώτη κλήση εγκαθιστά μόνο εξαρτήσεις τρίτου μέρους (αποθηκεύεται στην προσωρινή μνήμη εφόσον το «uv.lock» δεν αλλάζει) και η δεύτερη κλήση εκτελείται μετά το «COPY ». /app`, εγκαθιστά το δικό σας έργο χωρίς να αγγίξετε ξανά πακέτα τρίτων. Αλλάξτε μία γραμμή κώδικα εφαρμογής και το επίπεδο εξάρτησης παραμένει προσωρινά αποθηκευμένο.

Εγγενείς εξαρτήσεις: numpy, pillow και το πρόβλημα manylinux

Πακέτα όπως το numpy και το Pillow διαθέτουν προκατασκευασμένους τροχούς πολλών Linux για τις κοινές πλατφόρμες, επομένως στο linux/amd64 ή στο linux/arm64 uv sync συνήθως απλώς κατεβάζει έναν τροχό και δεν αγγίζει ποτέ έναν μεταγλωττιστή. Η λειτουργία αποτυχίας εμφανίζεται όταν βρίσκεστε σε μια ασυνήθιστη αρχιτεκτονική, καρφιτσώνετε μια πολύ νέα έκδοση πριν προλάβουν οι τροχοί ή εξαρτάστε από ένα πακέτο που αποστέλλει μόνο μια διανομή πηγής. Σε αυτήν την περίπτωση, το στάδιο οικοδόμησης χρειάζεται εργαλεία κατασκευής, το τελικό στάδιο δεν πρέπει ποτέ να δει:

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" (gcc, make, libc headers) ζει μόνο στο στάδιο του builder. Το τελικό στάδιο χωρίς διανομή αντιγράφει το ήδη μεταγλωττισμένο .venv, ποτέ τον μεταγλωττιστή που το κατασκεύασε. Εάν θέλετε να εξασκηθείτε στη δημιουργία και την αποστολή μιας πραγματικής υπηρεσίας με εμπορευματοκιβώτια από άκρο σε άκρο, το μάθημα FastAPI ακολουθεί ακριβώς αυτό το είδος API, από το πρώτο τελικό σημείο σε μια ανάπτυξη σε σχήμα παραγωγής.

Python με διαχείριση uv: σταματήστε ανάλογα με τον διερμηνέα που απέστειλε η βασική εικόνα

Το "python:3.13-slim" σάς παρέχει οποιαδήποτε έκδοση ενημερωμένης έκδοσης κώδικα αποφάσισε να διαθέσει το Debian εκείνη την εβδομάδα. Το uv μπορεί να διαχειριστεί τον ίδιο τον διερμηνέα: Η "uv python install 3.13.1" κατεβάζει μια συγκεκριμένη, αναπαραγώγιμη έκδοση CPython από το python-build-standalone project και την καρφιτσώνει στο ".python-version", έτσι ώστε το στάδιο του builder και ο φορητός υπολογιστής σας να εκτελούν τον ίδιο ακριβώς διερμηνέα στην έκδοση ενημέρωσης κώδικα. Σε συνδυασμό με το «UV_PYTHON_DOWNLOADS=never» στο Dockerfile, έτσι ώστε μια λανθασμένα καρφιτσωμένη έκδοση αποτυγχάνει δυνατά στο build αντί να κατεβάζει σιωπηλά κάτι διαφορετικό, αυτό κλείνει το κενό "works on my machine, fails on the runner" που αφήνει ανοιχτή μια αιωρούμενη βασική ετικέτα εικόνας.

Εντοπισμός σφαλμάτων ενός κοντέινερ χωρίς κέλυφος

Το ένα πραγματικό κόστος του distroless: δεν υπάρχει κέλυφος, επομένως το «docker exec -it container sh» δεν λειτουργεί. Για τοπικό εντοπισμό σφαλμάτων, δημιουργήστε το ίδιο Dockerfile με έναν στόχο που σταματά στο στάδιο του builder (docker build --target=builder -t myapp:debug .), το οποίο εξακολουθεί να έχει το πλήρες userland του python:3.13-slim και ένα κέλυφος. Κρατήστε αυτόν τον στόχο εντοπισμού σφαλμάτων εκτός του αγωγού ανάπτυξης. υπάρχει για το τερματικό σας, όχι για παραγωγή.

Το πρότυπο ενός αρχείου

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

Αντιγράψτε το, αλλάξτε το όνομα της ενότητας «ENTRYPOINT» ώστε να ταιριάζει με τη διάταξη του έργου σας και θα έχετε μια έκδοση που είναι μικρότερη, ταχύτερη σε επαναλαμβανόμενες εκδόσεις και πιο δύσκολη στην επίθεση από το μοτίβο «pip install -r requires.txt» που εξακολουθούν να διδάσκουν τα περισσότερα σεμινάρια. Είστε έτοιμοι να εξασκήσετε το backend που μπαίνει μέσα σε αυτό το κοντέινερ; Ξεκινήστε με το FastAPI.

Συχνές ερωτήσεις

Χρειάζομαι ενεργοποιημένο το BuildKit για να λειτουργήσει η προσάρτηση προσωρινής μνήμης;

Ναι, το "--mount=type=cache" είναι μια δυνατότητα μόνο για το BuildKit. Ήταν το προεπιλεγμένο πρόγραμμα δημιουργίας στο Docker Desktop και στις πρόσφατες εκδόσεις του Docker Engine, αλλά σε παλαιότερους εκτελούντες CI ενδέχεται να χρειαστεί να ορίσετε ρητά το "DOCKER_BUILDKIT=1" ή το "docker buildx build" αντί για το απλό "docker build".

Γιατί να μην χρησιμοποιήσετε απλώς το python:3.13-alpine για μικρότερη εικόνα;

Το musl libc της Alpine καταρρίπτει την υπόθεση του τροχού manylinux στην οποία βασίζονται τα numpy, το Pillow και τα περισσότερα μεταγλωττισμένα πακέτα, έτσι χάνετε τους προκατασκευασμένους τροχούς και είτε μεταγλωττίζετε από την πηγή είτε αντιμετωπίζετε ανεπαίσθητα σφάλματα χρόνου εκτέλεσης. Το distroless-glibc αποφεύγει αυτήν την παγίδα.

Το uv αντικαθιστά πλήρως το pip και το Poetry;

Για τα περισσότερα νέα έργα, ναι. Το uv επιλύεται και εγκαθίσταται γρηγορότερα, διαβάζει το ίδιο «pyproject.toml» και δημιουργεί ένα lockfile, αλλά εάν μια ομάδα εξαρτάται ήδη από προσθήκες Poetry ή μια ροή εργασίας δεν καλύπτει ακόμη το uv, δεν υπάρχει επείγουσα ανάγκη για μετεγκατάσταση στο μέσο του έργου. Δοκιμάστε πρώτα το uv σε μια νέα υπηρεσία.

Τι γίνεται αν η βασική μου εικόνα χρειάζεται μια ενημερωμένη έκδοση κώδικα ασφαλείας αφού έχω ήδη δημιουργήσει την εικόνα;

Ανοικοδομώ. Καρφιτσώστε μια ανακεφαλαίωση, όχι απλώς μια ετικέτα, για το «python:3.13-slim» στο στάδιο δημιουργίας και η ανακατασκευή με χρονοδιάγραμμα, εβδομαδιαία είναι λογική για τις περισσότερες ομάδες, επομένως μια διορθωμένη εικόνα βάσης ρέει σε μια νέα έκδοση αντί να παραμένει μπαγιάτικη για μήνες.

Μπορώ να χρησιμοποιήσω αυτό το μοτίβο για ένα έργο Django αντί για το FastAPI;

Ναι, το Dockerfile δεν ενδιαφέρεται ποιο πλαίσιο εκτελείτε. αλλάζει μόνο το τελικό «ENTRYPOINT» (για παράδειγμα «gunicorn myproject.wsgi:application»). Το μάθημα FastAPI είναι ένας γρήγορος τρόπος για να εξασκήσετε το μοτίβο από άκρο σε άκρο εάν ξεκινάτε μια νέα υπηρεσία.

Είναι έτοιμη η παραγωγή distroless ή είναι ακόμα πειραματική;

Χρησιμοποιείται ευρέως στην παραγωγή. Η Google το διατηρεί και πολλές ομάδες το αποστέλλουν ως προεπιλεγμένη βάση Python. Το αντάλλαγμα για να αποδεχτείτε την είσοδο είναι η έλλειψη ενός κελύφους, το οποίο είναι μια σκόπιμη επιλογή ασφάλειας, όχι ένα σφάλμα.

Είστε έτοιμοι να το δοκιμάσετε;

15 μαθήματα δωρεάν μετά από δωρεάν εγγραφή — χωρίς κάρτα. Δοκιμάστε την πλατφόρμα και μετά αποφασίστε.

Ξεκινήστε δωρεάν →

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.