別再手動部署了:CI/CD 完整入門指南,從軟體到韌體都適用
別再手動部署了:CI/CD 完整入門指南,從軟體到韌體都適用

你有沒有經歷過這種場景?
星期五下午五點半,你信心滿滿地把最新的程式碼推上伺服器。改了一個小 bug,應該沒什麼問題吧?結果十分鐘後,同事的 Slack 訊息炸了:「網站掛了。」
你慌了。趕緊 SSH 進伺服器,手動 git pull,發現忘了裝新的套件。跑了 npm install,又發現環境變數沒設。一路手忙腳亂修到晚上八點,終於恢復正常。
或者你是做韌體的。改了一行 SPI 驅動的程式碼,用手邊的開發板燒錄測試沒問題,信心滿滿地交付。結果量產後客戶回報:「藍牙連線會斷。」一查才發現,你改 SPI 的時候不小心動到了 BLE 的 timer 設定,而你手動測試的時候根本沒測藍牙功能。
不管你是寫網頁、做 APP、還是搞嵌入式韌體,這些故事的根源都一樣:太多事情靠人記、靠人做,遲早會出包。
這就是 CI/CD 要解決的問題。
用點餐來解釋 CI/CD

我知道,「持續整合」「持續部署」這些詞聽起來很嚇人。但其實概念非常簡單,讓我用你最熟悉的東西來解釋 — 一間餐廳的廚房。
想像你走進一間運作順暢的餐廳。從你點餐到食物送上桌,中間經過了一條流水線:
- 備料區:洗菜、切肉、準備食材
- 料理區:大火快炒、擺盤
- 品管區:主廚試味道,確認沒問題
- 出餐口:服務生端上桌
CI/CD 就是你的程式碼的這條流水線。不管這個「程式碼」是一個網站的前端、一支手機 APP 的後端、還是一顆 MCU 上面跑的韌體,邏輯都一樣。
CI — 持續整合(Continuous Integration)
對應「品管區」。你每次寫完一段程式碼,系統就自動幫你「試味道」 — 跑測試、檢查有沒有寫壞別人的功能。
不是等整道菜做完才試,而是每加一種調料就試一次。這樣萬一味道不對,你馬上知道是哪一步出了問題。
根據 Red Hat 的定義對軟體工程師來說,這意味著每次 push 就自動跑 Jest 或 Pytest。對韌體工程師來說,這意味著每次 push 就自動用交叉編譯器(cross-compiler)建構韌體,然後跑靜態分析。
CD — 持續交付(Continuous Delivery)
對應「出餐口」。菜做好了,品管也過了,放在出餐口等著。但什麼時候端出去?由你決定。
程式碼隨時可以上線,但需要有人按下那個「部署」按鈕。適合需要人工審核的場景,比如金融系統、醫療軟體,或是需要經過認證才能出貨的韌體產品。
CD — 持續部署(Continuous Deployment)
這就更猛了 — 菜做好、品管過了,自動送到客人桌上,連服務生都不用叫。
只要程式碼通過所有自動化測試,就直接部署到生產環境。Netflix 就是這樣做的,他們每天部署數千次程式碼變更。在韌體領域,這對應的是通過所有測試後自動產生 OTA(Over-the-Air)更新包,推送到已出貨的裝置上。
一句話區分三者:
CI = 每改一次就自動檢查 持續交付 = 隨時「能」上線 持續部署 = 隨時「會」上線
Pipeline 是什麼?你的程式碼生產線

