Docker 教學 第 6 堂:GitHub Actions 進階實作與課程總結
6-1 GitHub Actions 進階語法(約 1 小時)
環境變數
在 workflow 裡可以用環境變數來避免重複寫相同的值:
name: CI with Environment Variables
on:
push:
branches: [ main ]
env: # 整個 workflow 共用的環境變數
IMAGE_NAME: my-flask-app
PYTHON_VERSION: '3.11'
jobs:
build:
runs-on: ubuntu-latest
env: # 這個 job 專用的環境變數
FLASK_ENV: testing
steps:
- name: 顯示環境變數
run: |
echo "Image: ${{ env.IMAGE_NAME }}"
echo "Python: ${{ env.PYTHON_VERSION }}"
echo "Flask: $FLASK_ENV" 環境變數的三個層級:
| 層級 | 範圍 | 寫在哪裡 |
|---|---|---|
| Workflow 層 | 所有 job 都能用 | 最外層的 env: |
| Job 層 | 只有這個 job 能用 | job 底下的 env: |
| Step 層 | 只有這個 step 能用 | step 底下的 env: |
條件判斷
可以根據條件決定要不要執行某個 step 或 job:
steps:
- name: 只在 main 分支執行
if: github.ref == 'refs/heads/main'
run: echo "This is main branch"
- name: 只在 PR 時執行
if: github.event_name == 'pull_request'
run: echo "This is a pull request"
- name: 前一步失敗才執行(通知用)
if: failure()
run: echo "Something went wrong!"多個 Job 的依賴關係
jobs:
test:
runs-on: ubuntu-latest
steps:
- run: echo "Running tests..."
build:
runs-on: ubuntu-latest
needs: test # 等 test 通過才跑
steps:
- run: echo "Building..."
deploy:
runs-on: ubuntu-latest
needs: build # 等 build 完成才跑
steps:
- run: echo "Deploying..."執行順序:
test → build → deploy
(任何一步失敗,後面的都不會跑)手動觸發 Workflow
除了 push 自動觸發,也可以加上手動觸發的按鈕:
on:
push:
branches: [ main ]
workflow_dispatch: # 加上這行就能手動觸發
inputs:
environment:
description: '部署環境'
required: true
default: 'staging'
type: choice
options:
- staging
- production在 GitHub 的 Actions 頁面就會出現「Run workflow」按鈕,可以選擇參數後手動執行。
6-2 實作:完整的 Flask CI/CD Pipeline(約 1.5 小時)
目標
建立一個完整的 CI/CD 流程:
- Push 程式碼到 GitHub
- 自動跑測試
- 測試通過 → 自動 build Docker Image
- 自動 push 到 Docker Hub
- 輸出部署資訊
步驟 1:建立 Flask 專案
建立新的專案資料夾或使用之前的 docker-cicd-demo。
app.py:
from flask import Flask, jsonify
app = Flask(__name__)
@app.route('/')
def hello():
return jsonify({
'message': 'Hello from CI/CD!',
'version': '1.0.0'
})
@app.route('/health')
def health():
return jsonify({'status': 'healthy'})
def add(a, b):
return a + b
def is_positive(n):
return n > 0
if __name__ == '__main__':
app.run(host='0.0.0.0', port=5000)test_app.py:
import pytest
from app import app, add, is_positive
@pytest.fixture
def client():
app.config['TESTING'] = True
with app.test_client() as client:
yield client
def test_hello(client):
response = client.get('/')
assert response.status_code == 200
data = response.get_json()
assert 'message' in data
def test_health(client):
response = client.get('/health')
assert response.status_code == 200
data = response.get_json()
assert data['status'] == 'healthy'
def test_add():
assert add(2, 3) == 5
assert add(-1, 1) == 0
def test_is_positive():
assert is_positive(1) == True
assert is_positive(-1) == False
assert is_positive(0) == Falserequirements.txt:
flask
pytestDockerfile:
FROM python:3.11-slim
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY . .
EXPOSE 5000
CMD ["python", "app.py"].dockerignore:
.git
.github
__pycache__
*.pyc
venv/
.env
*.md
test_*.py步驟 2:建立完整的 CI/CD Workflow
.github/workflows/cicd.yml:
name: Flask CI/CD Pipeline
on:
push:
branches: [ main ]
pull_request:
branches: [ main ]
env:
IMAGE_NAME: docker-cicd-demo
jobs:
# ===== Job 1: 測試 =====
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
# ===== Job 2: Build 並 Push Docker Image =====
docker:
runs-on: ubuntu-latest
needs: test # 測試通過才跑
if: github.event_name == 'push' # 只有 push 才 build,PR 不 build
steps:
- name: 拉取程式碼
uses: actions/checkout@v4
- name: 登入 Docker Hub
uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: 取得版本資訊
id: version
run: |
echo "sha_short=$(git rev-parse --short HEAD)" >> $GITHUB_OUTPUT
echo "date=$(date +'%Y%m%d')" >> $GITHUB_OUTPUT
- name: Build 並 Push
uses: docker/build-push-action@v5
with:
context: .
push: true
tags: |
${{ secrets.DOCKERHUB_USERNAME }}/${{ env.IMAGE_NAME }}:latest
${{ secrets.DOCKERHUB_USERNAME }}/${{ env.IMAGE_NAME }}:${{ steps.version.outputs.sha_short }}
- name: 輸出部署資訊
run: |
echo "Docker Image 已推送!"
echo "Image: ${{ secrets.DOCKERHUB_USERNAME }}/${{ env.IMAGE_NAME }}"
echo "Tags: latest, ${{ steps.version.outputs.sha_short }}"
echo ""
echo "要在伺服器上部署,執行:"
echo "docker pull ${{ secrets.DOCKERHUB_USERNAME }}/${{ env.IMAGE_NAME }}:latest"
echo "docker run -d -p 5000:5000 ${{ secrets.DOCKERHUB_USERNAME }}/${{ env.IMAGE_NAME }}:latest" 步驟 3:Push 並觀察完整流程
git add .
git commit -m "add complete CI/CD pipeline"
git push到 GitHub Actions 頁面觀察:
test(跑測試)
↓ 通過
docker(build + push)
↓ 完成
Docker Hub 上出現新的映像檔步驟 4:驗證映像檔可以使用
# 從 Docker Hub 拉下來
docker pull 你的帳號/docker-cicd-demo:latest
# 跑起來
docker run -d --name cicd-test -p 5000:5000 你的帳號/docker-cicd-demo:latest
# 測試
curl http://localhost:5000
curl http://localhost:5000/health
# 清理
docker rm -f cicd-test6-3 GitHub Actions 實用技巧(約 15 分鐘)
Badge:在 README 顯示 CI 狀態
在 README.md 加上一行,就能顯示 CI 是通過還是失敗的徽章:
效果:別人一進你的 Repository 就能看到目前 CI 狀態是綠色(通過)還是紅色(失敗)。
快取依賴加速 CI
每次 CI 都要重新 pip install 很慢,可以加上快取:
- name: 快取 pip 套件
uses: actions/cache@v4
with:
path: ~/.cache/pip
key: ${{ runner.os }}-pip-${{ hashFiles('requirements.txt') }}
- name: 安裝依賴
run: pip install -r requirements.txt第一次跑會正常安裝,之後只要 requirements.txt 沒變,就會用快取,速度快很多。
多版本測試(Matrix)
想同時在多個 Python 版本上測試:
jobs:
test:
runs-on: ubuntu-latest
strategy:
matrix:
python-version: ['3.9', '3.10', '3.11', '3.12']
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}
- run: pip install -r requirements.txt
- run: pytest test_app.py -v這會同時跑 4 個 job,分別在 Python 3.9、3.10、3.11、3.12 上測試。
6-4 課程總結(約 15 分鐘)
六堂課回顧
| 堂次 | 主題 | 學到的重點 |
|---|---|---|
| 1 | Docker 基礎概念與安裝 | Docker 是什麼、容器 vs VM、安裝 Docker Desktop |
| 2 | 映像檔與容器操作 | pull、run、ps、stop、rm、Volume、端口映射 |
| 3 | Docker Compose 與進階操作 | compose、MySQL、多容器管理、情境題 |
| 4 | Dockerfile 與映像檔建構 | Dockerfile 語法、分層、優化、push 到 Docker Hub |
| 5 | CI/CD 與 GitHub Actions 入門 | CI/CD 概念、workflow 語法、自動測試、自動 build |
| 6 | GitHub Actions 進階實作 | 完整 pipeline、環境變數、快取、多版本測試 |
完整的開發到部署流程
寫程式碼 → git push → GitHub Actions 自動跑測試
↓ 測試通過
自動 build Docker Image
↓
自動 push 到 Docker Hub
↓
部署到伺服器
↓
使用者看到最新版本接下來可以學什麼?
| 方向 | 工具 / 技術 |
|---|---|
| 容器編排 | Kubernetes(K8s)、Docker Swarm |
| 雲端部署 | AWS ECS、GCP Cloud Run、Azure Container |
| 監控 | Prometheus、Grafana、ELK Stack |
| 進階 CI/CD | ArgoCD、GitOps |
課堂練習
練習 1:加上健康檢查測試
在 workflow 裡加一個 step,build 完 Docker Image 之後先在 CI 環境裡跑起來,用 curl 測試 /health endpoint 確認容器正常運行,再 push 到 Docker Hub。
練習 2:手動觸發部署
修改 workflow,加上 workflow_dispatch,讓你可以手動點按鈕觸發部署,並選擇要部署到 staging 還是 production。
練習 3:完整專案挑戰
建立一個完整的 Flask + MySQL 專案(用 Docker Compose),搭配 GitHub Actions 實現:
- Push 時自動跑測試
- 測試通過自動 build 並 push 映像檔
- 在 README 加上 CI 狀態徽章