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 builddocker push自動 build 和 push
SSH 進伺服器手動更新自動部署
「我本地測過了,怎麼上線就壞了?」每次都在統一環境測試
部署一次要 30 分鐘部署一次只要等幾分鐘

常見的 CI/CD 工具

工具說明
GitHub ActionsGitHub 內建,目前最主流,本課程使用
GitLab CI/CDGitLab 內建,GitLab 用戶首選
Jenkins老牌工具,需要自己架伺服器,新專案較少使用
CircleCI第三方服務

本課程使用 GitHub Actions,因為大部分人都有 GitHub 帳號、免費額度最多(每月 2000 分鐘)、學習資源最豐富。


5-2 GitHub Actions 基礎(約 30 分鐘)

前置準備

  1. 確認你有 GitHub 帳號(沒有的話去 github.com 註冊)
  2. 安裝 Git(Windows 用戶下載 Git for Windows)
  3. 確認 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 看結果

  1. 打開你的 Repository 頁面
  2. 點上方的 「Actions」 分頁
  3. 你會看到一個正在跑(或已完成)的 workflow
  4. 點進去可以看到每個 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 的兩種寫法:

usesrun
用途使用別人寫好的 Action自己寫 shell 指令
範例uses: actions/checkout@v4run: 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 功能來安全存放。

  1. 到你的 Repository → SettingsSecrets and variablesActions
  2. New repository secret
  3. 新增兩個 secret:
    • Name: DOCKERHUB_USERNAME,Value: 你的 Docker Hub 帳號
    • Name: DOCKERHUB_TOKEN,Value: 你的 Docker Hub Access Token

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 頁面觀察,你會看到:

  1. 先跑 test job(測試)
  2. 測試通過後,自動跑 docker job(build + push)
  3. 完成後,到 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 下來跑跑看。

0%