Pipeline,中文叫「管線」或「流水線」,就是你的程式碼從「寫完」到「上線」之間要經過的所有自動化步驟。
軟體的 Pipeline
一條典型的 Web / APP Pipeline 長這樣:
程式碼提交 → 安裝依賴 → 自動建構 → 自動測試 → 安全掃描 → 部署上線
| 階段 | 白話文 | 做什麼 |
|---|---|---|
| Source | 你按了 git push | 程式碼推上去,觸發整條流水線 |
| Build | 把原料變成成品 | 編譯程式碼、安裝套件、打包 |
| Test | 品質檢查 | 跑單元測試、整合測試,確認沒壞 |
| Security | 安全檢查 | 掃描有沒有安全漏洞 |
| Deploy | 送出去 | 把通過檢查的版本部署到伺服器 |
韌體的 Pipeline
韌體的 Pipeline 架構類似,但有幾個獨特的階段:
程式碼提交 → 交叉編譯 → 靜態分析 → 單元測試 → HIL 測試 → 產生韌體包 → OTA / 燒錄
| 階段 | 白話文 | 做什麼 |
|---|---|---|
| Source | 你按了 git push | 跟軟體一樣,程式碼推上去觸發流水線 |
| Cross-compile | 用電腦編譯給晶片跑的程式 | 用 ARM GCC 等交叉編譯器產生目標平台的二進位檔 |
| Static Analysis | 幫你抓潛在的 bug | 用 Cppcheck、PC-lint 等工具檢查 MISRA-C 合規性 |
| Unit Test | 不用硬體也能測 | 在 Host 上用 Unity / Google Test 跑單元測試 |
| HIL Test | 接上真正的硬體測 | 硬體在環測試(Hardware-in-the-Loop),自動燒錄到開發板並驗證 |
| Artifact | 打包產出物 | 產生 .bin、.hex、OTA 更新包,存到儲存庫 |
重點是:這一切都是自動的。你只要 git push,剩下的機器全包。
CI/CD 到底能幫你做什麼?
這可能是你最想知道的部分。CI/CD 不是一個抽象的概念,它能做非常具體的事情。我把它分成四大類:
品質守門員
1. 自動跑測試 每次提交程式碼,自動執行單元測試、整合測試、端對端測試。不用再靠人記得「啊,我應該跑一下測試」。
2. 程式碼品質檢查 自動偵測程式碼風格問題、潛在 bug、過高的複雜度。軟體工程師用 ESLint、SonarQube;韌體工程師用 Cppcheck、PC-lint 做 MISRA-C 合規性檢查,確保程式碼符合汽車、醫療等產業的安全編碼標準。
3. 程式碼審查輔助 自動產生測試覆蓋率報告,讓 Code Review 有數據可看,不再只靠感覺。
安全防護盾
4. 漏洞掃描 自動掃描你用的第三方套件有沒有已知的安全漏洞。Palo Alto Networks 指出,將安全掃描嵌入 Pipeline 是現代 DevSecOps 的基本功。
5. 密鑰偵測 防止你不小心把 API Key 或密碼推到 GitHub 上(別笑,這種事每天都在發生)。韌體專案裡更常見的是不小心把加密金鑰、OTA 簽章私鑰或是量產用的 provisioning 資料推上去。
效率加速器
6. 自動建構與打包
軟體工程師不用再手動跑 npm run build 或打 Docker image。韌體工程師不用再手動開 IDE 按編譯,或是記住那串又臭又長的 arm-none-eabi-gcc 參數。機器幫你做,每次都一模一樣。
7. 自動部署 / 自動燒錄 軟體世界:一鍵部署到 staging 環境測試,通過後自動推到 production。根據 IBM 的報告,這能讓團隊從「每週部署一次」變成「每天部署多次」。韌體世界:自動燒錄到測試用的開發板,或是產生 OTA 更新包推送到裝置端。
8. 自動回滾 部署出問題?系統自動回到上一個穩定版本。在韌體 OTA 的場景裡,這對應的是 MCUboot 的 rollback 機制 — 如果新韌體開機失敗,自動回退到前一個版本。
團隊協作利器
9. 環境一致性 「在我的電腦可以跑啊」— 這句話有了 CI/CD 之後就不會再出現了。軟體專案用 Docker 統一環境;韌體專案用 Docker 包裝交叉編譯工具鏈,確保每個人用的 ARM GCC 版本都一樣。
10. 快速反饋迴圈 提交程式碼後幾分鐘內就知道有沒有問題,不用等到兩週後的整合測試才發現自己兩週前寫的東西是壞的。
韌體工程師的 CI/CD:不只是軟體的事

