雲原生課程 / Cloud Native Series
CI/CD with GitHub Actions
雲原生時代的軟體交付: 從 commit 到 deploy 的自動化工程

陳紹雲 / Shao-Yun Chen · TSMC/BSID/TPMP
講師介紹
About Me
陳紹雲 / Shao-Yun Chen
TSMC / BSID / TPMP
今天的目標
讓你下課之後,可以在自己的 repo 寫出第一條 CI/CD pipeline
課程大綱
今天會帶你做什麼
01
理解角色
搞懂 CI/CD 在雲原生工作流程裡的角色
02
看懂 Workflow
看懂 GitHub Actions 的 workflow 結構
03
實作 CI
在 Codespaces 跑 Fastify 專案的 CI,用 act 本機模擬
04
容器化 + CD
把 app 容器化,加上 CD 自動部署,理解 branch 策略
05
認識 GitOps
雲原生時代的 CD 新範式
背景脈絡
先回到雲原生
過去幾堂課我們學了:
Container
把 app 跟它的環境打包在一起
Kubernetes
大規模管理 container
Microservices
把大系統拆成多個小服務

一個系統如果有 30 個微服務、每天有 50 次更新⋯⋯誰要手動把 image build 出來、推到 registry、再部署到 K8s?
雲原生定義
雲原生四要素
CNCF 對「雲原生」的定義包含四個技術支柱:
Containers
解決環境一致性問題
Microservices
系統可拆解、可獨立部署
Dynamic Orchestration
規模化管理
DevOps / CI/CD
讓上面三個能持續、安全地交付

沒有 CI/CD,前面三個就只是好看的架構圖
核心概念
CI/CD 是雲原生的引擎

容器 + K8s 給你
部署的能力

CI/CD 給你
「持續部署」的工程紀律
沒有 CI/CD 的雲原生 = 用跑車載貨,但每次都要手推上去
課程地圖
這堂課在課程的位置
CI/CD 是把前面的雲原生概念串成一條真正能跑的路,也是通往後續進階主題的基石。
痛點情境
沒有 CI/CD 的雲原生世界
想像你公司有 20 個微服務跑在 K8s 上⋯⋯
工程師 A
手動 docker build → 手動 docker push → 手動 kubectl apply
工程師 B
用了不一樣版本的 base image,production 爆炸
工程師 C
在自己機器 build 的 image,沒人知道是哪個 commit
Release 前一晚
20 個服務手動部署一輪,凌晨 3 點 沒有人敢碰 production
核心概念
軟體是怎麼做出來的
任何一個軟體專案都會經歷以下階段:Analysis → Design → Development → Testing → Deployment → Maintenance
從工程師日常視角,每天反覆做的就是這四步。一個微服務一天可能跑 5 次,30 個服務一天 150 次——沒有自動化,光這四步就把整個團隊吃光。
核心定義
CI/CD 把這四步自動化
CI — Continuous Integration
持續地把多人的 code 合進同一個專案,每次合進來都自動跑 Test 跟 Build
CD — Continuous Delivery / Deployment
CI 產出的 build,自動 Release 到測試或正式環境

不是 CodeIgniter,也不是光碟片 📀📀📀
CI 深入
CI 在做的事情
每次有人 push code,自動執行:
1
抓最新的 code
2
跑 lint / format check
檢查 code style
3
跑 type check(TypeScript)
4
跑單元測試 → Build 成可部署產物

核心精神:每次 commit 都驗證,壞掉立刻知道——不要累積到 release 那天才一次發現問題
CI 原理
CI 怎麼知道測試「壞掉」?
CI 其實很笨,它只看一個東西:Exit Code
$ npm test ✓ all tests passed $ echo $? 0 # ← 0 = 成功
$ npm test ✗ 1 test failed $ echo $? 1 # ← 非 0 = 失敗
回傳 0 → 綠燈,繼續下一步
回傳非 0 → 紅燈,整條 pipeline 停下來
這就是 CI 擋下爛 code 的全部原理。換成 Python、Go、Rust 都一樣,因為這是 Unix 的通用約定。
工具生態
CI Tools
市面上常見的 CI 工具:
Jenkins
老牌、功能強、要自己維運
GitLab CI
跟 GitLab 綁在一起
Azure Pipelines
微軟生態
GitHub Actions
今天要用這個
核心概念
換湯不換藥:CI Pipeline 的本質
CI 工具品牌一堆,但核心模型都一樣——任何 pipeline 在描述兩件事:
Works (要做什麼)
  • What to do — 要做哪些步驟?(checkout、test、build…)
  • How to do — 怎麼做?(指令、腳本)
