Docker 教學 第 5 堂:CI/CD 概念與 GitHub Actions 入門
5-1 什麼是 CI/CD?(約 30 分鐘)
先講一個故事
想像你在一間餐廳工作:
- 傳統做法:廚師做完一道菜 → 自己端出去 → 自己收盤子 → 回來再做下一道。每件事都要手動做。
- 自動化做法:廚師做完菜放到出餐口 → 傳送帶自動送到客人桌上 → 髒盤子自動回收。廚師只需要專心做菜。
CI/CD 就是軟體開發的「傳送帶」——你只需要專心寫程式碼,剩下的測試、打包、部署全部自動完成。
CI/CD 的定義
CI(Continuous Integration,持續整合):
- 開發者把程式碼推到 Git 之後,自動執行測試
- 確保每次改動都不會弄壞現有的功能
- 「我 push 了程式碼 → 系統自動幫我跑測試 → 告訴我有沒有通過」
CD(Continuous Deployment / Delivery,持續部署/交付):
- 測試通過之後,自動把程式打包、部署到伺服器
- 不需要手動 SSH 進伺服器去更新程式
- 「測試通過 → 自動 build Docker Image → 自動部署到伺服器」
完整流程:
開發者 push 程式碼
↓
CI:自動跑測試
↓ (測試通過)
CD:自動 build Docker Image
↓
CD:自動 push 到 Docker Hub
↓
CD:自動部署到伺服器
↓
使用者看到最新版本沒有 CI/CD 的痛苦
| 手動流程 | CI/CD 自動化 |
|---|---|
| 自己跑測試,常常忘記跑 | 每次 push 自動跑,不會漏 |
自己打 docker build、docker push | 自動 build 和 push |
| SSH 進伺服器手動更新 | 自動部署 |
| 「我本地測過了,怎麼上線就壞了?」 | 每次都在統一環境測試 |
| 部署一次要 30 分鐘 | 部署一次只要等幾分鐘 |
常見的 CI/CD 工具
| 工具 | 說明 |
|---|---|
| GitHub Actions | GitHub 內建,目前最主流,本課程使用 |
| GitLab CI/CD | GitLab 內建,GitLab 用戶首選 |
| Jenkins | 老牌工具,需要自己架伺服器,新專案較少使用 |
| CircleCI | 第三方服務 |
本課程使用 GitHub Actions,因為大部分人都有 GitHub 帳號、免費額度最多(每月 2000 分鐘)、學習資源最豐富。
5-2 GitHub Actions 基礎(約 30 分鐘)
前置準備
- 確認你有 GitHub 帳號(沒有的話去 github.com 註冊)
- 安裝 Git(Windows 用戶下載 Git for Windows)
- 確認 Git 設定好了:
git --version
git config --global user.name "你的名字"
git config --global user.email "你的email"核心概念
GitHub Actions 的架構很簡單,只有四個概念:
Repository(你的 Git 專案)
└── .github/workflows/ ← 放 workflow 檔案的地方
└── ci.yml ← 一個 workflow 檔案
├── Event(觸發條件):什麼時候要跑?例如 push 時
├── Job(工作):要做什麼大任務?
│ ├── Step 1(步驟):具體的一個動作
│ ├── Step 2
│ └── Step 3
└── Runner(執行環境):在哪裡跑?例如 Ubuntu用白話解釋:
- Workflow:一份「自動化腳本」,寫在 YAML 檔案裡
- Event:什麼事件觸發這個腳本(例如 push、發 PR)
- Job:腳本裡的一個大任務
- Step:大任務裡的一個步驟
- Runner:GitHub 提供的免費虛擬機,幫你跑這些步驟
第一個 Workflow:Hello GitHub Actions
步驟 1:在 GitHub 上建立新的 Repository
到 GitHub 點「New Repository」:
- Repository name:
docker-cicd-demo - 勾選「Add a README file」
- 點「Create repository」
步驟 2:Clone 到本機
cd C:\docker-lab
git clone https://github.com/你的帳號/docker-cicd-demo.git
cd docker-cicd-demo步驟 3:建立 workflow 檔案
mkdir -p .github/workflows在 .github/workflows/ 下建立 hello.yml:
# Workflow 的名稱(會顯示在 GitHub 的 Actions 頁面)
name: Hello GitHub Actions
# Event:什麼時候觸發?
on:
push:
branches: [ main ] # 當 push 到 main 分支時觸發
# Jobs:要做什麼?
jobs:
say-hello: # Job 的 ID(自己取名)
runs-on: ubuntu-latest # Runner:在 Ubuntu 虛擬機上跑
steps: # 步驟
- name: 打招呼
run: echo "Hello GitHub Actions!"
- name: 顯示目前時間
run: date
- name: 顯示系統資訊
run: |
echo "作業系統:"
uname -a
echo "目前目錄:"
pwd 步驟 4:Push 到 GitHub
git add .
git commit -m "add first workflow"
git push步驟 5:到 GitHub 看結果
- 打開你的 Repository 頁面
- 點上方的 「Actions」 分頁
- 你會看到一個正在跑(或已完成)的 workflow
- 點進去可以看到每個 Step 的輸出
恭喜!你剛才讓 GitHub 的伺服器自動幫你跑了一段程式。這就是 CI/CD 的起點。
Workflow 語法詳解
name: CI Pipeline # Workflow 名稱
on: # 觸發條件
push: # push 時觸發
branches: [ main ]
pull_request: # 發 PR 時也觸發
branches: [ main ]
jobs:
build: # Job ID
runs-on: ubuntu-latest # 執行環境
steps:
# 使用別人寫好的 Action(用 uses)
- name: 拉取程式碼
uses: actions/checkout@v4
# 自己寫指令(用 run)
- name: 安裝依賴
run: pip install -r requirements.txt
# 多行指令用 |
- name: 執行多個指令
run: |
echo "第一行"
echo "第二行" Step 的兩種寫法:
uses | run | |
|---|---|---|
| 用途 | 使用別人寫好的 Action | 自己寫 shell 指令 |
| 範例 | uses: actions/checkout@v4 | run: echo "hello" |
| 類比 | 用 npm 套件 | 自己寫程式碼 |
actions/checkout@v4是最常用的 Action,功能是把你的程式碼拉到 Runner 上。幾乎每個 workflow 都會用到。
5-3 實作:自動跑 Python 測試(約 1 小時)
建立一個有測試的 Python 專案
步驟 1:建立專案檔案
在 docker-cicd-demo 目錄下建立 app.py:
def add(a, b):
return a + b
def subtract(a, b):
return a - b
def multiply(a, b):
return a * b
def is_even(n):
return n % 2 == 0建立 test_app.py:
from app import add, subtract, multiply, is_even
def test_add():
assert add(2, 3) == 5
assert add(-1, 1) == 0
assert add(0, 0) == 0
def test_subtract():
assert subtract(5, 3) == 2
assert subtract(1, 1) == 0
def test_multiply():
assert multiply(3, 4) == 12
assert multiply(0, 5) == 0
def test_is_even():
assert is_even(2) == True
assert is_even(3) == False
assert is_even(0) == True建立 requirements.txt:
pytest步驟 2:本機先跑一次測試確認
pip install pytest
pytest test_app.py -v應該會看到所有測試都 PASSED。
步驟 3:建立 CI workflow
建立 .github/workflows/ci.yml:
name: Python CI
on:
push:
branches: [ main ]
pull_request:
branches: [ main ]
jobs:
test:
runs-on: ubuntu-latest
steps:
- name: 拉取程式碼
uses: actions/checkout@v4
- name: 安裝 Python
uses: actions/setup-python@v5
with:
python-version: '3.11'
- name: 安裝依賴
run: pip install -r requirements.txt
- name: 執行測試
run: pytest test_app.py -v步驟 4:Push 並觀察
git add .
git commit -m "add Python CI with tests"
git push到 GitHub 的 Actions 頁面看,你會看到 CI 自動跑起來了,測試結果會顯示在上面。
體驗 CI 擋住壞掉的程式碼
步驟 5:故意寫一個有 bug 的改動
修改 app.py,把 add 改壞:
def add(a, b):
return a - b # 故意寫錯!git add .
git commit -m "break add function"
git push步驟 6:看 CI 結果
到 Actions 頁面,你會看到這次的 workflow 紅色打叉(失敗)!
點進去看 log,會清楚顯示哪個測試失敗了:
FAILED test_app.py::test_add - assert -1 == 5這就是 CI 的價值——壞掉的程式碼會被立刻抓到,不會偷偷混進去。
步驟 7:修好然後再推
把 app.py 改回正確的,推上去,CI 就會變回綠色。
5-4 實作:自動 Build Docker Image(約 1 小時)
把 Docker 加入 CI 流程
現在我們要做更進階的事——每次 push 程式碼,自動 build Docker Image 並 push 到 Docker Hub。
步驟 1:建立 Dockerfile
在 docker-cicd-demo 目錄下建立 Dockerfile:
FROM python:3.11-slim
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY . .
CMD ["python", "-m", "pytest", "test_app.py", "-v"]步驟 2:在 GitHub 設定 Docker Hub 密碼
因為 workflow 要推映像檔到 Docker Hub,需要帳號密碼。但密碼不能寫在程式碼裡!GitHub 提供了 Secrets 功能來安全存放。
- 到你的 Repository → Settings → Secrets and variables → Actions
- 點 New repository secret
- 新增兩個 secret:
- Name:
DOCKERHUB_USERNAME,Value: 你的 Docker Hub 帳號 - Name:
DOCKERHUB_TOKEN,Value: 你的 Docker Hub Access Token
- Name:
Docker Hub Access Token 怎麼拿? Docker Hub → 右上角帳號 → Account Settings → Security → New Access Token
步驟 3:建立 Docker build workflow
建立 .github/workflows/docker.yml:
name: Docker Build & Push
on:
push:
branches: [ main ]
jobs:
test:
runs-on: ubuntu-latest
steps:
- name: 拉取程式碼
uses: actions/checkout@v4
- name: 安裝 Python
uses: actions/setup-python@v5
with:
python-version: '3.11'
- name: 安裝依賴
run: pip install -r requirements.txt
- name: 執行測試
run: pytest test_app.py -v
docker:
runs-on: ubuntu-latest
needs: test # 等 test job 通過才跑
steps:
- name: 拉取程式碼
uses: actions/checkout@v4
- name: 登入 Docker Hub
uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Build 並 Push Docker Image
uses: docker/build-push-action@v5
with:
context: .
push: true
tags: |
${{ secrets.DOCKERHUB_USERNAME }}/docker-cicd-demo:latest
${{ secrets.DOCKERHUB_USERNAME }}/docker-cicd-demo:${{ github.sha }} 步驟 4:Push 並觀察
git add .
git commit -m "add Docker build CI/CD"
git push到 Actions 頁面觀察,你會看到:
- 先跑
testjob(測試) - 測試通過後,自動跑
dockerjob(build + push) - 完成後,到 Docker Hub 看你的映像檔已經自動上傳了!
流程圖:
git push → 自動跑測試 → 測試通過 → 自動 build Image → 自動 push 到 Docker Hub
↓ 測試失敗
停止,不會 build
needs: test是關鍵——它確保測試沒通過就不會 build 和 push,避免壞掉的程式碼被打包成映像檔。
理解 Workflow 裡的變數
${{ secrets.DOCKERHUB_USERNAME }} # 從 Secrets 讀取(安全,不會顯示在 log 裡)
${{ github.sha }} # 這次 commit 的 hash(用來當版本號)用 commit hash 當 tag 的好處是每次 push 都有獨立的版本號,方便回滾。
課堂練習
練習 1:修改 workflow 觸發條件
修改 hello.yml,讓它在建立 Pull Request 時也會觸發。
練習 2:新增測試函數
在 app.py 新增一個 divide(a, b) 函數,在 test_app.py 新增對應的測試(記得處理除以 0 的情況),push 上去確認 CI 通過。
練習 3:觀察 Docker Hub
到 Docker Hub 確認你的映像檔有自動上傳,試著在本機 docker pull 下來跑跑看。