很多人聽到 CI/CD 會覺得「那是做網頁、做 APP 的人在用的吧?」這是一個非常常見的誤解。事實上,韌體開發可能比軟體開發更需要 CI/CD,原因很簡單:韌體出 bug 的代價更高。
網站掛了,你可以在五分鐘內重新部署。但如果已經出貨到客戶手上的 IoT 裝置韌體有 bug 呢?輕則要遠端 OTA 更新,重則要整批召回。根據 Parasoft 的白皮書,嵌入式系統的開發週期長、手動測試瓶頸多、韌體發布風險高,這些痛點 CI/CD 都能直接緩解。
韌體 CI/CD 的獨特挑戰
跟軟體相比,韌體做 CI/CD 有幾個額外的挑戰:
1. 交叉編譯(Cross-Compilation)
你寫程式碼的電腦是 x86 或 ARM Mac,但程式碼要跑在 Cortex-M4 或 RISC-V 晶片上。所以 CI 環境必須安裝正確的交叉編譯工具鏈。
解法很成熟:用 Docker 把整個工具鏈打包成映像檔。比如一個包含 arm-none-eabi-gcc 14.2、nRF Connect SDK 和 Zephyr RTOS 的 Docker image,團隊所有人和 CI 伺服器用同一個映像,徹底消除「在我電腦上可以編譯」的問題。
2. 硬體在環測試(Hardware-in-the-Loop, HIL)
軟體測試只需要虛擬機。但韌體最終要跑在真正的硬體上 — GPIO 的時序對不對?SPI 通訊正不正常?BLE 連線穩不穩定?這些只有接上真正的硬體才能驗證。

HIL 測試的做法是:在 CI 伺服器旁邊放一塊(或一組)開發板,Pipeline 跑到測試階段時,自動把韌體燒錄到開發板上,用自動化腳本驅動測試,讀取結果回報。GitLab 有一篇詳細的指南說明如何設定。
聽起來很複雜?確實比純軟體測試多了一步。但想想看 — 如果你有 10 個工程師同時開發,每個人改完程式碼都要搶那一塊開發板來手動測試,排隊的時間加起來有多恐怖?自動化之後,提交程式碼就排入佇列,機器自動幫你燒錄、測試、回報結果。
3. 靜態分析與合規性
如果你的韌體要用在汽車(ISO 26262)、醫療器材(IEC 62304)或航空(DO-178C)領域,程式碼必須符合 MISRA-C 或 CERT-C 等編碼標準。手動檢查這些規則?幾乎不可能。
CI Pipeline 裡放一個靜態分析步驟,每次提交自動檢查所有規則違反,在程式碼合併之前就攔住不合規的程式碼。開源的 Cppcheck 或商業的 IAR C-STAT、Parasoft C/C++test 都能做到。
4. OTA 更新包的自動化產生
對 IoT 產品來說,CI/CD 的最後一步不是「部署到伺服器」,而是「產生 OTA 更新包並簽章」。Pipeline 可以自動:
- 編譯韌體
- 計算 checksum
- 用私鑰簽章
- 上傳到 OTA 伺服器(如 Memfault、AWS IoT、Nordic nRF Cloud)
- 等待審核後推送到裝置
韌體 CI/CD 的實際 Pipeline 範例
這是一個用 GitHub Actions 建構 Zephyr RTOS 韌體的真實範例,改編自 Embedded CI/CD 社群的推薦做法:
name: Firmware CI
on:
push:
branches: [main, develop]
pull_request:
branches: [main]
jobs:
build:
runs-on: ubuntu-latest
container:
image: zephyrprojectrtos/ci:latest # 預裝好交叉編譯工具鏈的 Docker 映像
steps:
- uses: actions/checkout@v4
- name: 初始化 Zephyr workspace
run: |
west init -l .
west update
- name: 建構韌體(nRF52840 DK)
run: |
west build -b nrf52840dk/nrf52840 app/
- name: 靜態分析
run: |
cppcheck --enable=all --error-exitcode=1 \
--suppress=missingInclude \
src/
- name: 單元測試(Host 端模擬)
run: |
west build -b native_sim tests/
./build/zephyr/zephyr.exe
- name: 上傳韌體產物
uses: actions/upload-artifact@v4
with:
name: firmware-nrf52840
path: |
build/zephyr/zephyr.hex
build/zephyr/zephyr.bin
看到了嗎?結構跟軟體的 CI Pipeline 幾乎一樣 — 觸發、建構、測試、產出。差別只在用的工具不同:west build 取代 npm run build,cppcheck 取代 eslint,native_sim 模擬器取代瀏覽器測試。
軟體 vs 韌體 CI/CD 對照表
| 面向 | 軟體(Web / APP) | 韌體(嵌入式 / IoT) |
|---|---|---|
| 編譯 | 本機編譯器(gcc, node) | 交叉編譯器(arm-none-eabi-gcc) |
| 環境統一 | Docker + Node/Python 版本 | Docker + 工具鏈版本(SDK, RTOS) |
| 測試 | Jest, Pytest, Cypress | Unity, Google Test, HIL 測試 |
| 靜態分析 | ESLint, SonarQube | Cppcheck, PC-lint, MISRA-C 檢查 |
| 部署 | 伺服器部署、CDN 更新 | OTA 更新、燒錄到硬體 |
| 回滾 | 重新部署舊版 | MCUboot rollback |
| 合規性 | OWASP, SOC 2 | MISRA-C, ISO 26262, IEC 62304 |
| 額外挑戰 | 瀏覽器相容性 | 硬體相依性、記憶體限制、即時性 |
工具怎麼選?三分鐘搞懂