Resources (用什麼資源)
  • Who to do — 誰負責?(哪個 job、哪個 service account)
  • Where to do — 在哪台機器?(runner、agent、container)
學會這個抽象模型,換工具只是換語法
工具選擇
為什麼選 GitHub Actions
原生整合
跟 GitHub 原生整合,push 就跑,零設定基礎建設
免費額度
Public repo 完全免費,學生最友善
YAML 語法
寫過一次就會,學習曲線低
Marketplace 生態
很多現成的 action 可用,開箱即用
核心概念
GitHub Actions 的概念
Event
例如程式碼推送 (push)、合併請求 (pull_request) 或定時排程 (schedule),是觸發自動化流程的起點。
Job
一個獨立的執行單元,由一系列 Step 組成,且總是在同一台 Runner 上執行。多個 Job 可以並行或依序執行。
Step
Job 中的最小執行步驟,可以是執行一個指令 (run) 或使用一個別人寫好的 Action (uses)。
Runner
執行 Job 的虛擬機器或實體機器。GitHub 提供代管 Runner,你也可以自架 Runner 來滿足特定需求。
核心概念
GitLab CI 的概念
與 GitHub Actions 相似,GitLab CI 也有觸發事件、執行單元和執行環境,但多了一層 Stage 的概念來組織 Job。
GitLab CI 多了 Stage 這層,用來分組 Job 並控制執行順序。你可以想像 Stage 是「橫向群組」,Job 則是「群組裡的成員」。
核心概念
Azure Pipelines 的概念
Azure Pipelines 作為 Microsoft Azure DevOps 的核心組件,提供強大的 CI/CD 能力。它與 GitLab CI 概念相似,同樣透過 Stage 來組織 Job,但也有其獨特的詞彙。

Azure Pipelines 與 GitLab CI 在結構上非常相似,兩者都強調 Stage 的概念。主要差異在於執行機器稱為 Agent (而非 Runner),以及單一步驟分為 Step / Task 兩層。
核心概念
三家對照:同一件事,不同名字
不同的 CI 工具,對於概念的命名略有差異,但核心意義相似:
  • GitHub 稱之為 Workflow,其他工具多稱為 Pipeline。
  • GitLab CI 與 Azure Pipelines 具有 Stage 的概念來分組工作與控制執行順序,而 GitHub Actions 則透過 needs: 處理任務依賴關係。
學會 GitHub Actions,讀懂 GitLab CI / Azure Pipelines 的設定也大致能理解其核心邏輯。
YAML 基礎
為什麼 GitHub Actions 用 YAML
YAML = YAML Ain't Markup Language,一種給人讀的資料格式。

縮排很重要,用空格不要用 Tab
YAML 實戰
YAML 在 GitHub Actions 長什麼樣
jobs: test: # job 名字 runs-on: ubuntu-latest # 跑在哪台機器 steps: # 步驟列表 - run: npm test # - 開頭表示 list 的一個元素 - run: npm run lint
jobs: test: runs-on:
都是 key-value 結構
steps: 後面是 list
- 開頭,縮排決定誰是誰的子層

看懂這個,就看懂 GitHub Actions 80% 了
第一個 Workflow
Hello World Workflow
name: Hello GitHub Actions # workflow 名字 on: # 什麼時候要跑 push: branches: - "**" jobs: # 要做哪些事 hello-job: runs-on: ubuntu-latest steps: - name: Checkout code uses: actions/checkout@v4 # uses = 用現成 action - name: Say Hello run: echo "Hello World!" # run = 跑一行 shell
放在 .github/workflows/hello.yaml,push 到任意分支就會跑。
真實 CI Pipeline
升級版:跑測試
name: Node CI on: push: branches: - "**" jobs: test: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: actions/setup-node@v4 with: node-version: 22 - run: npm install - run: npm test

