> ## Documentation Index
> Fetch the complete documentation index at: https://felimet-hub.jmcores.com/llms.txt
> Use this file to discover all available pages before exploring further.

# 配合 Python 程式的 Dockerfile

> 典型 Python 應用的 Dockerfile、指令順序為何重要，以及用 multi-stage build 縮小映像。

`Dockerfile` `Python`

把 [指令](/notes/docker/dockerfile/instructions/) 兜成一個實際可用的 Python 應用 Dockerfile，重點在指令的**順序**。

## 典型 Python 應用

```dockerfile theme={null}
# 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.txt` 再 `COPY . .`？** layer cache 的規則是「某層變了，後面所有層全部重建」（完整機制見 [layer cache](/notes/docker/dockerfile/caching/)）。原始碼改動的頻率遠高於相依清單。如果先 `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 工具不進最終映像：

```dockerfile theme={null}
# 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 工具即可，不會帶進最終映像：

```dockerfile theme={null}
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 ...
```

## 接下來

* [選擇別人的映像](/notes/docker/dockerfile/images/)：`python:3.12-slim` 跟 alpine、完整版怎麼選。
* [Layer cache 與最佳實踐](/notes/docker/dockerfile/caching/)：快取機制與一份可抄的範本。

官方參考：[docs.docker.com/guides/python/containerize](https://docs.docker.com/guides/python/containerize/)、[multi-stage build](https://docs.docker.com/build/building/multi-stage/)