CI/CD 工具很多,但新手其實只要認識這幾個就夠了。我用選手機來比喻:
| 工具 | 像什麼 | 適合誰 | 一句話評價 |
|---|---|---|---|
| GitHub Actions | iPhone | 已經用 GitHub 的所有人 | 最容易上手,生態最豐富 |
| GitLab CI | 三星旗艦 | 想要一站式平台的團隊 | 從程式碼到部署全包 |
| Jenkins | 組裝電腦 | 需要極度客製化的企業 | 什麼都能做,但什麼都要自己裝 |
根據 2026 年的數據,68% 的 GitHub 開源專案使用 GitHub Actions,而 GitLab CI 在企業市場年增長率達 34%。
韌體專案的工具選擇
韌體專案有些額外考量:
- GitHub Actions:適合大多數韌體團隊。可以用 Docker container 跑交叉編譯,也有人分享了 NXP、STM32、Zephyr 等平台的範例。
- GitLab CI:如果你的團隊需要 HIL 測試和合規性追蹤,GitLab 有比較好的整合方案。
- Jenkins:如果你用的是商業編譯器(如 IAR、Keil)且授權綁定特定機器,Jenkins 的 self-hosted runner 比較容易設定。
- IAR Build Tools:IAR 有專門的嵌入式 CI/CD 方案,可以在 Pipeline 裡跑 IAR 編譯器和靜態分析。
我的建議:不管軟體還是韌體,如果你是新手,直接用 GitHub Actions 開始。不需要額外設定伺服器,免費額度對個人專案綽綽有餘,而且網路上的教學資源最多。
動手做:你的第一個 CI Pipeline