這就是一條真正能用的 Node.js CI pipeline
產物概念
Artifact:CI 的產物
Build Artifact(CI 階段)
CI 跑完留下的中間產物,用於除錯或審計:
  • 測試報告(JUnit XML、coverage)
  • Build 出來的 binary / dist 檔
  • Log 檔
actions/upload-artifact@v4
Deployment Artifact(CD 階段)
雲原生時代,最終要部署到環境的東西
  • Container Image ← 今天的主角
  • (傳統還有 jar / war / zip…)
→ Image 推到 Container Registry
CI + PR
CI 怎麼跟 PR 結合
確保不會把壞掉的 code merge 進主分支:

可以在 GitHub repo 設定:CI 沒過就不准 merge
CI → CD
從 CI 走到 CD
1
CI
CODE · BUILD · TEST
驗證並打包 code
2
Release
通過驗證的產物
3
CD
DEPLOY · OPERATE · MONITOR
送到實際環境跑
CI 把 code 驗證好、打包好 → CD 把這個產物送到實際環境跑。
CD 流程
Continuous Delivery (CD)
雲原生時代,這條路很標準化:
打 tag(版本、commit SHA)
Push 到 container registry
更新 K8s manifest / Helm values
健康檢查,失敗自動 rollback
Container 的價值
為什麼 Container 是 CD 的關鍵
Container Image = 雲原生的 Deployment Artifact
不可變、可複製、隨處可跑

Build once, run anywhere — 這就是雲原生的核心精神
兩個 D 的差異
Delivery vs Deployment
Continuous Delivery
  • 自動部署到 staging / 測試環境
  • 要人工按一下才會上 production
  • 適合需要 QA 或法遵 review 的團隊
Continuous Deployment
  • 從 code 到 production 完全自動
  • 不需要人介入
  • 對測試覆蓋率、監控要求很高

課程口語講 "CD" 通常指前者(Continuous Delivery)
部署細節
Deploy Stage 在做什麼
部署不只是「丟個檔案上去」,要考慮:
Artifact
要部哪個版本?
Architecture
一台 VM?K8s?Serverless?
Replica
開幾個 instance?
Secrets 管理
DB 連線、API key — 不能寫死在 code
安全實踐
Secrets:不能寫死的東西
DB 密碼、API key、Cloud credential⋯⋯寫死在 code 裡 = 推到 GitHub 那刻全世界都知道
- name: Deploy env: DB_PASSWORD: ${{ secrets.DB_PASSWORD }} API_KEY: ${{ secrets.PROD_API_KEY }} run: ./deploy.sh
設定地方:Repo Settings → Secrets and variables → Actions

Secret 在 log 裡會自動被遮蔽成 ***,但故意 echo 出來還是看得到,別亂試。雲端部署的 Secrets 管理是門大學問(HashiCorp Vault、AWS Secrets Manager、K8s Secret…)
Branch 策略
從概念到實戰:Branch 策略
「我哪個 branch 該跑 CI?哪個該跑 CD?」

每個 image 都能對應到一個明確的 commit / 版本,這是雲原生可追溯性的基礎。
CI/CD 的價值
CI/CD 帶來的世界 🌈
對團隊
  • Bugs 在 commit 當天抓到,不是 release 那天
  • Release 變日常,不是大工程
  • 一天可以上 N 次,不是一個月一次
對工程師個人
  • 專心寫 code,不用手動跑流程
  • Reviewer 看 PR 不用先擔心跑不跑得起來
  • 半夜不會被叫起來修 production
接下來,我們動手做 👇
Lab 實作
Lab 動手做

用 Codespaces + GitHub Actions + Docker + act
跑出一條完整 pipeline
設計取捨
為什麼 Lab 用 Docker Compose,不用 K8s?
設計取捨
課程目標是理解 CI/CD pipeline 的本質,不是學雲端設定。K8s + cloud registry + IAM 設好,可能就吃掉整堂課。
概念相通
Docker Compose 跟 K8s 都是「描述要跑什麼 container」。CD 流程只差最後一步——把 docker compose up 換成 kubectl apply 而已。

真實工作:把 lab 的 cd.yml 最後一段換成『push image 到 Registry → 部署到 K8s』,就是企業級 CD。Pipeline 的骨架是一樣的。
Lab 環境
Lab 環境總覽
GitHub Codespaces
雲端開發環境,瀏覽器就能寫
Node.js + Fastify + TypeScript
一個簡單的 web app + Vitest 測試框架
GitHub Actions
CI/CD 平台
Docker + act
容器化 + 本機模擬 GitHub Actions

