一. 前言 #
你有沒有遇過這種悲劇:在自己的電腦上跑得好好的 Python 程式,部署到伺服器或同事的電腦上就各種炸裂?🤯
ModuleNotFoundError: No module named 'pandas'
ERROR: Python 3.12 is required but found 3.9
「在我的電腦上可以跑啊!」—— 這句話大概是所有工程師最常說的名言了。
Docker 就是為了解決這個問題而生的!它把你的程式碼、Python 版本、所有相依套件、甚至作業系統環境,全部打包成一個容器(Container)。不管在哪台機器上執行,結果都一模一樣。
今天拍拍君就來帶大家從零開始學 Docker,讓你的 Python 專案從此告別「在我電腦上可以跑」的噩夢!🐳
二. 安裝 Docker #
macOS #
最簡單的方式是安裝 Docker Desktop:
# 用 Homebrew 安裝
brew install --cask docker
安裝完成後,打開 Docker Desktop 應用程式,等到狀態列的小鯨魚圖示不再跳動就代表 Docker daemon 已經啟動。
Linux (Ubuntu/Debian) #
# 官方安裝腳本(最快的方式)
curl -fsSL https://get.docker.com | sh
# 讓你的使用者不用每次都 sudo
sudo usermod -aG docker $USER
安裝完成後記得登出再登入,讓群組權限生效。
驗證安裝 #
docker --version
# Docker version 27.5.1, build 9f9e405
docker run hello-world
# Hello from Docker! 🎉
如果看到 Hello from Docker! 就代表安裝成功了!
三. Docker 基本概念 #
在開始寫 Dockerfile 之前,先來搞清楚幾個核心概念:
| 概念 | 說明 | 比喻 |
|---|---|---|
| Image(映像檔) | 應用程式的快照,包含所有需要的檔案 | 🍰 蛋糕食譜 |
| Container(容器) | Image 的執行實例 | 🎂 用食譜做出來的蛋糕 |
| Dockerfile | 建構 Image 的指令檔 | 📝 寫食譜的步驟 |
| Registry | 存放 Image 的倉庫(如 Docker Hub) | 📚 食譜圖書館 |
一個 Image 可以跑出多個 Container,就像一份食譜可以做出很多個蛋糕一樣。
Dockerfile → docker build → Image → docker run → Container
(食譜) (烤蛋糕) (成品) (開吃) (正在被吃的蛋糕)
四. 你的第一個 Dockerfile #
假設我們有一個超級簡單的 Python 應用:
# app.py
from datetime import datetime
def main():
print(f"🐍 拍拍君的 Docker 應用啟動了!")
print(f"⏰ 現在時間:{datetime.now().strftime('%Y-%m-%d %H:%M:%S')}")
print(f"✅ 環境一切正常,容器運作中!")
if __name__ == "__main__":
main()
現在來寫 Dockerfile:
# 使用 Python 3.12 的輕量級映像檔
FROM python:3.12-slim
# 設定工作目錄
WORKDIR /app
# 複製程式碼
COPY app.py .
# 執行指令
CMD ["python", "app.py"]
建構並執行:
# 建構 Image(-t 是給它一個名字)
docker build -t pypy-app .
# 執行 Container
docker run pypy-app
# 🐍 拍拍君的 Docker 應用啟動了!
# ⏰ 現在時間:2026-02-25 03:00:00
# ✅ 環境一切正常,容器運作中!
恭喜!你的第一個 Docker 容器跑起來了!🎉
五. 處理 Python 相依套件 #
真實的專案一定有套件相依,讓我們來看看怎麼正確處理:
# requirements.txt
fastapi==0.115.0
uvicorn[standard]==0.34.0
httpx==0.28.0
# main.py
from fastapi import FastAPI
app = FastAPI(title="拍拍君的 API")
@app.get("/")
async def root():
return {"message": "哈囉!我是跑在 Docker 裡的拍拍君 🐳"}
@app.get("/health")
async def health():
return {"status": "ok", "container": True}
對應的 Dockerfile:
FROM python:3.12-slim
WORKDIR /app
# 先複製 requirements.txt,利用 Docker 的 layer cache
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
# 再複製程式碼(這樣改程式碼不會重新安裝套件)
COPY . .
# 開放 port
EXPOSE 8000
# 啟動 FastAPI
CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000"]
🔑 Layer Cache 的重要性 #
Docker 的每一行指令都會產生一個 layer。如果某個 layer 沒有變動,Docker 會直接使用快取,不需要重新執行。
這就是為什麼我們要先 COPY requirements.txt,再 COPY 程式碼:
# ✅ 好的做法:分開 COPY
COPY requirements.txt . # Layer 1:很少變動
RUN pip install -r requirements.txt # Layer 2:很少重建
COPY . . # Layer 3:常常變動
# ❌ 不好的做法:一起 COPY
COPY . . # 程式碼一改,下面全部重來
RUN pip install -r requirements.txt # 每次都要重新安裝 😭
建構並執行:
docker build -t pypy-api .
docker run -p 8000:8000 pypy-api
打開瀏覽器連 http://localhost:8000,就能看到拍拍君的 API 回應了!
六. Multi-stage Build:讓 Image 更小 #
預設的 Python image 通常很大(接近 1 GB)。用 multi-stage build 可以大幅縮小最終的 image 大小:
# ===== Stage 1: 建構階段 =====
FROM python:3.12-slim AS builder
WORKDIR /app
# 安裝建構相依(如果需要編譯 C extension)
RUN apt-get update && apt-get install -y --no-install-recommends \
gcc \
&& rm -rf /var/lib/apt/lists/*
COPY requirements.txt .
RUN pip install --no-cache-dir --prefix=/install -r requirements.txt
# ===== Stage 2: 執行階段 =====
FROM python:3.12-slim
WORKDIR /app
# 只複製安裝好的套件,不帶編譯工具
COPY --from=builder /install /usr/local
COPY . .
# 用非 root 使用者執行(安全最佳實踐)
RUN useradd --create-home appuser
USER appuser
EXPOSE 8000
CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000"]
比較一下大小差異:
docker images
# REPOSITORY TAG SIZE
# pypy-api v1 1.02GB ← 沒有 multi-stage
# pypy-api v2 187MB ← 有 multi-stage 🎉
差了 5 倍以上!而且更安全,因為最終 image 裡面沒有 gcc 等編譯工具。
💡 選擇正確的 Base Image #
| Image | 大小 | 適用場景 |
|---|---|---|
python:3.12 |
~1 GB | 需要完整系統工具 |
python:3.12-slim |
~150 MB | 大多數情況的最佳選擇 ✅ |
python:3.12-alpine |
~50 MB | 最小,但可能有 musl 相容問題 |
拍拍君的建議:預設用 slim,除非你有特殊需求再考慮 alpine。
七. Docker Compose:多容器協作 #
真實的應用通常不只一個容器。例如一個 API + 一個 PostgreSQL 資料庫 + 一個 Redis 快取。Docker Compose 讓你用一個 YAML 檔案管理多個容器:
# docker-compose.yml
services:
api:
build: .
ports:
- "8000:8000"
environment:
- DATABASE_URL=postgresql://pypy:secret@db:5432/pypydb
- REDIS_URL=redis://cache:6379
depends_on:
db:
condition: service_healthy
cache:
condition: service_started
db:
image: postgres:16-alpine
environment:
POSTGRES_USER: pypy
POSTGRES_PASSWORD: secret
POSTGRES_DB: pypydb
volumes:
- pgdata:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U pypy"]
interval: 5s
timeout: 5s
retries: 5
cache:
image: redis:7-alpine
ports:
- "6379:6379"
volumes:
pgdata:
啟動整個應用:
# 啟動所有容器(背景執行)
docker compose up -d
# 查看狀態
docker compose ps
# 看 log
docker compose logs -f api
# 停止並清除
docker compose down
只需要一個指令,三個容器就全部跑起來了!而且 Docker Compose 會自動建立一個內部網路,讓容器之間可以用服務名稱互相連線(例如 db、cache)。
八. 實用技巧與最佳實踐 #
1. 善用 .dockerignore
#
就像 .gitignore 一樣,.dockerignore 可以避免不必要的檔案被複製到 image 中:
# .dockerignore
__pycache__/
*.pyc
.git/
.venv/
.env
*.md
tests/
.pytest_cache/
.mypy_cache/
.ruff_cache/
2. 用環境變數管理設定 #
# 在 Dockerfile 中設定預設值
ENV PYTHONUNBUFFERED=1
ENV PYTHONDONTWRITEBYTECODE=1
ENV APP_ENV=production
# 執行時覆蓋
docker run -e APP_ENV=development pypy-api
PYTHONUNBUFFERED=1 特別重要——它確保 Python 的 print 輸出會即時顯示在 docker logs 中,不會被緩衝區吃掉。
3. Health Check #
HEALTHCHECK --interval=30s --timeout=10s --retries=3 \
CMD python -c "import httpx; httpx.get('http://localhost:8000/health').raise_for_status()"
4. 開發時使用 Volume Mount #
寫程式時不需要每次都重新 build,用 volume 把本機程式碼掛進去:
docker run -p 8000:8000 \
-v $(pwd):/app \
pypy-api \
uvicorn main:app --host 0.0.0.0 --reload
--reload 會自動偵測檔案變動並重新載入,開發體驗跟本機幾乎一樣!
5. 常用指令速查 #
# 列出所有容器
docker ps -a
# 進入執行中的容器
docker exec -it <container_id> bash
# 清除未使用的資源
docker system prune -a
# 查看 image 的層級結構
docker history pypy-api
# 複製檔案到容器內/外
docker cp local_file.txt container_id:/app/
docker cp container_id:/app/output.csv ./
結語 #
恭喜你學會了 Docker 的核心技能!🎉 讓我們回顧一下今天學到的內容:
- ✅ Docker 的基本概念:Image、Container、Dockerfile
- ✅ 撰寫 Dockerfile 並善用 layer cache
- ✅ Multi-stage build 大幅縮小 image 大小
- ✅ Docker Compose 管理多容器應用
- ✅
.dockerignore、環境變數、health check 等最佳實踐
Docker 已經是現代 Python 開發者的必備技能了。不管你是要部署 API、跑資料處理 pipeline、還是建立可重現的開發環境,Docker 都能幫你搞定。
下次再也不用說「在我的電腦上可以跑」了——因為有了 Docker,哪台電腦都可以跑!🐳
延伸閱讀 #
- Docker 官方文件
- Docker Hub Python 映像檔
- FastAPI Docker 部署教學
- 拍拍君的 httpx 教學 — 搭配 Docker 做 API 測試超方便