說了這麼多,不如直接動手。
軟體版:Node.js 專案
在你的專案根目錄建立 .github/workflows/ci.yml:
# 這個檔案告訴 GitHub:每次有人推程式碼,就自動做以下的事
name: CI Pipeline # Pipeline 的名字,隨你取
on: # 什麼時候觸發?
push: # 有人推程式碼的時候
branches: [main] # 只針對 main 分支
pull_request: # 或是有人開 PR 的時候
branches: [main]
jobs: # 要做哪些工作?
test: # 工作名稱:test
runs-on: ubuntu-latest # 在 Ubuntu 虛擬機上跑
steps: # 具體步驟
- uses: actions/checkout@v4 # 第一步:把程式碼拉下來
- uses: actions/setup-node@v4 # 第二步:安裝 Node.js
with:
node-version: '20'
- run: npm install # 第三步:安裝套件
- run: npm test # 第四步:跑測試
就這樣,22 行 YAML。
韌體版:C/C++ 嵌入式專案
如果你的專案用 CMake + ARM GCC,可以這樣寫:
name: Firmware CI
on:
push:
branches: [main]
pull_request:
branches: [main]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
# 安裝 ARM 交叉編譯工具鏈
- name: 安裝 ARM GCC
run: |
sudo apt-get update
sudo apt-get install -y gcc-arm-none-eabi
# 用 CMake 建構韌體
- name: 建構韌體
run: |
mkdir build && cd build
cmake -DCMAKE_TOOLCHAIN_FILE=../cmake/arm-gcc.cmake ..
make -j$(nproc)
# 靜態分析
- name: 靜態分析(Cppcheck)
run: |
sudo apt-get install -y cppcheck
cppcheck --enable=warning,style --error-exitcode=1 src/
# 單元測試(在 Host 端跑,不需要硬體)
- name: 單元測試
run: |
cd tests
mkdir build && cd build
cmake ..
make -j$(nproc)
ctest --output-on-failure
# 保存編譯產物
- name: 上傳韌體
uses: actions/upload-artifact@v4
with:
name: firmware
path: build/*.bin
兩個版本的結構完全一樣:觸發 → 環境設定 → 建構 → 測試 → 產出。只是用的工具不同而已。
把這個檔案推上 GitHub,到 Actions 頁籤,你會看到一個綠色的勾勾(或紅色的叉叉,如果測試沒過的話)。
新手常踩的坑
每個剛接觸 CI/CD 的人都會踩到一些坑,不管你是做軟體還是韌體:
坑 1:Pipeline 跑太久
現象:每次 push 要等 20 分鐘才知道結果。 原因:沒有快取依賴套件或工具鏈,每次都重新下載。 解法:加上快取設定。
軟體專案 — 快取 node_modules:
- uses: actions/cache@v4
with:
path: node_modules
key: ${{ runner.os }}-node-${{ hashFiles('package-lock.json') }}
韌體專案 — 快取工具鏈和 Zephyr modules:
- uses: actions/cache@v4
with:
path: |
<sub>/zephyr-sdk
</sub>/.cache/west
key: ${{ runner.os }}-zephyr-${{ hashFiles('west.yml') }}
根據 Somco Software 的分析,有效的快取策略可以讓嵌入式 CI 的建構時間減少 50% 以上。
坑 2:測試在本機過但 CI 上掛掉
現象:「我本機明明可以跑!」 原因:軟體專案通常是環境變數或版本差異。韌體專案更常見的是工具鏈版本不同 — 你本機用 ARM GCC 13.2,CI 上裝的是 12.3,某些 C11 語法或 linker 行為不一樣。 解法:用 Docker 統一環境。韌體專案特別建議把整個工具鏈封裝成 Docker image,團隊和 CI 共用同一個。
坑 3:把密鑰寫死在設定檔裡
現象:CI 設定檔裡直接寫 API_KEY=sk-xxxxx。韌體專案更危險的是把 OTA 簽章私鑰或量產金鑰放進 repo。
原因:圖方便。
解法:用 GitHub 的 Secrets 功能。在 Settings → Secrets 裡設定,然後在 YAML 裡用 ${{ secrets.OTA_SIGNING_KEY }} 存取。永遠不要把密鑰寫在程式碼裡。
坑 4:韌體專用 — 忽略二進位檔大小監控
現象:某天韌體突然塞不進 Flash。
原因:MCU 的 Flash 通常只有 256KB 到 1MB,沒有人注意到每次 commit 都在慢慢長大。
解法:在 Pipeline 裡加一步,自動檢查 .bin 檔大小,超過閾值就報錯:
- name: 檢查韌體大小
run: |
MAX_SIZE=262144 # 256KB
ACTUAL_SIZE=$(stat -c%s build/zephyr/zephyr.bin)
echo "Firmware size: $ACTUAL_SIZE bytes (max: $MAX_SIZE)"
if [ "$ACTUAL_SIZE" -gt "$MAX_SIZE" ]; then
echo "ERROR: Firmware exceeds Flash limit!"
exit 1
fi
坑 5:韌體專用 — 只在 Host 端測試,不做 HIL
現象:單元測試全過,但燒到板子上行為異常。 原因:Host 端模擬測不到硬體特有的行為 — 中斷時序、DMA 傳輸、周邊裝置互動。 解法:至少設定一塊開發板做基本的 HIL 測試。可以用 self-hosted runner 連接到實體硬體。初期不需要很複雜,能自動燒錄 + 跑 smoke test 就是巨大的進步。
為什麼你應該現在就開始

CI/CD 表面上是一套工具,但本質上是一種開發文化的轉變。
它逼你思考幾個重要的問題:你的程式碼有測試嗎?你的建構流程可以被重複嗎?你敢在星期五下午部署嗎?你的韌體如果改了藍牙模組,有沒有自動驗證 Wi-Fi 功能沒被影響?
JetBrains 的調查指出,導入 CI/CD 的團隊不只是部署變快了,而是整個開發流程的品質都提升了 — 因為自動化迫使你把每個步驟都想清楚、寫清楚。
Infolitz 的嵌入式 CI/CD 專文也指出,嵌入式系統向來以開發週期長、手動測試瓶頸多、韌體發布風險高聞名 — 但現代工程團隊已經證明,這些問題都可以透過 CI/CD 自動化來解決。
2025 年的數據更驚人:76% 的 DevOps 團隊已經把 AI 整合進 CI/CD,用 AI 來自動選擇該跑哪些測試、偵測 Pipeline 中的異常模式。不只軟體,嵌入式領域的 CI/CD 也在快速演化 — IAR、Memfault 等廠商都在提供更完善的嵌入式 DevOps 工具鏈。
但不管工具怎麼變,核心理念始終沒變:讓機器做機器擅長的事,讓人做人擅長的事。
重複性的編譯、測試、燒錄、部署?交給機器。創造性的架構設計、硬體選型、產品決策?留給你自己。
你的下一步
如果這篇文章讓你對 CI/CD 有了基本概念,這裡是你可以做的下一步:
軟體工程師
- 今天就做:在你的任何一個 GitHub 專案裡,加上那 22 行 YAML
- 這週嘗試:為專案寫幾個簡單的單元測試,讓 CI 有東西可以跑
- 這個月挑戰:加上自動部署,讓程式碼推上去就自動更新到伺服器
韌體工程師
- 今天就做:把你的韌體建構流程寫成 GitHub Actions,至少做到「自動編譯不報錯」
- 這週嘗試:加上 Cppcheck 靜態分析和韌體大小檢查
- 這個月挑戰:把單元測試框架(Unity 或 Google Test)整合進 Pipeline,用
native_sim或 Host 端模擬跑測試 - 下個季度目標:設定一塊開發板做 HIL 測試,自動燒錄 + smoke test
不需要一次到位。CI/CD 是一段旅程,不是一個開關。先從最簡單的「自動編譯 + 自動測試」開始,慢慢加上更多自動化步驟。
你會發現,當那個綠色勾勾第一次亮起來的時候,那種安心感是無價的。
延伸閱讀
通用 CI/CD
- Red Hat — What is CI/CD? — 最權威的 CI/CD 概念說明
- GitLab — CI/CD 概念介紹 — 另一個角度的完整解釋
- IBM — CI/CD Pipeline 指南 — 企業級視角
- GitHub Actions 官方文件 — 動手做的時候看這個
- JetBrains — 12 Benefits of CI/CD — 想說服老闆導入 CI/CD 的時候用
韌體 / 嵌入式 CI/CD
- Dojo Five — CI/CD for Firmware Teams — 韌體團隊導入 CI/CD 的實戰指南
- Parasoft — CI/CD for Embedded Systems — 嵌入式 CI/CD 白皮書
- Embedded CI/CD 社群 — 開源嵌入式 CI/CD 框架
- MCU on Eclipse — CI/CD with Docker and GitHub Actions — NXP LPC55S16 的完整範例
- GitLab — 4 Ways to Accelerate Embedded Development — HIL 測試與合規性整合
- IAR — Embedded CI/CD — 商業級嵌入式 CI/CD 方案
- Somco Software — CI for Embedded Systems — 嵌入式持續整合深度分析
本文最初發布於 HackMD @BASHCAT。
留言
張貼留言