⚠️ 不需要安裝任何東西,Codespaces 已經幫你裝好了
環境架構
環境分層:你現在到底在哪一層?
Lab 會有三層 container,debug 時要分清楚:
1
2
3
1
Layer 1:Codespace
你的開發機,本身是容器,你在這裡敲指令
2
Layer 2:act Runner Container
用來模擬 GitHub Actions runner
3
Layer 3:Application Container
docker compose 跑起來的 app
Debug 時看到一個 log,先問自己「這是哪一層的?」
專案結構
Lab 專案長什麼樣
cicd-lab/ ├── .github/ │ └── workflows/ # ← 一開始是空的 ├── snippets/ │ ├── 01_hello.yaml # Lab-01 會用 │ ├── 02_run-test.yaml # Lab-02 會用 │ ├── ci.yaml # Lab-04 會用 │ └── cd.yaml # Lab-04 會用 ├── src/ │ ├── app.ts # Fastify app (可測試) │ └── server.ts # 啟動 server ├── test/ │ └── app.test.ts # Vitest 測試 ├── Dockerfile ├── docker-compose.yml └── package.json
這個 Lab 專案模擬一個完整的 Node.js + Fastify 服務,包含原始碼、測試檔案和 Docker 設定。

為了讓大家循序漸進理解 GitHub Actions,所有的 Workflow 設定檔都先放在 snippets/ 資料夾裡。我們會分階段引導你將它們複製到 .github/workflows/,這樣就能一步步感受 CI/CD Pipeline 的演進。
Lab 實作
Step 0:Fork 並開 Codespaces
這是實作的第一步,請依照以下步驟設定您的開發環境:
  1. 前往課程 Repo,點擊右上角 Fork 按鈕以建立您的副本。
  1. 進入您 Fork 出來的儲存庫。
  1. 點擊綠色的 <> Code 按鈕 → 選擇 Codespaces → 點擊 Create codespace on main
  1. 等待約 1-2 分鐘,讓雲端開發環境自動建立完成。
環境建立完成後,請驗證各工具的版本:
node --version docker --version docker compose version act --version
Step 1
Step 1:本地跑起來
# 安裝依賴 npm ci # 啟動服務 npm run build npm run start
開另一個 terminal 驗證:
curl http://localhost:3000/ curl http://localhost:3000/health

應該會看到 {"status":"ok"} 之類的回應
Step 2
Step 2:跑單元測試
npm test
看到測試結果全部綠色就表示程式碼符合預期,沒有錯誤。
接著,請您故意修改 src/app.ts 檔案,將 /health 端點的回應內容改錯。再次執行 npm test,這時您應該會看到測試失敗(紅色)。

這就是持續整合 (CI) 在 GitHub 上自動為您執行的任務。請務必在觀察完測試失敗後,將 src/app.ts 恢復原狀,否則後續推送 (push) 時,CI 將會持續失敗。
Lab 實作
Lab-01: 複製 hello.yaml + push
將位於 snippets/01_hello.yaml 的檔案複製到 .github/workflows/ 目錄下,並推送到新的分支:
git checkout -b feature/ci-observe cp snippets/01_hello.yaml .github/workflows/ git add . git commit -m "ci: add hello.yaml" git push origin feature/ci-observe

完成推送後,點擊左側導航欄的 Actions 分頁,您將會看到名為 Hello GitHub Actions 的 Workflow 已經自動開始運行。
Lab 觀察
觀察:Hello GitHub Actions 在做什麼
點進 Hello GitHub Actions workflow run,並點開每個 step 檢視 log:
01
Checkout code
GitHub Actions runner 會將您的程式碼儲存庫複製 (clone) 到執行環境中。
02
Hello world action step
執行一個簡單的 echo "Hello World!" 命令,輸出至 log。
03
Show current directory
執行 ls -al 命令,列出 checkout 後的檔案結構,驗證程式碼已存在。

