CI/CD 到底是什麼?
前言
CI/CD 是一種軟體開發的實踐方法,目的是透過自動化的流程,來頻繁地將程式碼整合、測試並部署到應用程式中。
簡單來說,它就像是一條自動化生產線:
- 你寫好程式碼 (原料)。
- 機器自動幫你檢查有沒有語法錯誤、邏輯錯誤 (組裝與品管)。
- 機器自動幫你把程式打包好,送到客戶手上 (包裝與出貨)。
CI:持續整合 (Continuous Integration)
CI 的重點在於「頻繁」且「自動」地檢查程式碼品質。
當多個開發者同時在開發同一個專案時,CI 伺服器會在開發者將程式碼上傳 (Push) 到儲存庫 (如 GitHub/GitLab) 後,自動執行以下動作:
- 還原套件 (Restore):檢查專案依賴的 NuGet 套件是否都能下載。
- 建置 (Build):執行
dotnet build,確保程式碼可以成功編譯成 DLL 或執行檔,沒有語法錯誤。 - 測試 (Test):執行
dotnet test,跑過所有的單元測試 (Unit Test),確保新的修改沒有壞掉原本的邏輯。
優點:
- 提早發現錯誤:不用等到最後要發佈時才發現編譯失敗。
- 解決合併衝突:強迫大家頻繁同步程式碼。
CD:持續交付/部署 (Continuous Delivery / Deployment)
CD 接續在 CI 之後,重點在於將通過測試的程式碼發佈出去。這裡分成兩個層次:
1. 持續交付 (Continuous Delivery)
自動化流程會將程式碼建置好,並部署到測試環境 (Staging)。但是,要發佈到正式環境 (Production) 時,通常需要人工審核 (Manual Approval)或是手動按下按鈕,這在企業級應用很常見。
2. 持續部署 (Continuous Deployment)
更進階的自動化。只要通過 CI 的所有測試,程式碼就會自動部署到正式環境給使用者使用,完全不需要人工介入。
C# 專案常見的 CD 動作:
- 執行
dotnet publish產出發佈檔案。 - 將檔案複製到 IIS 伺服器。
- 或是打包成 Docker Image 推送到雲端 (如 Azure App Service)。
- 修改
web.config或appsettings.json的連線字串 (針對不同環境)。
C# 開發者的 CI/CD 工具
身為 .NET 開發者,我們最常用的工具有:
- Azure DevOps (ADO):微軟自家的工具,對 .NET 支援度最好,企業最愛用。
- GitHub Actions / GitLab CI:目前最流行的選擇,開源專案免費,設定檔 (YAML) 簡單直觀。
- Jenkins:老牌的 CI/CD 工具,自由度高但設定較繁瑣。
CI 範例
GitHub Actions
假設我們有一個 .NET Core 的 Console 專案,一個簡單的 CI 設定檔 (.yml) 長得像這樣:
1 | name: .NET Core CI |
在 GitHub Actions 的 steps (步驟) 中,主要有兩種指令方式:
run:執行你在終端機 (Terminal) 會打的指令。- 例如:
dotnet build、echo "Hello"。
- 例如:
uses:使用別人寫好的套件 (Action)。- 這就像是 C# 中的
using或是呼叫 NuGet 套件一樣。你不需要自己寫幾百行 Shell Script 來完成複雜的工作,直接「引用」別人做好的功能即可。
- 這就像是 C# 中的
actions/checkout@v2 是一個官方提供的 Action,它的作用就是:「登入你的 GitHub 儲存庫,把你的程式碼 git pull 下來放到虛擬機的資料夾中。」當 GitHub Actions 啟動一台虛擬機 (Runner) 準備幫你跑 CI 時,這台虛擬機是空的、乾淨的,裡面完全沒有你的程式碼。如果你不執行這一步,直接跑 dotnet build,系統會報錯說:「找不到專案檔」,因為資料夾是空的。
GitLab CI (.gitlab-ci.yml)
1 | # 1. 指定環境 (Image) |
比較:GitLab vs GitHub 的 CI 差異
這兩個範例做的事情是一模一樣的,但寫法有幾個很重要的不同的地方:
1. uses: actions/checkout 去哪了?
GitLab 自動幫你做了!
這是最大的不同。GitLab CI 在執行任何 Job 之前,預設會自動執行 git clone/fetch 把你的程式碼抓下來。所以你不需要像 GitHub 那樣顯式地寫一行 checkout 指令。
2. uses: actions/setup-dotnet 去哪了?
被 image: ... 取代了。
- GitHub:是用
ubuntu-latest(通用環境),然後透過steps去安裝 .NET。 - GitLab:更傾向直接指定一個 Docker Image (
mcr.microsoft.com/dotnet/sdk:6.0)。這就像是直接租了一間「.NET 專用廚房」,進去的時候工具都已經掛在牆上了,不用再安裝。
3. 語法對照表
| 功能 | GitHub Actions | GitLab CI |
|---|---|---|
| 執行指令 | run: dotnet build |
script: - dotnet build |
| 使用外掛/套件 | uses: ... |
比較少用,通常靠 Docker Image 預裝 |
| 觸發條件 | on: push: branches: ... |
only: - master |
| 環境/虛擬機 | runs-on: ubuntu-latest |
image: mcr.microsoft.com/... |
從 CI 到 CD,以部屬到 AWS 為例
目前為止我們做的事情 (Restore, Build, Test),都只是在「工廠內部」把產品做出來並確保品質良好 (CI)。但是,如果產品做好了卻放在倉庫長灰塵,客戶 (使用者) 是看不到的。
CD (持續部署) 的過程,說穿了就是把這個做好的「產品包」(Artifact),自動搬運到 AWS 伺服器上。
要在 YAML 裡面實現部署到 AWS,通常需要兩個關鍵步驟:
- 身分驗證:CI/CD 機器人需要有權限才能操作你的 AWS 帳號 (就像你要給外送員大門鑰匙)。
- 傳遞產物 (Artifact):把 CI 階段編譯好的檔案 (.dll, .exe, .zip) 傳給 CD 階段。
以最常見的 AWS CLI (Command Line Interface) 方式來示範。想像一下,這就是讓機器人幫你輸入你平常在終端機打的指令。
前置作業:設定帳號、密碼和 tocken
在寫 YAML 之前,絕對不能把 AWS 的帳號密碼直接寫在程式碼裡!這是資安大忌。
你需要去 GitHub 或 GitLab 的後台設定 Variables (Secrets),把這兩個東西存進去:
AWS_ACCESS_KEY_ID(帳號)AWS_SECRET_ACCESS_KEY(密碼)
CI/CD 執行時,會自動從後台讀取這些變數。
GitHub Actions
GitHub 的邏輯是拆成兩個 Job:build 和 deploy。因為 deploy 必須等 build 成功才能做,所以會用到 needs 關鍵字。
關鍵字:needs 與 artifact
1 | name: Deploy to AWS |
解析:
needs: build:這建立了依賴關係。如果你不寫這個,GitHub 會同時跑 Build 和 Deploy,那時候還沒編譯好,部署一定會失敗。upload-artifact/download-artifact:這就像大隊接力。CI 跑完把棒子 (編譯好的檔案) 交出去,CD 接棒後繼續跑。因為 GitHub 的每個 Job 都是獨立的全新虛擬機,檔案不會自動保留,必須透過這個動作傳遞。upload-artifact和download-artifact兩邊的path不一樣:- 上傳階段 (
upload-artifact) 是打包- 情境:你的
dotnet publish指令剛剛把編譯好的檔案放在了output這個資料夾裡。 - 動作:GitHub Actions 會去
output/資料夾把所有檔案抓出來,打包成一個叫做my-app-package的壓縮檔存到雲端。
- 情境:你的
- 下載階段 (
download-artifact) 是拆包- 情境:現在是一個全新的虛擬機 (Deploy Job),裡面空空如也。
- 動作:GitHub Actions 從雲端把
my-app-package下載下來,然後自動建立一個叫做app-files/的資料夾,把內容物全部解壓縮進去。
- 上傳階段 (
GitLab CI
GitLab 的邏輯比較直觀,透過 stages 來控制順序。而且 GitLab 有一個很強大的特性:Artifacts (產物) 會自動傳遞給下一個 Stage,不用像 GitHub 那樣顯式地下載。
關鍵字:artifacts 與 image
1 | # 定義順序:先 Build 再 Deploy |
解析:
image的切換:build_job用的是微軟的 image(因為要編譯 C#)。deploy_to_aws換成了 AWS 的 image(因為要下aws指令)。這是 GitLab CI 最靈活的地方,每個步驟可以用最適合的工具箱。
- 隱藏的 AWS 變數:GitLab 會自動把你在後台設定的
AWS_ACCESS_KEY_ID注入到環境變數中,所以你在script裡不需要寫登入指令,aws指令會自動讀取這些變數。
比較:GitLab vs GitHub 的 CD 差異
| 特性 | GitHub Actions | GitLab CI |
|---|---|---|
| 流程控制 | 使用 needs: build |
使用 stages 定義順序 needs 是用來無視 stages 的等待規則 |
| 檔案傳遞 | upload-artifact (上傳) download-artifact (下載) |
artifacts (定義後自動傳遞) |
| AWS 工具 | 使用 uses: aws-actions/... |
使用 image: .../aws-base |
| 部署指令 | 手寫 run: aws ... |
手寫 script: aws ... |
部署到 AWS 的方式有很多種(S3, EC2, Elastic Beanstalk, Lambda),上面的範例是用最通用的 AWS CLI 指令。
實際工作上,那行 aws s3 cp ... 可能會換成:
aws elasticbeanstalk update-environment ...(如果是用 EB)dotnet lambda deploy-function ...(如果是用 Lambda)
但不變的核心邏輯都是:CI 產出檔案 -> 傳遞檔案 -> CD 取得權限 -> CD 執行指令將檔案送上雲端。
總結
- CI (持續整合) = 自動 Build + 自動 Test。確保程式碼是「健康的」。
- CD (持續交付/部署) = 自動 Publish + 自動 Deploy。確保程式碼能「送到使用者手上」。