Docker 入門課:核心概念、hello-world、Dockerfile+FastAPI、容器化 pipeline、DockerHub
4. 開場:為什麼需要 Docker
4-1. 「在我電腦可以跑」的問題
你一定遇過這種狀況:
| 情境 | 沒有 Docker | 有 Docker |
|---|---|---|
| 同事要跑你的專案 | 「你先裝 Python 3.11、再裝 MySQL、再裝 Redis…」 | docker compose up 一鍵全部跑起來 |
| 換電腦 | 重裝所有工具,版本可能不同 | 拉同一個 image,環境 100% 一樣 |
| 部署到伺服器 | 手動裝一堆套件,每次都不確定有沒有漏 | 把 image push 上去就好 |
| 多個專案互相干擾 | 專案 A 要 MySQL 5.7、專案 B 要 MySQL 8.0 | 每個專案各自有自己的 container |
4-2. 這門課為什麼 Docker 這麼重要
後續幾乎每一集都會用 Docker:
| 集數 | 用 Docker 跑什麼 |
|---|---|
| EP05-07 | RabbitMQ + Flower + Celery workers |
| EP08 | MySQL + phpMyAdmin |
| EP09-10 | FastAPI |
| EP11 | Portainer + Grafana + Prometheus |
| EP12 | Metabase |
| EP13-14 | Airflow(整套環境就是一個 docker-compose) |
| EP15-22 | GCP 上的 Docker 部署 |
如果你不會 Docker,後面全部卡住。所以今天一定要把 Docker 搞懂。
5. Part 1:Docker 核心概念
5-1. Docker 是什麼
Docker 是一個把「程式 + 執行環境」打包在一起的工具。打包出來的東西叫 Image(映像檔),跑起來的實例叫 Container(容器)。
| 概念 | 比喻 | 說明 |
|---|---|---|
| Image | 料理食譜 | 定義了環境長什麼樣(OS、Python 版本、套件、你的 code) |
| Container | 照食譜做出來的那道菜 | Image 的一個正在跑的實例。可以同時跑多個 |
| Dockerfile | 寫食譜的步驟 | 一個文字檔,告訴 Docker 怎麼建 Image |
| Docker Compose | 一桌套餐的菜單 | 一個 YAML 檔,同時管多個 Container(API + DB + 爬蟲…) |
| DockerHub | 食譜分享平台 | 存放和分享 Image 的雲端倉庫 |
5-2. Docker vs 虛擬機
| Docker Container | 虛擬機(VM) | |
|---|---|---|
| 啟動速度 | 秒級 | 分鐘級 |
| 資源佔用 | 輕量(共用 OS kernel) | 重量(每個 VM 跑一個完整 OS) |
| 隔離程度 | Process 級隔離 | 完整 OS 隔離 |
| 適合場景 | 微服務、CI/CD、開發環境 | 需要完整 OS 的場景 |
我們用 Docker 不用 VM,因為資料工程的服務(MySQL、API、爬蟲)都是輕量微服務,不需要整個 OS。
5-3. Dockerfile 基本指令
| 指令 | 功能 | 範例 |
|---|---|---|
FROM |
基底映像檔 | FROM python:3.11-slim |
WORKDIR |
設定工作目錄 | WORKDIR /app |
COPY |
複製檔案進 image | COPY . /app |
RUN |
執行指令(建構階段) | RUN pip install -r requirements.txt |
CMD |
容器啟動時執行的指令 | CMD ["python", "main.py"] |
EXPOSE |
聲明要開放的 port | EXPOSE 8000 |
5-4. docker-compose.yml 基本結構
services:
app:
build: .
ports:
- "8000:8000"
volumes:
- .:/app
db:
image: mysql:8.0
environment:
MYSQL_ROOT_PASSWORD: password
ports:
- "3306:3306"
| 欄位 | 功能 |
|---|---|
services |
定義所有服務 |
build: . |
用當前目錄的 Dockerfile 建 image |
image: |
直接用現成的 image(如 mysql:8.0) |
ports |
把容器的 port 對應到主機 |
volumes |
把主機的資料夾掛進容器(檔案同步) |
environment |
設定環境變數 |
5-5. Docker 常用指令速查
| 指令 | 功能 |
|---|---|
docker run <image> |
跑一個 container |
docker build -t <name> . |
用 Dockerfile 建 image |
docker ps |
列出正在跑的 container |
docker ps -a |
列出所有 container(含已停止) |
docker stop <id> |
停止 container |
docker rm <id> |
刪除 container |
docker images |
列出所有 image |
docker rmi <id> |
刪除 image |
docker logs <id> |
看 container 的 log |
docker compose up |
啟動 compose 裡的所有服務 |
docker compose up -d |
背景啟動 |
docker compose down |
停止並刪除所有服務 |
docker compose logs |
看所有服務的 log |
6. Lab 1:安裝 Docker + hello-world
📂 這段要準備什麼:能上網的電腦 + WSL/Terminal
Step 1:安裝 Docker Desktop
🪟 Windows:
- 打開瀏覽器,前往
https://www.docker.com/products/docker-desktop/ - 點「Download for Windows」
📸 截圖 D1:Docker Desktop 官網下載頁(標出下載按鈕)
- 執行下載的安裝檔
- 安裝時確認「Use WSL 2 instead of Hyper-V」已勾選
📸 截圖 D2:安裝選項(標出 WSL 2 選項)
- 安裝完成後,Docker Desktop 會自動啟動
- 看到 Docker Desktop 的鯨魚圖示在系統列出現
🍎 Mac:
brew install --cask docker
安裝完成後打開 Docker Desktop:
open -a Docker
✅ 預期:Docker Desktop 的鯨魚圖示在選單列出現
Step 2:設定 WSL Integration(🪟 Windows 限定)
- 打開 Docker Desktop
- 點右上角齒輪 → Settings
- 進入 Resources → WSL Integration
- 確認你的 Ubuntu 發行版是「Enabled」
📸 截圖 D3:WSL Integration 設定頁面(標出 Ubuntu 的開關)
- 點 Apply & Restart
Step 3:驗證 Docker 安裝
回到 WSL Ubuntu(或 Mac Terminal):
docker version
✅ 預期:看到 Client 和 Server 兩區的版本資訊
❌ 如果只有 Client 沒有 Server:Docker Desktop 沒開。打開它再試。 ❌ 如果
docker: command not found:WSL Integration 沒啟用。回 Step 2 確認。
docker compose version
✅ 預期:
Docker Compose version v2.x.x
注意:
docker compose(有空格)是新版 Compose plugin;docker-compose(有 dash)是舊版 standalone。我們用新版。
Step 4:跑第一個 container
docker run hello-world
✅ 預期:
Hello from Docker! This message shows that your installation appears to be working correctly. ...
這個指令做了什麼?
- Docker 在本機找
hello-worldimage → 找不到 - 從 DockerHub 自動下載
hello-worldimage - 用這個 image 建立一個 container
- Container 跑完後自動停止
📸 截圖 D4:docker run hello-world 的完整輸出
Step 5:看看目前的 container 和 image
docker ps -a
✅ 預期:看到一個 STATUS 為
Exited (0)的 hello-world container
docker images
✅ 預期:看到
hello-worldimage
7. Lab 2:寫 Dockerfile + FastAPI 體驗 Docker
這段用一個最簡單的 FastAPI 範例來體驗「寫 Dockerfile → build → run → 瀏覽器看到結果」的完整流程。
Step 6:建立 FastAPI 範例專案
cd ~/projects
mkdir docker-demo
cd docker-demo
code .
在 VS Code 裡建立 main.py:
from fastapi import FastAPI
app = FastAPI()
@app.get("/")
def hello():
return {"message": "Hello from Docker!"}
建立 requirements.txt:
fastapi
uvicorn[standard]
✅ 預期:
docker-demo/裡有main.py和requirements.txt
Step 7:寫 Dockerfile
在 docker-demo/ 裡建立 Dockerfile(沒有副檔名):
FROM python:3.11-slim
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY . .
EXPOSE 8000
CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000"]
逐行解釋:
| 行 | 指令 | 做什麼 |
|---|---|---|
| 1 | FROM python:3.11-slim |
基底是 Python 3.11 的精簡版 Linux |
| 2 | WORKDIR /app |
在容器裡建 /app 目錄並進入 |
| 3 | COPY requirements.txt . |
先只複製 requirements.txt(利用 Docker cache) |
| 4 | RUN pip install ... |
安裝 Python 套件 |
| 5 | COPY . . |
把所有 code 複製進去 |
| 6 | EXPOSE 8000 |
聲明這個容器會用 port 8000 |
| 7 | CMD [...] |
容器啟動時跑 uvicorn |
💡 為什麼
COPY requirements.txt和COPY . .分兩步?因為 Docker 有 layer cache:如果 requirements.txt 沒變,pip install 那步會用快取,不用每次重裝。
Step 8:Build image
docker build -t docker-demo .
| 參數 | 意思 |
|---|---|
-t docker-demo |
給 image 取名叫 docker-demo |
. |
用目前目錄的 Dockerfile |
✅ 預期:一步一步執行 Dockerfile 的每一行,最後顯示
Successfully tagged docker-demo:latest
📸 截圖 D5:docker build 的完整輸出(標出每個 Step)
Step 9:Run container
docker run -p 8000:8000 docker-demo
| 參數 | 意思 |
|---|---|
-p 8000:8000 |
把主機的 port 8000 對應到容器的 port 8000 |
✅ 預期:看到 uvicorn 啟動訊息:
Uvicorn running on http://0.0.0.0:8000
Step 10:在瀏覽器看結果
打開瀏覽器,輸入 http://localhost:8000
✅ 預期:看到
{"message": "Hello from Docker!"}
📸 截圖 D6:瀏覽器顯示 JSON 結果
試試 Swagger UI:http://localhost:8000/docs
📸 截圖 D7:FastAPI Swagger UI(標出 /docs 頁面)
🎉 你剛才做了什麼?你寫了一個 Python API,包進 Docker image,跑起來的 container 在瀏覽器上回應了你。任何電腦只要有 Docker,跑同一個 image 就會得到一模一樣的結果。
在 terminal 按 Ctrl+C 停止 container。
8. Lab 3:容器化 finmind pipeline + Docker Compose
現在把 EP03 做的 finmind package 容器化。
Step 11:複製 finmind 種子到 EP03
cd ~/serial-course
cp -r EP02/code/finmind EP03/code/finmind
cd EP03/code/finmind
ls
✅ 預期:看到
finmind/、tests/、pyproject.toml等 EP03 的檔案(累積快照)
Step 12:為 finmind 寫 Dockerfile
在 EP03/code/finmind/ 裡建立 Dockerfile:
FROM python:3.11-slim
WORKDIR /app
COPY pyproject.toml .
COPY finmind/ finmind/
RUN pip install --no-cache-dir .
CMD ["python", "-m", "finmind.extract"]
這個 Dockerfile 做的事:用 Python 3.11 + 安裝 finmind package + 跑 extract 模組
Step 13:Build finmind image
docker build -t finmind-pipeline .
✅ 預期:Build 成功,
Successfully tagged finmind-pipeline:latest
Step 14:Run finmind container
docker run finmind-pipeline
✅ 預期:看到 finmind extract 的輸出(跟 EP03
python -m finmind.extract一樣)
Step 15:寫 docker-compose.yml
在 EP03/code/finmind/ 裡建立 docker-compose.yml:
services:
pipeline:
build: .
container_name: finmind-pipeline
command: python -c "from finmind.extract import fetch_sample_stock; print(fetch_sample_stock())"
Step 16:用 Docker Compose 啟動
docker compose up
✅ 預期:看到 finmind pipeline 的執行結果
docker compose down
✅ 預期:Container 被停止並移除
Step 17:加入 pytest 測試到 compose
更新 docker-compose.yml,加一個 test 服務:
services:
pipeline:
build: .
container_name: finmind-pipeline
command: python -c "from finmind.extract import fetch_sample_stock; print(fetch_sample_stock())"
test:
build: .
container_name: finmind-test
command: pytest tests/ -v
docker compose up test
✅ 預期:在 Docker container 裡跑 pytest,3 個測試全部通過
📸 截圖 D8:docker compose up test 的輸出(標出 pytest 結果)
🎉 你的 finmind pipeline 現在可以在 Docker 裡跑了。任何電腦只要
docker compose up就能重現結果。
9. Lab 4:DockerHub push
9-1. 什麼是 DockerHub
DockerHub 就是 Docker image 的 GitHub——你可以把 image push 上去,別人就能 docker pull 下來跑。
| 功能 | 說明 |
|---|---|
| 免費 | 個人帳號免費,可以存 public image |
| 自動 | push 上去後,別人只要 docker pull 你的帳號/image名 就能用 |
| 官方 image | python、mysql、redis 等都存在 DockerHub 上 |
Step 18:建 DockerHub 帳號
- 打開瀏覽器,前往
https://hub.docker.com/ - 點 Sign Up
- 註冊帳號(記住你的 username)
📸 截圖 D9:DockerHub 註冊頁面
Step 19:在 terminal 登入 DockerHub
docker login
輸入你的 DockerHub username 和 password
✅ 預期:
Login Succeeded
Step 20:Tag image
docker tag finmind-pipeline 你的DockerHub帳號/finmind-pipeline:v1
例如:
docker tag finmind-pipeline dex123/finmind-pipeline:v1
Step 21:Push image
docker push 你的DockerHub帳號/finmind-pipeline:v1
✅ 預期:一層一層上傳,最後顯示 digest
📸 截圖 D10:docker push 的完整輸出
Step 22:在 DockerHub 確認
- 打開瀏覽器,到
https://hub.docker.com/登入 - 找到你剛 push 的
finmind-pipeline
📸 截圖 D11:DockerHub 上看到 image
🎉 你的 finmind pipeline image 現在在雲端了。任何人只要
docker pull 你的帳號/finmind-pipeline:v1就能跑。
10. 常見錯誤 & 排錯
| # | 錯誤 | 原因 | 解法 |
|---|---|---|---|
| 1 | Cannot connect to the Docker daemon |
Docker Desktop 沒開 | 打開 Docker Desktop |
| 2 | docker: command not found |
WSL Integration 沒啟用 | Docker Desktop → Settings → Resources → WSL Integration → 開啟 |
| 3 | Build 卡在 pip install | 網路慢 | 加 --no-cache-dir,或換網路 |
| 4 | port is already allocated |
另一個程式佔了同一個 port | 改 port(-p 8001:8000)或停掉佔 port 的程式 |
| 5 | Dockerfile: no such file |
不在正確的目錄 | pwd 確認,ls 看有沒有 Dockerfile |
| 6 | Container 跑了但瀏覽器看不到 | 忘記 -p 參數 |
加 -p 8000:8000 |
| 7 | compose 指令沒反應 |
用了舊版 docker-compose |
改成 docker compose(有空格) |
| 8 | 檔案修改後 container 沒更新 | 沒有重新 build | docker compose up --build |
11. 指令速查表
| 指令 | 功能 |
|---|---|
docker version |
看 Docker 版本 |
docker compose version |
看 Compose 版本 |
docker run hello-world |
跑第一個 container |
docker build -t <name> . |
用 Dockerfile 建 image |
docker run -p 8000:8000 <name> |
跑 container 並開 port |
docker ps |
列出正在跑的 container |
docker stop <id> |
停止 container |
docker images |
列出所有 image |
docker compose up |
啟動 compose 所有服務 |
docker compose up --build |
重建 image 後啟動 |
docker compose down |
停止並移除所有服務 |
docker login |
登入 DockerHub |
docker tag <src> <dst> |
給 image 加 tag |
docker push <name> |
push image 到 DockerHub |

送件鴿飛出去了