請記住,每個 Runner 都是一台全新的 VM 或 Container,它在執行您的 Workflow 之前是空的。只有經過 Checkout code 步驟後,您的程式碼才會被複製到這個環境中,供後續步驟使用。
Lab 實作
Lab-02: 複製 run-test.yaml + push
接下來,請將位於 snippets/02_run-test.yaml 的檔案複製到 .github/workflows/ 目錄下,並推送到您目前的分支:
cp snippets/02_run-test.yaml .github/workflows/ git add . git commit -m "ci: add run-test.yaml" git push origin feature/ci-observe
完成推送後,點擊左側導航欄的 Actions 分頁,您將會看到以下兩個 Workflow 自動開始運行:
  • Hello GitHub Actions
  • Run Tests

Lab 觀察
觀察:Run Tests 的 Artifact
01
進入 Workflow 執行頁面
點進 Run Tests Workflow 的執行頁面。
02
檢視測試結果
點開 `Run tests` 步驟,仔細查看 Vitest 執行測試的輸出日誌。
03
尋找 Artifacts 區塊
滑動頁面到最底部,您會看到一個名為 Artifacts 的區塊。
04
下載測試報告
在此區塊中,您會發現一個名為 `test-report` 的壓縮檔,下載並解壓後,內容是 `vitest-junit.xml` 格式的測試報告。

這就是我們在課程中提到的 Build Artifact —— CI Pipeline 執行後所產生的中間產物。在真實專案中,除了測試報告,還會包含程式碼覆蓋率報告、編譯後的二進位檔案或打包好的應用程式等,這些都是後續 CD 部署的重要依據。
工具介紹
act 是什麼
act 是一個強大的工具,能讓您在本地端執行 GitHub Actions workflow,對於除錯和快速迭代 CI/CD 流程非常有幫助。

詳細資訊可參閱:官網GitHub 儲存庫
Lab 實作
Lab-03: 用 act 模擬 push
act 工具不僅能模擬完整的 CI/CD 流程,還能模擬特定的事件類型(例如 push),讓您在本地端就能測試 Workflow,大幅提升開發效率。
您可以直接在分支上執行 act push 模擬推送事件:
act push
若想只針對單一 Workflow 進行測試,可以加上 -W 參數:
act push -W .github/workflows/01_hello.yaml act push -W .github/workflows/02_run-test.yaml
進階用法:若不想切換分支,但希望模擬特定分支的推送事件,可以使用 --env GITHUB_REF 參數:
act push --env GITHUB_REF=refs/heads/your-feature-branch

第一次執行 act 會自動拉取必要的 Docker 映像檔 (例如 catthehacker/ubuntu),這可能需要約 1-2 分鐘。為了節省時間,您可以在課程開始前先執行 docker pull catthehacker/ubuntu:act-latest 預先拉取映像檔。
進階 Workflow
接下來:業界進階版的 ci.yml + cd.yml
目前為止,hello.ymlrun-test.yaml 已經讓您對 GitHub Actions 有了基本認識。但業界真實的 CI/CD pipeline 會比這兩個檔案執行更多複雜的任務。
接下來,我們將帶您解析 snippets/ci.ymlsnippets/cd.yml 這兩個進階設定檔,了解它們如何處理更複雜的 CI/CD 流程。

