跳轉到主要內容
Dockerfile Python 指令 兜成一個實際可用的 Python 應用 Dockerfile,重點在指令的順序

典型 Python 應用

# syntax=docker/dockerfile:1
FROM python:3.12-slim

WORKDIR /app

# 1. 先只複製相依清單
COPY requirements.txt .

# 2. 裝相依(這層只在 requirements.txt 變動時才重建)
RUN pip install --no-cache-dir -r requirements.txt

# 3. 最後才複製原始碼(改 .py 不會讓上面的 pip install 重跑)
COPY . .

EXPOSE 8000
CMD ["python", "app.py"]
為什麼先 COPY requirements.txtCOPY . . layer cache 的規則是「某層變了,後面所有層全部重建」(完整機制見 layer cache)。原始碼改動的頻率遠高於相依清單。如果先 COPY . .pip install,每改一個 .py 都會害 pip install 整個重跑(很慢)。把相依清單單獨先複製、先裝,pip install 那層就只在 requirements.txt 真的變動時才重建。 pip install --no-cache-dir 讓 pip 不把下載的 wheel 快取留進映像層,縮小 image。

用 multi-stage 縮小映像

要更小的映像,用 multi-stage build:在 builder stage 裝好相依進 venv,最終 stage 只複製 venv,build 工具不進最終映像:
# syntax=docker/dockerfile:1
FROM python:3.12-slim AS builder
WORKDIR /app
RUN python3 -m venv /venv
ENV PATH="/venv/bin:$PATH"
RUN --mount=type=cache,target=/root/.cache/pip \
    --mount=type=bind,source=requirements.txt,target=requirements.txt \
    pip install -r requirements.txt

FROM python:3.12-slim
WORKDIR /app
COPY --from=builder /venv /venv
ENV PATH="/venv/bin:$PATH"
COPY . .
EXPOSE 8000
CMD ["python", "-m", "uvicorn", "app:app", "--host=0.0.0.0", "--port=8000"]
  • FROM ... AS builder 命名 build stage,COPY --from=builder 從它複製產物。
  • --mount=type=cache,target=/root/.cache/pip 讓 pip 下載快取跨 build 持久化(不進映像層),重 build 不重複下載。
  • 最終映像只含 runtime 與 venv,沒有編譯工具,明顯更小。
需要編譯原生套件時,在 builder stage 補裝 build 工具即可,不會帶進最終映像:
FROM python:3.12-slim AS builder
RUN apt-get update && apt-get install -y --no-install-recommends \
    build-essential libpq-dev \
    && rm -rf /var/lib/apt/lists/*
# ... pip install ...

接下來

官方參考:docs.docker.com/guides/python/containerizemulti-stage build