AI & Data Dreamers練功房
寶箱片場規則
前傳・開工前環境安裝

Docker 入門課:核心概念、hello-world、Dockerfile+FastAPI、容器化 pipeline、DockerHub

← 回資料館・在 GitHub 看原始檔(de-course)・裝到一半卡住?左下角送件鴿或 Discord #觀眾服務台 喊一聲

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:

  1. 打開瀏覽器,前往 https://www.docker.com/products/docker-desktop/
  2. 點「Download for Windows」

📸 截圖 D1:Docker Desktop 官網下載頁(標出下載按鈕)

  1. 執行下載的安裝檔
  2. 安裝時確認「Use WSL 2 instead of Hyper-V」已勾選

📸 截圖 D2:安裝選項(標出 WSL 2 選項)

  1. 安裝完成後,Docker Desktop 會自動啟動
  2. 看到 Docker Desktop 的鯨魚圖示在系統列出現

🍎 Mac:

brew install --cask docker

安裝完成後打開 Docker Desktop:

open -a Docker

✅ 預期:Docker Desktop 的鯨魚圖示在選單列出現

Step 2:設定 WSL Integration(🪟 Windows 限定)

  1. 打開 Docker Desktop
  2. 點右上角齒輪 → Settings
  3. 進入 Resources → WSL Integration
  4. 確認你的 Ubuntu 發行版是「Enabled」

📸 截圖 D3:WSL Integration 設定頁面(標出 Ubuntu 的開關)

  1. 點 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.
...

這個指令做了什麼?

  1. Docker 在本機找 hello-world image → 找不到
  2. 從 DockerHub 自動下載 hello-world image
  3. 用這個 image 建立一個 container
  4. Container 跑完後自動停止

📸 截圖 D4:docker run hello-world 的完整輸出

Step 5:看看目前的 container 和 image

docker ps -a

✅ 預期:看到一個 STATUS 為 Exited (0) 的 hello-world container

docker images

✅ 預期:看到 hello-world image


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 帳號

  1. 打開瀏覽器,前往 https://hub.docker.com/
  2. 點 Sign Up
  3. 註冊帳號(記住你的 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 確認

  1. 打開瀏覽器,到 https://hub.docker.com/ 登入
  2. 找到你剛 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