iT邦幫忙

2026 iThome 鐵人賽

DAY 26
0

一個版本部署完成後,如果過一陣子回頭問:當時測了什麼、誰核准、部署的是哪一個建置產物,還找得到嗎?整理 CI/CD 時,我除了讓每次交付都能重複執行,也希望這些問題都能從流程留下的紀錄裡找到答案。

本篇名詞小筆記

  • CI/CD:持續整合與持續交付/部署(Continuous Integration/Continuous Delivery or Deployment),自動執行建置、測試與交付流程。
  • Artifact:建置產物,例如可部署的 JAR、容器映像或安裝套件。
  • Commit SHA:Git Commit 的唯一識別值,用來把建置產物對回確切的原始碼版本。
  • Environment Protection:部署環境保護規則,例如指定核准人、分支與必要檢查後才能部署。
  • 證據鏈:把需求、程式碼、建置、測試、核准到部署的紀錄串起來,任何一環都能對回前後環節,事後才查得出一個版本的來龍去脈。

今天要解決的問題

Pipeline 自動部署,省下了很多手動操作;但事後查問題時,還需要找回這個版本的測試結果、核准紀錄與建置產物。要留下哪些資料,應該在設計流程時就決定,等到要查時才補,往往已經來不及。

CI 平台的執行紀錄與建置產物多半有保存期限,到期就會自動刪除;如果沒有依追查需要調整,時間一久,當時的結果就找不到了。所以我會先確認各種紀錄留多久,以及建置產物能不能對回需求與程式碼。

架構師視角:交付流程即證據鏈

https://ithelp.ithome.com.tw/upload/images/20260926/20184230x8Mp8KupvQ.png

圖 Day 26-1:可稽核 CI/CD。

我希望每一關都留下結果:從需求、程式碼,到建置、測試、掃描、核准、部署與部署後的 smoke test,都能一路對應起來,接手的人不用重新猜當時做過什麼。所以圖的最後一格是 Evidence,它不是另一個步驟,而是前面各關留下的紀錄合起來的結果。

不過,這些紀錄要能對上,前提是部署出去的和測試過的是同一個 image。所以我採用 Build once, deploy many:image 只建置一次,之後每個環境都部署同一個 image,不重新建置。因為重新建置時,相依套件或基底 image 可能已經更新,部署的就不一定是測試過的那一份。

工程師視角:版本識別、secret 與核准紀錄

部署與驗證紀錄要對應到同一個版本,image 本身就要帶著能對回原始碼的識別資訊。所以每個 image 除了一般的 tag,都另外加一個由 commit SHA 組成的 tag。

在 GitHub Actions 裡,版本識別、核准與 secret 這幾項要求,都落在幾行設定上。以下是目前 workflow 的簡化節錄:

jobs:
  build-push:
    needs: test                          # 測試沒過就沒有 image
    permissions:
      contents: read
      packages: write                    # 只給推 registry 需要的權限
    steps:
      - uses: docker/build-push-action@v7
        with:
          push: true
          tags: |
            ghcr.io/<org>/${{ matrix.service }}:${{ steps.tag.outputs.tag }}   # main 為 latest,其他分支 develop-latest
            ghcr.io/<org>/${{ matrix.service }}:sha-${{ github.sha }}   # 每個 image 都能對回 commit

  deploy:
    needs: build-push
    if: github.ref_name == 'main'
    environment: production              # GitHub Environment 保護:可設定 required reviewers
    steps:
      - uses: appleboy/ssh-action@v1.2.5
        with:
          host: ${{ secrets.DEPLOY_HOST }} # 連線資訊全部走受控 secret
          key: ${{ secrets.DEPLOY_SSH_KEY }}
          script: |
            cd /opt/app
            test -f .env || { echo "ERROR: .env missing on server"; exit 1; }   # .env 不經 pipeline 傳輸
            export TAG=sha-${{ github.sha }}   # 部署剛建好的那一版,不是 latest
            docker compose pull && docker compose up -d --remove-orphans
            curl -sf --max-time 10 http://localhost:9090/actuator/health || exit 1   # smoke test

這段有三個我刻意留下的痕跡。一是因為 latest 會隨下一次建置移動,事後看不出當時跑的是哪一版,所以 image 的 tag 由 commit SHA 組成,部署時也指定這個 tag。二是 deploy job 綁在 production environment 上,在平台設好 required reviewers 之後,核准者與時間就會由平台記錄。三是 .env 從不經過 pipeline,連線帳密就不會出現在 CI 的 log 裡;伺服器上找不到就直接失敗,而不是悄悄用預設值跑起來。

Secret 放在 GitHub Secrets,不寫進 repository 或 Log。GitHub 會自動遮掉 log 裡出現的 secret,但主要靠比對完全相同的字串;如果 secret 經過 base64 等轉換,或被包在 JSON、YAML 裡,印出來時就可能比對不到而留在 log 裡。所以除錯時不要把設定整份印出來,轉換過的值也要用 ::add-mask:: 另外登錄遮罩。

我希望最後串起來的證據鏈是:PR 透過範本裡的相關單號連到需求,pipeline 留下測試、掃描、核准與部署結果,正式環境再配合 Environment Protection 與最小權限。緊急變更也要保留核准,事後補齊原因與驗證,證據鏈才不會在這裡斷掉一段。

策略取捨與限制

取捨 這樣選的理由 何時要重新評估
Build once, deploy many 正式環境跑的必須是測試過的 image 無
檢查關卡先統一,再視需要依風險分級 低風險變更不必承擔全部檢查成本 變更量變大、統一檢查拖慢交付時
必要檢查不可略過 能略過的檢查,趕時間時很容易被略過 無
緊急變更事後補齊證據 允許快速處理,但不放棄可追溯性 緊急變更比例過高時,檢討常規流程

分級的意思是:低風險的變更可以少跑幾關,但標為必要的那幾關,不論風險高低都要跑完。能穩定自動判斷的,就寫成自動檢查;需要人判斷的,也要寫清楚該看什麼,否則核准很容易變成只是按一下同意。

驗證方式與衡量指標

指標 計算方式 想回答的問題
部署頻率 單位時間的部署次數 交付是否順暢?
Lead Time for Changes 從 commit 到上線的時間 流程是否有瓶頸?
Change Failure Rate 需修復或回退的部署/全部部署 檢查關卡是否有效?
pipeline 成功率 成功執行數/全部執行數 流程本身是否穩定?
回退時間 從決定回退到完成的時間 出問題時能多快恢復?

如果部署變頻繁,失敗率卻也上升,就要繼續查品質與流程出了什麼變化。這種情況要幾項對照才看得出來,所以我會把這五項放在一起看。前面 Day 03 建好的定義與基準,到了這裡就能拿來對照。

今天先整理到這裡

今天想留下的,是一份能重複執行,也能回頭說明的交付流程。下一篇,再談部署平台,看看 Docker Compose 與 Kubernetes 要怎麼依需求選擇。

參考資料

  1. GitHub, Understand GitHub Actions,查閱日期:2026-10-09。
  2. GitHub, Secure use reference,查閱日期:2026-10-09。
  3. GitHub, Configuring the retention period for GitHub Actions artifacts and logs in your organization,查閱日期:2026-10-09。

上一篇
Day 25|Prometheus、Grafana、Jaeger:從 Dashboard 到可行動告警
系列文
30 天把舊 ERP 整合成現代微服務平台:架構治理與工程實作雙軌實戰 共 26 篇
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言