這部分課程主要以帶看講解為主,暫不要求實際操作。若您想在本機環境運行,稍後會介紹如何使用 act 進行模擬。
Lab 目標
Lab-04 的目標
在實際應用中,我們希望透過自動化流程達成以下目標:
1
每次提交都跑 CI
確保每一次程式碼提交都能觸發 CI 流程,自動驗證程式碼品質與功能穩定性。
2
每次提交都建構 Image
每一次提交都建構 Docker Image,確保版本可追溯,有助於後續的問題排查與管理。
3
Image Tag 區分功能與發佈
利用 Image Tag 明確區分開發中的 Feature 版本與正式發佈的 Release 版本,確保部署的正確性。
4
只從 Release 分支部署
嚴格限制只有來自 Release 分支的變更才能觸發正式環境部署,避免非預期的部署造成環境不穩定。
接下來,我們將深入解析 snippets/ci.yamlsnippets/cd.yaml,看看它們是如何實現這些進階目標的。
ci.yml 解析
ci.yml — 觸發條件與權限
讓我們打開 snippets/ci.yml,從頭開始解析這個進階的 CI Workflow 設定。
name: ci on: push: branches: [ '**' ] # 所有 branch 都跑 pull_request: # PR 也跑 permissions: contents: read # 最小權限原則
觸發條件 (on)
這個 Workflow 會在每次程式碼被 push 到任何分支時自動觸發。同時,當有新的 pull_request 被建立或更新時,也會觸發 CI 流程,確保每次合併前程式碼都能通過測試。
權限設定 (permissions)
這裡採用「最小權限原則」,僅賦予 Workflow 讀取儲存庫內容 (contents: read) 的權限。這是一個重要的安全實踐,避免因權限過大而造成潛在的資安風險。
ci.yml 解析
ci.yml — 兩個進階配置
Concurrency:重複 push 自動取消舊的
concurrency: group: ci-${{ github.workflow }}-${{ github.ref }} cancel-in-progress: true
同一 branch 連續 push,只跑最新那次,省 runner 時間
Matrix:多版本並行驗證
strategy: fail-fast: false matrix: node-version: ['22', '24']
同時用 Node 22 跟 24 各跑一次,驗證升級風險
ci.yml 解析
ci.yml — 主要 Steps
- name: Checkout uses: actions/checkout@v4 # 抓 code - name: Setup Node.js uses: actions/setup-node@v4 # 裝 Node with: node-version: ${{ matrix.node-version }} cache: npm - name: Install dependencies run: npm ci # 裝依賴 # add your steps here, e.g. lint, test, build etc. # - run: npm run typecheck # 型別檢查 # - run: npm run ci # lint + test

每一行都是一個 quality gate,任何一步失敗整個 CI 失敗
ci.yml 解析
ci.yml — 計算 Image Tag
- name: Compute image tag id: meta run: | BRANCH_NAME="${GITHUB_HEAD_REF:-${GITHUB_REF#refs/heads/}}" if [[ "$BRANCH_NAME" == release/* ]]; then VERSION="${BRANCH_NAME#release/}" IMAGE_TAG="release-${VERSION}" else IMAGE_TAG="sha-${GITHUB_SHA::7}" fi
Feature Branch
sha-a1b2c3d
Release Branch
release-1.4.0

每個 image 都能對應到一個明確的 commit / 版本。
ci.yml 解析
ci.yml — Build Image + 觸發 CD
- name: Build Docker image if: ${{ matrix.node-version == '24' }} run: docker build -t my-app:${{ steps.meta.outputs.image_tag }} . # 另一個 job cd: if: ${{ startsWith(github.ref, 'refs/heads/release/') }} needs: ci uses: ./.github/workflows/cd.yml

關鍵:只有 release/* branch、CI 成功,才會觸發 CD
cD.yml 解析
cd.yml — 看一下結構
on: workflow_dispatch: # 可手動觸發 workflow_call: # 可被 ci.yml 呼叫 jobs: deploy: if: ${{ startsWith(github.ref_name, 'release/') }} steps: - name: Checkout uses: actions/checkout@v4 - name: Compute release image tag ... - name: Build and deploy with Docker Compose run: docker compose up -d --build - name: Verify health endpoint run: curl -fsS http://localhost:3000/health
Lab 實作
Lab-04:複製 ci/cd + act 模擬
首先,將進階版的 ci.yamlcd.yaml 複製到您的 workflows 目錄:
cp snippets/ci.yaml .github/workflows/ cp snippets/cd.yaml .github/workflows/
Feature Branch 觀察 ci.yaml:
在功能分支上模擬 push 事件,觀察 CI 流程的行為:
git checkout -b feature/a act push docker images # 查看 build 出來的 image

您會觀察到 Docker Image 的 Tag 會是類似 sha-a1b2c3d 的格式,這代表了本次commit的 SHA 值。
Release Branch 觀察 ci.yaml + cd.yaml:
切換到發布分支,模擬 push 事件,觀察 CI/CD 流程的協同工作:
git checkout -b release/1.0.0 act push docker images # 查看 release tag image

您會看到 Docker Image 的 Tag 變為 release-1.0.0,同時也會觸發後續的 CD 流程。

請注意,act push 會模擬完整的 GitHub Actions 執行環境。在 Codespaces 中首次執行時,可能需要下載 Docker 映像檔,請耐心等待。
CD 流程解析
CD 在做的事

這就是一條最小可用的 CD pipeline
教學說明
⚠️ 一個教學妥協要說清楚
剛才 docker compose up -d --build —CD 重 build 一次 image。但前面說 image 是 immutable artifact、build once run anywhere。
自相矛盾嗎?是的,這是為了 lab 簡單做的妥協。

Lab 省略 Registry 是為了不卡認證設定。延伸作業方向之一就是把這段補上。
練習 A
練習 A:加上 lint 跟 format check
.github/workflows/ci.yml 的 steps 加:
- name: Format check run: npm run format:check - name: Lint run: npm run lint
驗收:
  1. act push 會看到這兩步驟有跑
  1. 故意把某個檔案格式弄壞 → CI 在 format:check 失敗
  1. 故意違反 lint 規則 → CI 在 lint 失敗
練習 B
練習 B:新增 API + 測試
修改 src/app.ts,加一個 /version endpoint:
app.get('/version', async () => { return { version: process.env.APP_VERSION || 'dev' }; });
test/app.test.ts 加對應的測試。
驗收:
  • npm test 全綠
  • act push 能完整跑過 CI
快速參考
Lab 常用指令小抄
開發
npm install # 第一次 npm start # 跑起來 npm run dev # 熱重載
驗證
npm run typecheck # 型別檢查 npm test # 跑測試 npm run lint # lint npm run format:check
CI/CD 模擬
# 模擬 push 事件(預設跑全部) act push # 只跑 ci.yml act push -W .github/workflows/ci.yml act pull_request # 模擬 PR 事件 act -j test # 只跑名為 test 的 job act -l # 列出所有 workflow 跟 job
常見問題
常見踩雷
Q: Fork 後 Actions 沒跑?
A: 點 Actions 分頁,啟用 workflow
Q: act 第一次跑很慢?
A: 它要拉 docker image,正常的,之後會快
Q: CI 一直紅,看哪裡?
A: 點失敗的 step,看 log。同樣的指令在本機重跑(npm test
Q: 不想 push 一直炸 Actions 紀錄?
A: 用 act 在本機先驗
反思
動完手,來反思一下
剛才你做了什麼?
寫 code → CI 自動驗 → 推 release branch → CD 自動部署
CI/CD「主動推」變更到部署環境(Push 模型)
但如果有一天你要管 100 個服務、5 個 K8s cluster 呢?Push 模型的問題會浮現:
每個 cluster 認證都要交給 CI ⚠️ CI 被入侵 = 全中槍
CI 不知道 cluster 現在狀態,有人手改 cluster CI 不會發現
規模一大,部署狀態無法追蹤
GitOps
GitOps:翻轉部署方向
核心精神:Git 就是 K8s cluster 的唯一真相
SSOT
Git 長怎樣,cluster 長怎樣
Traceable
每個變更都是 commit
Self-healing
有人手改 cluster,自動拉回來
Auditable
git revert 就能 rollback
GitOps 生態
GitOps 生態的兩個關鍵字
ArgoCD
業界最紅的 GitOps CD 工具:
  • Declarative — 你描述,它去達成
  • 自動 sync Git → K8s cluster
  • Self-healing — 有人手改 cluster,自動拉回來
  • 同類:Flux CD、Jenkins X
Source / Deploy 雙 Repo
  • Source repo:應用 code(CI 跑這裡)
  • Deployment repo:K8s manifest / Helm(ArgoCD watch 這裡)
好處:改部署不觸發 build、獨立審核、降低誤操作
課程總結
今天總結
CI/CD 在雲原生扮演的角色
  • Container 打包好 → CI 自動 build image
  • K8s manifest → CD 自動部署
  • Microservices → 每個服務都有獨立 pipeline
  • 沒有 CI/CD,雲原生只是漂亮的架構圖
今天技能點
  • CI = 每次 commit 都驗證,壞掉立刻知道
  • CD = 通過驗證的版本自動部署
  • GitHub Actions YAML 怎麼寫
  • act 本機模擬,除錯飛快
  • Branch 策略決定 CI/CD 行為
  • GitOps = Git 是 cluster 的唯一真相
下一步
雲原生這條路還有什麼
CI/CD 之後可以再深入:

今天打的基礎,是後面所有主題的前提
Q & A / Thank You 🙏

參考資料
Repo

GitHub

GitHub - yubinTW/cicd-lab

Contribute to yubinTW/cicd-lab development by creating an account on GitHub.

Happy Shipping! 🚀