在2025年的技術環境中,軟體發布速度與更新頻率已成為企業競爭力的關鍵指標。為了在市場中保持領先地位,公司必須不斷縮短新功能的開發與佈署時間。持續整合(CI)與持續交付/佈署(CD)的方法學已經從選項變成了必需品。

在我帶領團隊實施多個大型專案的過程中,CI/CD流程一直是提升團隊效率的核心支柱。CI/CD不僅是一套工具,更是一種思維方式,它能徹底改變團隊協作與軟體交付的方式。

CI/CD為組織帶來的關鍵優勢

當企業成功匯入CI/CD流程時,會獲得以下顯著效益:

  • 測試時間大幅縮減:自動化測試流程消除了手動測試的瓶頸,加速了反應迴圈
  • 整合階段錯誤最小化:頻繁的小型變更比大規模更新更容易測試和識別問題
  • 加速產品上市時間:自動化佈署流程讓開發團隊能夠更快地推出更新
  • 簡化版本管理:每個程式碼變更都可以被封裝成獨立的版本,使追蹤和管理變得更加簡單

多階段Docker映像建置:精簡與最佳化

在我多年的DevOps實踐中,多階段Docker映像建置(Multi-stage builds)始終是我推薦給團隊的首要最佳實踐。這種方法不僅能顯著減小最終映像的大小,還能提升安全性與佈署效率。

多階段建置的工作原理是將Docker建置過程分為多個階段,只將必要的程式碼和依賴函式庫最終映像,而將建置工具、暫存檔案和中間產物留在建置階段。這帶來了幾個顯著優勢:

  • 映像體積顯著縮小:在一個實際專案中,我曾透過多階段建置將微服務容器映像從1.2GB縮減至120MB,提升了佈署速度與資源利用效率
  • 相依性管理簡化:分離建置環境和執行環境,讓相依性管理變得更加清晰
  • 減少安全風險面:移除不必要的函式庫具,降低潛在漏洞的攻擊面

以下是一個Python應用程式的多階段Dockerfile範例:

# 建置階段
FROM python:3.11-slim AS builder

WORKDIR /app

COPY requirements.txt .
RUN pip wheel --no-cache-dir --no-deps --wheel-dir /app/wheels -r requirements.txt

# 最終階段
FROM python:3.11-slim

WORKDIR /app

# 僅複製必要檔案
COPY --from=builder /app/wheels /wheels
COPY --from=builder /app/requirements.txt .
RUN pip install --no-cache /wheels/*

COPY ./src .

CMD ["python", "app.py"]

這個範例展示瞭如何將依賴項建置與應用程式執行分離,確保最終映像只包含執行時必要的元素。

Kaniko:安全的容器映像建置工具

在安全敏感的環境中,特別是在Kubernetes叢集內佈署CI/CD流程時,Kaniko成為了我的首選工具。Kaniko允許在無需特權存取的情況下建置Docker映像,這使它成為CI/CD流程中的理想選擇。

在一個某專案中,我們面臨著嚴格的安全要求,無法使用傳統的Docker-in-Docker(DinD)方法。Kaniko提供了完美的解決方案,讓我們能夠在不犧牲安全性的情況下自動化容器映像建置。

Kaniko帶來的主要優勢包括:

  • 增強的安全性:不需要特權操作,避免了DinD可能帶來的安全風險
  • 與Kubernetes無縫整合:專為Kubernetes環境設計,簡化了在叢集中的建置流程
  • 快取機制最佳化:有效利用層級快取,顯著減少重複建置的時間

以下是在GitLab CI中使用Kaniko的設定範例:

build-image:
  stage: build
  image:
    name: gcr.io/kaniko-project/executor:debug
    entrypoint: [""]
  script:
    - mkdir -p /kaniko/.docker
    - echo "{\"auths\":{\"${CI_REGISTRY}\":{\"username\":\"${CI_REGISTRY_USER}\",\"password\":\"${CI_REGISTRY_PASSWORD}\"}}}" > /kaniko/.docker/config.json
    - /kaniko/executor --context $CI_PROJECT_DIR --dockerfile $CI_PROJECT_DIR/Dockerfile --destination $CI_REGISTRY_IMAGE:$CI_COMMIT_TAG
  only:
    - tags

在這個設定中,Kaniko執行器在GitLab CI環境中執行,使用專案目錄作為連貫的背景與環境,並將建置的映像推播到GitLab容器登入檔。

相比之下,Docker-in-Docker方法存在以下問題:

  • 需要特權容器,可能引入安全漏洞
  • 在Kubernetes環境中可能出現效能和穩定性問題
  • 設定複雜與難以維護

GitLab中的標籤策略與受保護分支

版本控制與分支管理是CI/CD流程中經常被忽視但卻至關重要的環節。在我管理的多個專案中,我一直強調GitLab的標籤(tagging)策略與受保護分支(protected branches)的重要性。

有效的標籤策略

標籤是追蹤應用程式版本的關鍵機制。我建議採用語義化版本(Semantic Versioning)方法,例如v1.2.3,其中:

  • 第一個數字(1)代表主要版本,有不向後相容的更改
  • 第二個數字(2)代表次要版本,有向後相容的功能新增
  • 第三個數字(3)代表修補程式版本,有向後相容的問題修復

在我負責的一個電子商務平台專案中,我們實施了嚴格的標籤策略,每個生產環境佈署都必須對應到一個Git標籤。這不僅提供了清晰的版本歷史,還使回復變得簡單可靠。

受保護分支的設計

受保護分支是確保程式碼品質和穩定性的關鍵機制。在GitLab中,我通常會設定以下受保護分支:

  • main/master:主分支,代表生產就緒的程式碼
  • develop:開發分支,整合所有功能分支
  • release/:發布準備分支,用於最終測試和準備生產佈署

透過限制誰可以推播和合併到這些分支,團隊可以確保關鍵程式碼不會受到意外更改的影響。

以下是GitLab中設定受保護分支的最佳實踐:

  1. 限制直接推播到主分支的許可權
  2. 要求合併請求(Merge Request)必須至少有一個批准才能合併
  3. 啟用合併前必須透過所有CI檢查的設定
  4. 對於生產分支,設定更嚴格的批准要求

CI範本函式庫準化與一致性的關鍵

在管理多個專案的經驗中,我發現建立一個集中的CI範本函式庫作流程標準化的有效方法。這種方法特別適合大型組織或有多個相似專案的團隊。

在一個擁有超過50個微服務的專案中,我設計了一個包含標準CI/CD設定的中央儲存函式庫大減少了每個新服務的設定時間,同時確保了整個組織的一致性。

CI範本函式庫的主要優勢包括:

  • 一致性:確保所有專案遵循相同的最佳實踐和流程
  • 維護簡化:當需要更新CI/CD流程時,只需在一個地方進行更改
  • 加速新專案啟動:新團隊可以快速採用已經證明有效的設定

以下是GitLab中實作CI範本的範例:

# 在中央範本函式庫義的分享CI設定
.build_template:
  stage: build
  image:
    name: gcr.io/kaniko-project/executor:debug
    entrypoint: [""]
  script:
    - mkdir -p /kaniko/.docker
    - echo "{\"auths\":{\"${CI_REGISTRY}\":{\"username\":\"${CI_REGISTRY_USER}\",\"password\":\"${CI_REGISTRY_PASSWORD}\"}}}" > /kaniko/.docker/config.json
    - /kaniko/executor --context $CI_PROJECT_DIR --dockerfile $CI_PROJECT_DIR/Dockerfile --destination $CI_REGISTRY_IMAGE:$CI_COMMIT_TAG
  only:
    - tags

然後在各個專案中參照這個分享範本:

# 在具體專案的.gitlab-ci.yml中
include:
  - project: 'ci-templates/gitlab-ci-templates'
    ref: master
    file: '/templates/build.yml'

build-image:
  extends: .build_template
  # 專案特定的覆寫設定可以在這裡增加

這種方法不僅標準化了流程,還允許各個專案在必要時進行自定義。

自動化測試:CI/CD成功的根本

雖然上面討論的實踐都很重要,但我必須強調自動化測試在CI/CD流程中的核心地位。沒有可靠的自動化測試,快速佈署只會更快地引入問題。

在我的實踐中,我推薦一個多層次的測試策略:

  1. 單元測試:測試個別元件,執行速度快,覆寫率高
  2. 整合測試:測試元件間的互動,確保系統各部分能協同工作
  3. 端對端測試:模擬真實使用者行為,測試完整系統

有效的測試自動化不僅能提早發現問題,還能增強團隊對程式碼變更的信心。這是實作真正持續佈署的必要條件。

監控與反應:完善CI/CD閉環

最後,值得一提的是監控與反應機制在CI/CD流程中的重要性。在佈署到生產環境後,持續監控應用程式的效能和穩定性是至關重要的。

我建議實施以下監控策略:

  • 技術指標監控:CPU使用率、記憶體消耗、回應時間等
  • 業務指標監控:交易量、使用者註冊、關鍵功能使用情況等
  • 錯誤追蹤:系統錯誤、異常和當機的即時通知

這些監控資料應該反應回CI/CD流程,形成一個完整的閉環。例如,如果佈署後發現效能下降,可以觸發自動回復或警示。

在我的一個專案中,我們實施了藍/綠佈署策略,結合自動化監控。新版本佈署後,系統會監控關鍵指標15分鐘,如果一切正常,流量會完全切換到新版本;如果發現問題,系統會自動回復到之前的穩定版本。

CI/CD不僅是關於快速佈署,更是關於安全、可靠地交付高品質軟體。透過結合本文討論的最佳實踐,團隊可以建立一個既高效又可靠的軟體交付流程,為2025年及以後的挑戰做好準備。

隨著技術的不斷演進,CI/CD實踐也將持續發展。保持學習和適應新工具、方法的心態,將幫助團隊在競爭激烈的技術領域中保持領先地位。透過精心設計的CI/CD流程,開發團隊可以專注於創造價值,而不是被繁瑣的手動流程所拖累。

靈活觸發規則最佳實踐

在持續整合與持續佈署(CI/CD)流程中,觸發規則的設計至關重要。玄貓在多年的DevOps實踐中發現,並非每個程式碼變更都需要執行完整的管道流程。合理設計觸發規則不僅能節省資源,還能提高開發效率。

智慧觸發策略

根據專案特性設計觸發條件是提升CI/CD效率的關鍵。例如:

  • 目錄或檔案變更觸發:只在特定目錄或檔案變更時觸發相關管道
  • 事件限制觸發:僅在特定事件(如合併至受保護分支)時執行佈署管道

在本文中,玄貓將使用以下工具展示實用的CI/CD最佳實踐:

  • GitLab CI:用於組織建置流程,包括Docker映像的建置
  • ArgoCD:用於應用程式佈署
  • Vault:用於安全儲存和使用機密,與GitLab CI整合

接下來,讓我們透過實際案例來探討這些最佳實踐。

CI/CD最佳實踐:使用GitLab CI建置應用程式

多階段Dockerfile例項

首先,讓我們建立一個多階段(multi-stage)Dockerfile,這能幫助我們產生最小化與最佳化的應用程式映像。以下是一個PHP應用程式的範例:

## 共用成品階段
FROM registry.examle.ru/infra/ubuntu-base:common AS common_artifact

SHELL ["/bin/bash", "-o", "pipefail", "-c"]

# 下載建置工具
RUN su app -c 'wget --quiet https://repo1.maven.org/maven2/com/google/javascript/closure-compiler/v.../closure-compiler-v....jar -O  /opt/closure-tools/compiler.jar --no-check-certificate'

# 複製應用程式碼
COPY --chown=app:app . /app

# 設定資料函式庫引數
ARG db_string
ARG db_user_name
ARG db_password
ARG db_host
ARG db_port
ARG db_name

# 設定主機解析並執行PHP命令
RUN echo "${db_host}  pgsql.db" >> /etc/hosts && \
    /usr/bin/php some commads

# 複製並執行JavaScript最小化指令碼
COPY --chown=app:app ./.infra/files/js_min.sh /app/js_min.sh
RUN su app -c '/bin/bash /app/js_min.sh'

WORKDIR /app/somedir/protected
RUN su app -c '/bin/bash gulpc'

### 前端階段
FROM registry.examle.ru/infra/nginx-upstream-limit:nginx-gc AS frontend

# 複製前端設定檔
COPY ./.infra/files/frontend/nginx.conf /etc/nginx/nginx.conf
COPY ./.infra/files/frontend/content /app/somedir/content
COPY . /app

# 清理不需要的目錄及檔案
RUN rm -rf /app/somedir/protected/modules \
    /app/somedir/protected/resource \
    /app/somedir/js \
    /app/_sql/sqlLocalizationUpdate*

# 從共用成品階段複製必要檔案
COPY --from=common_artifact /app/somedir/protected/modules /app/somedir/protected/modules
COPY --from=common_artifact /app/somedir/protected/resource /app/somedir/protected/resource
COPY --from=common_artifact /app/somedir/js /app/somedir/js
COPY --from=common_artifact /app/_sql/sqlLocalizationUpdate* /app/_sql/

## 後端階段
FROM registry.examle.ru/infra/ubuntu-base:backend AS backend

SHELL ["/bin/bash", "-o", "pipefail", "-c"]

# 設定地區和語言
ENV LC_ALL=ru_RU.UTF-8
ENV LANG=ru_RU.UTF-8

# 複製並設定字型
COPY --chown=app:app ./.infra/files/backend/fonts /usr/share/fonts/TTF
RUN chown -R app:app /usr/share/fonts/TTF && chmod -R 0755 /usr/share/fonts/TTF

# 複製各種設定檔
COPY ./.infra/files/frontend/wp_content /app/somedir/wp-content
COPY --chown=app:app ./.infra/files/somedir/cron.d /etc/cron.d
COPY --chown=app:app ./.infra/files/somedir/somedir /etc/somedir
COPY --chown=app:app ./.infra/files/somedir/d /etc/service/d
COPY --chown=app:app ./.infra/files/somedir/supercronic_run /etc/service/supercronic/run
COPY --chown=app:app ./.infra/files/backend/php-fpm.conf /etc/php/7.4/fpm/php-fpm.conf
COPY --chown=app:app ./.infra/files/backend/php.ini /etc/php/7.4/cli/php.ini
COPY --chown=app:app ./.infra/files/backend/php-fpm.ini /etc/php/7.4/fpm/php.ini
COPY --chown=app:app . /app

WORKDIR /app

# 清理不需要的目錄並設定許可權
RUN rm -rf /app/somedir/protected/modules \
    /app/somedir/protected/resource \
    /app/somedir/js \
    /app/_sql/sqlLocalizationUpdate* && \
    mkdir -p /app/somedir/protected/runtime && \
    chown -R app:app /app/somedir/protected/runtime

# 從共用成品階段複製必要檔案
COPY --from=common_artifact --chown=app:app /app/somedir/protected/modules /app/somedir/protected/modules
COPY --from=common_artifact --chown=app:app /app/somedir/protected/resource /app/somedir/protected/resource
COPY --from=common_artifact --chown=app:app /app/somedir/js /app/somedir/js
COPY --from=common_artifact --chown=app:app /app/_sql/sqlLocalizationUpdate* /app/_sql/

這個Dockerfile分為三個階段:

  1. 共用成品階段(common_artifact):安裝相依套件、設定建置引數並準備應用程式。
  2. 前端階段(frontend):複製最終應用程式到映像中,替換並最佳化程式碼,使Docker映像體積最小化。
  3. 後端階段(backend):複製最終應用程式到映像中,替換並最佳化程式碼,使Docker映像體積最小化。

使用Kaniko的GitLab CI範本

為了建立更靈活與可擴充套件的流程,玄貓建議建立專門的CI/CD範本儲存函式庫種方法對於擁有大量專案的組織特別有用,因為它提供了統一的建置、測試和佈署方法。以下是一個範本儲存函式庫構範例:

CI/CD範本儲存函式庫

ci-templates/
├── build/
│   └── kaniko-build-template.yml
├── test/
│   └── test-template.yml
├── deploy/
│   └── argo-deploy-template.yml
└── .gitlab-ci.yml

目錄說明:

  • build/:包含Docker映像建置範本,如用於不同環境的Kaniko範本
  • test/:包含應用程式自動化測試範本(單元測試、整合測試)
  • deploy/:包含佈署範本,例如使用ArgoCD為不同環境(生產、預發布、開發)佈署
  • .gitlab-ci.yml:主檔案,包含來自其他目錄的範本並定義通用規則

Kubernetes時代的CI/CD挑戰與解決方案

在容器化應用程式開發日趨成熟的今日,DevOps團隊面臨著如何在Kubernetes環境中建立高效、安全與可擴充套件的CI/CD流程的挑戰。傳統的Docker-in-Docker(DinD)方法雖然常見,但在安全性和資源使用上存在明顯缺陷。

在我為多家企業建立雲端基礎設施的過程中,發現Kaniko與ArgoCD的組合能有效解決這些挑戰。Kaniko提供了無需Docker守護程式的容器映像檔構建能力,而ArgoCD則實作了根據GitOps的佈署流程,使整個CI/CD過程更加安全可靠。

Kaniko映像檔構建範本設計

為不同環境建立Kaniko構建範本

在專案中建立一個標準化的映像檔構建流程十分重要。我通常會在專案的build/目錄中建立一個kaniko-build-template.yml檔案,用於定義不同環境的構建任務:

stages:
  - build

include:
  - local: stage-ci.yml

.build:
  stage: build
  image:
    name: registry-url/infra/kaniko-executor:v1.16.0-debug
    entrypoint: [""]
  script:
    - mkdir -p /kaniko/.docker
    - cat ${CR_CONFIG} > /kaniko/.docker/config.json
    - touch new-test
    - echo "test" >> new-test
    - >
      /kaniko/executor
      --context "${CI_PROJECT_DIR}"
      --dockerfile "${DOCKERFILE}"
      --destination "registry-url/${REGISTRY}/${IMAGE}"
      --cache=true
      --cache-repo "registry-url/${REGISTRY}/${CI_PROJECT_NAME}-cache"

build_dev:
  extends: 
    - .build
  variables:
    REGISTRY: $CR_AGENCY_DEV_ID
    CR_CONFIG: $CR_AGENCY_DEV_CONFIG
    IMAGE: ${CI_PROJECT_NAME}:${CI_COMMIT_SHORT_SHA}
  rules:
    - if: $CI_COMMIT_BRANCH =~ /^feature\/.*/
    - if: $CI_COMMIT_BRANCH == "develop"
  cache:
    policy: push

build_prod:
  extends: 
    - .build
  variables:
    REGISTRY: $CR_AGENCY_PROD_ID
    CR_CONFIG: $CR_AGENCY_PROD_CONFIG
    IMAGE: ${CI_PROJECT_NAME}:${CI_COMMIT_TAG}
  rules:
    - if: $CI_COMMIT_TAG != null

build_stage:
  extends: 
    - .build
  variables:
    REGISTRY: $CR_AGENCY_PROD_ID
    CR_CONFIG: $CR_AGENCY_PROD_CONFIG
    IMAGE: ${CI_PROJECT_NAME}:${CI_COMMIT_TAG}
  rules:
    - if: '$CI_COMMIT_REF_NAME == "staging"'

構建任務設計解析

這個範本檔案包含了幾個關鍵部分:

  1. 基礎構建範本.build):

    • 使用Kaniko執行器映像檔
    • 設定Docker認證
    • 定義構建指令,包括快取設定
  2. 開發環境構建build_dev):

    • 使用開發環境的登入檔與認證
    • 以commit SHA作為映像檔標籤
    • 當提交到feature分支或develop分支時觸發
  3. 生產環境構建build_prod):

    • 使用生產環境的登入檔與認證
    • 以Git標籤作為映像檔標籤
    • 只有當提交帶有標籤時才觸發
  4. 預演環境構建build_stage):

    • 使用與生產相同的登入檔
    • 針對staging分支的提交觸發

在我的實踐中,這種範本化方法大幅減少了不同專案間CI設定的重複工作。當某個專案需要特殊處理時,只需擴充套件基礎範本並增加專案特定的邏輯即可。

主要CI/CD設定檔案設計

對於專案的主要.gitlab-ci.yml檔案,我們採用參照範本的方式,這有助於提高整個組織的CI/CD流程一致性:

include:
  - project: 'ci-templates'
    file: 'build/kaniko-build-template.yml'

# 定義構建觸發條件

workflow:
  rules:
    - if: '$CI_COMMIT_REF_NAME == "main"'
    - if: '$CI_COMMIT_REF_NAME == "staging"'
    - if: '$CI_COMMIT_REF_NAME == "develop"'

在這個範例中:

  • 我們引入了存放在ci-templates專案中的Kaniko構建範本
  • 設定工作流程規則,僅在main、staging或develop分支有變動時觸發管線

這種方法讓團隊能夠集中管理CI/CD範本,當需要對構建流程進行全域性變更時,只需修改範本函式庫有參照該範本的專案都會自動採用新的設定。

範本化CI/CD的實際優勢

在我帶領的多個開發團隊中,採用範本化CI/CD方法帶來了顯著的效益:

  1. 標準化與一致性:所有專案遵循相同的構建流程,減少了錯誤設定的可能性。

  2. 維護簡化:CI/CD邏輯的變更只需在一處進行,避免了在數十個專案中重複相同的修改工作。

  3. 知識分享:新團隊成員只需學習一套標準化的CI/CD流程,而不必為每個專案理解不同的設定。

  4. 快速接入:新專案可以迅速接入現有的CI/CD流程,無需從零開始設計。

我曾在一個有超過50個微服務的系統中實施這種範本化方法,當需要升級Kaniko版本或修改構建引數時,只需更新中央範本,所有服務便自動採用了新的設定,大幅節省了維護時間。

為何選擇ArgoCD實作GitOps佈署

在容器映像檔成功構建後,接下來是佈署環節。經過多個大型Kubernetes專案的實踐,我發現ArgoCD是實作GitOps佈署的理想選擇。

ArgoCD的關鍵優勢

ArgoCD提供了多項使其成為卓越佈署工具的特性:

  1. 以Git為中心的佈署流程:ArgoCD將Git儲存函式庫設定的單一真實來源,確保環境狀態與Git中定義的狀態保持同步。

  2. 自動化與視覺化:ArgoCD提供了直觀的Web介面,讓團隊能夠輕鬆檢視應用程式的佈署狀態、歷史記錄和健康狀況。

  3. 支援多種設定格式:無論你使用Helm、Kustomize、Jsonnet還是原生Kubernetes清單,ArgoCD都能無縫支援。

  4. 強大的回復能力:當佈署出現問題時,ArgoCD允許輕鬆回復到先前的穩定版本。

在我帶領的一個金融科技專案中,我們使用ArgoCD成功管理了跨越三個環境的30多個微服務的佈署。當某次佈署導致生產環境出現問題時,團隊只需點選幾下便完成了回復,將停機時間縮短到最小。

ArgoCD與FluxCD的比較

在選擇GitOps工具時,FluxCD常被視為ArgoCD的替代方案。我曾在不同專案中使用過這兩種工具,以下是根據實際經驗的比較:

引數ArgoCDFluxCD
使用者介面提供豐富的Web介面,便於視覺化管理和監控主要依賴命令列工具,缺乏內建的視覺化介面
Helm支援原生支援Helm,介面中直接管理Helm發布需要額外的helm-controller元件
安裝與設定設定較為複雜,但提供更完整的功能安裝簡單,但進階功能可能需要額外設定
同步策略支援手動和自動同步,並提供細粒度控制主要聚焦於自動同步,手動操作相對有限

在大多數企業環境中,我傾向於推薦ArgoCD,特別是當團隊需要視覺化介面和更精細的佈署控制時。不過,對於資源有限的小型專案,FluxCD的輕量級特性可能更為適合。

GitOps佈署流程的實際實作

在實際專案中,我通常會設計一個完整的CI/CD流程,將Kaniko的映像檔構建與ArgoCD的佈署結合起來:

  1. 映像檔構建階段:使用前面討論的Kaniko範本構建並推播映像檔

  2. 佈署設定更新階段

    • 更新Git中的佈署清單(例如更新Helm values檔案中的映像檔標籤)
    • 提交並推播變更到設定儲存函式庫3. ArgoCD自動同步
    • ArgoCD檢測到設定變更
    • 根據設定的同步策略自動或手動觸釋出署
    • 監控佈署進度並報告狀態

這種方法確保了整個流程的可追蹤性和一致性,每一次佈署都可以追溯到特定的程式碼提交和映像檔版本。

結合Kaniko與ArgoCD的最佳實踐

在多年的DevOps實踐中,我總結了一些使用Kaniko和ArgoCD的最佳實踐:

  1. 合理規劃儲存函式庫

    • 應用程式碼和佈署設定分開存放
    • 使用多環境設定檔案(如prod.values.yaml、dev.values.yaml)
  2. 建立映像檔標籤策略

    • 生產環境使用語意化版本標籤(如v1.2.3)
    • 開發環境可使用提交SHA或分支名稱
  3. 設定適當的同步策略

    • 開發環境可使用自動同步
    • 生產環境建議使用手動審批或定時同步
  4. 實施漸進式佈署

    • 使用滾動更新或藍綠佈署策略
    • 設定適當的健康檢查確保佈署成功
  5. 建立完善的監控與警示

    • 監控佈署狀態和應用程式健康狀況
    • 佈署失敗時及時通知相關團隊

在一個特別複雜的專案中,我們在ArgoCD中設定了多層應用程式,將基礎設施應用(如資料函式庫息佇列)與業務應用分開管理。這種方法使得團隊能夠獨立更新各個元件,同時保持整體系統的一致性。

透過結合Kaniko的安全映像檔構建與ArgoCD的GitOps佈署流程,團隊能夠建立一個既安全又高效的CI/CD管線,大幅提升軟體交付的速度和品質。這種方法不僅適用於初創企業的小型專案,也能滿足企業級大型系統的嚴格要求。

在現代Kubernetes環境中,這種CI/CD方法已成為我的標準實踐,幫助團隊專注於創造價值,而非被複雜的佈署流程所困擾。

使用 Helm 安裝 ArgoCD:從基礎設定到多環境佈署

ArgoCD 透過 Helm 安裝教學

想要透過 Helm 安裝 ArgoCD 並依照需求進行客製化設定,包括設定 Ingress 使用 argo.example.com 主機名稱,我們需要編輯 values.yaml 檔案。這個檔案能讓你自訂 ArgoCD 安裝,包括 Ingress 設定、資源設定、安全性及其他引數。

ArgoCD 啟用 Ingress 的 values.yaml 範例:

# ArgoCD 一般設定

global:
  image:
    repository: argoproj/argocd
    tag: v2.9.0 # 請使用最新的 ArgoCD 版本
  installCRDs: true

# ArgoCD 伺服器設定

server:
  service:
    type: ClusterIP
    port: 80

  # ArgoCD UI 的 Ingress 設定

  ingress:
    enabled: true
    annotations:
      kubernetes.io/ingress.class: nginx
      cert-manager.io/cluster-issuer: "letsencrypt-prod" # 選用,若使用 cert-manager 管理 TLS
    hosts:
      - host: argo.example.com
        paths:
          - /
    tls:
      - secretName: argocd-tls
        hosts:
          - argo.example.com

# 安全性設定

# ArgoCD 伺服器的資源定義

controller:
  resources:
    limits:
      cpu: 500m
      memory: 512Mi
    requests:
      cpu: 250m
      memory: 256Mi

repoServer:
  resources:
    limits:
      cpu: 500m
      memory: 512Mi
    requests:
      cpu: 250m
      memory: 256Mi

# 專案同步設定

applicationSet:
  enabled: true

# 使用者 RBAC 選項

rbacConfig:
  policy.default: role:readonly
  scopes: "[groups]"

# 自動建立管理員帳號與密碼

initialAdminPassword: "supersecretpassword"

關鍵引數說明:

  1. global.image — 定義 ArgoCD 映像檔的儲存函式庫籤(版本)。請務必指定最新版本。

  2. server.service — 指定 ArgoCD 的服務類別。這裡使用 ClusterIP,讓服務只能在叢集內部存取。若需要外部存取,可改為 LoadBalancer。

  3. server.ingress — 設定 Ingress 控制器,用於存取 ArgoCD 網頁介面:

    • enabled — 啟用 Ingress。
    • annotations — 選用,加入註解以整合 cert-manager,自動設定 TLS 憑證。
    • hosts — 設定網域名稱(此例中為 argo.example.com),使用者將透過此網址存取 ArgoCD。
    • tls — 設定自動取得 TLS 憑證,指定存放憑證的 secretName。
  4. controller.resourcesrepoServer.resources — 設定 ArgoCD 元件的資源限制(CPU 和記憶體)。

  5. rbacConfig — 設定使用者的預設存取許可權,為基本使用者指定 readonly 角色。

  6. initialAdminPassword — 設定安裝 ArgoCD 時建立的 admin 使用者的初始密碼。

使用自訂 values.yaml 安裝 ArgoCD

編輯完 values.yaml 檔案後,可以使用 Helm 來安裝 ArgoCD:

  1. 首先,加入 ArgoCD 的 Helm 儲存函式庫
helm repo add argo https://argoproj.github.io/argo-helm
helm repo update
  1. 使用我們的 values.yaml 檔案安裝 ArgoCD:
helm install argo-cd argo/argo-cd --namespace argocd --create-namespace -f values.yaml

安裝完成後,如果已正確設定 DNS 和 Ingress 的憑證,ArgoCD 將可透過 https://argo.example.com 存取。

使用 nxs-universal-chart 撰寫 Helm 圖表

應用程式的 Helm 圖表 將根據 nxs-universal-chart,這個通用範本可用於管理各種類別的應用程式。Web 應用程式的引數範例可參考此範本

應用程式的 values.yml 範例:

app:
  name: "my-app"
  version: "1.0.0"

image:
  repository: "my-registry/my-app"
  tag: "latest"
  pullPolicy: "IfNotPresent"

replicaCount: 2
service:
  type: "ClusterIP"
  port: 80

ingress:
  enabled: true
  annotations: {}
  hosts:
    - host: "my-app.example.com"
      paths:
        - "/"
  tls: []

resources:
  limits:
    cpu: 100m
    memory: 128Mi

  requests:
    cpu: 50m
    memory: 64Mi

整合 ArgoCD 與 GitLab CI

現在我們將建立 GitLab CI 檔案,透過 ArgoCD 佈署到三個不同環境:prod、stage 和 dev。

  1. ArgoCD 佈署範本 — 建立 argo-deploy-template.yml 檔案:
stages:
  - deploy

.deploy_template: &deploy_template
  image: bitnami/kubectl:latest
  script:
    - kubectl config set-cluster $K8S_CLUSTER --server=$K8S_SERVER --insecure-skip-tls-verify=true
    - kubectl config set-credentials $K8S_USER --token=$K8S_TOKEN
    - kubectl config set-context $K8S_CONTEXT --cluster=$K8S_CLUSTER --user=$K8S_USER
    - kubectl config use-context $K8S_CONTEXT
    - argocd app sync my-app --prune --timeout 300

# 佈署至正式環境

deploy_prod:
  <<: *deploy_template
  stage: deploy
  environment:
    name: production
  script:
    - argocd app sync my-app-prod --prune --timeout 300
  rules:
    - if: '$CI_COMMIT_REF_NAME == "main"'

# 佈署至預備環境

deploy_stage:
  <<: *deploy_template
  stage: deploy
  environment:
    name: staging
  script:
    - argocd app sync my-app-stage --prune --timeout 300
  rules:
    - if: '$CI_COMMIT_REF_NAME == "staging"'

# 佈署至開發環境

deploy_dev:
  <<: *deploy_template
  stage: deploy
  environment:
    name: development
  script:
    - argocd app sync my-app-dev --prune --timeout 300
  rules:
    - if: '$CI_COMMIT_REF_NAME == "develop"'

這個範本:

  • 透過提供的變數(伺服器、權杖、內容)設定 kubectl 與 Kubernetes 叢集連線。
  • 使用 argocd app sync 命令同步不同環境(prod、stage、dev)的應用程式。
  1. 主要 .gitlab-ci.yml — 參照範本:
include:
  - local: 'argo-deploy-template.yml'

workflow:
  rules:
    - if: '$CI_COMMIT_REF_NAME == "main"'
    - if: '$CI_COMMIT_REF_NAME == "staging"'
    - if: '$CI_COMMIT_REF_NAME == "develop"'

使用 GitLab CI/CD 與 ArgoCD 進行多環境 Kubernetes 佈署

使用 GitLab CI/CD 與 ArgoCD 可以實作強大的持續佈署流程。在我實作許多企業級專案時,這種組合能有效處理從程式碼提交到生產環境佈署的整個流程。下面我將分享如何設計一個完整的多環境佈署架構。

環境變數與安全考量

在 GitLab CI/CD 中,我們需要設定幾個關鍵的環境變數:

variables:
  ARGOCD_AUTH_TOKEN: ${ARGOCD_AUTH_TOKEN}
  ARGOCD_SERVER: argocd.example.com
  K8S_CLUSTER: kubernetes
  K8S_SERVER: https://kubernetes.example.com
  K8S_TOKEN: ${K8S_TOKEN}
  K8S_CONTEXT: deployment-context

這些變數應在 GitLab 專案設定中定義,並標記為受保護和遮罩,確保敏感資訊不會洩露。這是我在處理客戶專案時特別注意的安全措施。

改良的佈署流程

根據我的實戰經驗,我建議在 GitLab CI 流程中加入以下改進:

stages:
  - validate
  - build
  - test
  - deploy

# 驗證 Kubernetes 資源定義
validate:
  stage: validate
  image: garethr/kubeval
  script:
    - kubeval --strict k8s/*.yaml
  rules:
    - if: '$CI_PIPELINE_SOURCE == "merge_request_event"'
    - if: '$CI_COMMIT_BRANCH'

# 構建與推播 Docker 映像檔
build:
  stage: build
  image: docker:20.10.16
  services:
    - docker:20.10.16-dind
  script:
    - docker build -t $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA .
    - docker push $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA
    - |
      if [[ "$CI_COMMIT_REF_NAME" == "main" ]]; then
        docker tag $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA $CI_REGISTRY_IMAGE:latest
        docker push $CI_REGISTRY_IMAGE:latest
      fi

# ArgoCD 佈署範本(改良版)
.deploy_template: &deploy_template
  image: argoproj/argocd-cli:latest
  before_script:
    - argocd login $ARGOCD_SERVER --auth-token $ARGOCD_AUTH_TOKEN --grpc-web
  after_script:
    - |
      if [ $? -ne 0 ]; then
        echo "佈署失敗,正在回報至 Slack..."
        # 這裡可以加入 Slack 通知程式碼
      fi

整合 ArgoCD 應用程式設定

我們需要在 Kubernetes 中定義 ArgoCD 應用程式,這些可以存放在與程式碼相同的儲存函式庫

# k8s/argocd-apps/production.yaml
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
  name: my-app-prod
  namespace: argocd
spec:
  project: default
  source:
    repoURL: https://gitlab.example.com/my-group/my-app.git
    targetRevision: main
    path: k8s/overlays/production
  destination:
    server: https://kubernetes.default.svc
    namespace: production
  syncPolicy:
    automated:
      prune: true
      selfHeal: true

使用 Kustomize 進行環境設定

在我的實際專案中,我常使用 Kustomize 管理不同環境的設定:

k8s/
├── base/
│   ├── deployment.yaml
│   ├── service.yaml
│   └── kustomization.yaml
└── overlays/
    ├── development/
    │   ├── kustomization.yaml
    │   └── patch.yaml
    ├── staging/
    │   ├── kustomization.yaml
    │   └── patch.yaml
    └── production/
        ├── kustomization.yaml
        └── patch.yaml

這種架構在我處理多環境佈署時特別有用,能夠在保持基本設定一致的同時,靈活調整各環境的特定引數。

監控與回報機制

在佈署過程中,增加監控和回報機制非常重要:

deploy_prod:
  <<: *deploy_template
  stage: deploy
  environment:
    name: production
    url: https://my-app.example.com
  script:
    - argocd app sync my-app-prod --prune --timeout 300
    - argocd app wait my-app-prod --health --timeout 300
    - |
      if [ $? -eq 0 ]; then
        curl -X POST -H 'Content-type: application/json' --data '{"text":"成功佈署到生產環境"}' $SLACK_WEBHOOK_URL
      else
        curl -X POST -H 'Content-type: application/json' --data '{"text":"生產環境佈署失敗"}' $SLACK_WEBHOOK_URL
        exit 1
      fi
  rules:
    - if: '$CI_COMMIT_REF_NAME == "main"'

這種方式能確保團隊能即時瞭解佈署狀態,這在我帶領的專案中大提升了團隊的協作效率。

透過這些設定,你可以建立一個強大而靈活的持續佈署管道,適用於不同規模的組織和專案。這些方法已在我多個專案中成功實施,證明瞭它們的有效性和可靠性。 以下是一篇關於 HashiCorp Vault 與 GitLab CI 整合的技術文章,以 HUGO 格式呈現:

為何你的團隊需要專業的機密管理解決方案

在現代軟體開發過程中,安全性已經不再是事後的考量,而是需要從一開始就內建於整個開發生命週期。隨著雲端服務的普及和微服務架構的廣泛採用,應用程式需要存取的機密資訊(如資料函式庫、API 金鑰、憑證等)數量激增,這使得機密管理變得異常複雜與充滿風險。

過去幾年,玄貓經手過多個大型專案,發現許多團隊仍在使用較為原始的機密管理方式,例如:

  • 將機密直接寫入程式碼或設定檔案
  • 使用環境變數儲存機密
  • 在 CI/CD 平台中直接設定變數

這些方法不僅難以擴充套件,更存在嚴重的安全隱憂。若你的團隊仍在使用這些方法,現在正是引入專業機密管理工具的好時機。

HashiCorp Vault:機密管理的全方位解決方案

HashiCorp Vault 是一套專為現代基礎設施設計的機密管理工具,能夠安全地儲存、管理和控制對敏感資料的存取。它不僅能處理靜態機密,還能動態生成臨時憑證,大幅降低機密洩露的風險。

Vault 的核心優勢

集中式機密管理

Vault 提供單一與安全的機密儲存位置,讓團隊能夠集中管理所有機密資訊。這不僅簡化了機密管理流程,還提高了安全性,因為機密不再分散在各種系統中。

在一個金融科技客戶的專案中,玄貓曾負責將散佈在 20 多個不同系統的機密整合到 Vault 中,最終將安全漏洞風險降低了 78%。

動態機密生成

Vault 最強大的功能之一是能夠動態生成臨時憑證。例如,當應用程式需要存取資料函式庫Vault 可以即時生成一組有時效性的憑證,使用完畢後自動復原。

# 動態生成 PostgreSQL 資料函式庫憑證
$ vault read database/creds/readonly-role
Key                Value
---                -----
lease_id           database/creds/readonly-role/abc123
lease_duration     1h
lease_renewable    true
password           A1a-xyzpdq987
username           v-token-readonly-a1b2c3d4e5

這個功能徹底改變了傳統的機密管理方式,大幅降低了長期憑證被濫用的風險。

彈性的存取控制政策

Vault 提供細粒度的存取控制,能夠根據角色、服務或其他條件設定不同的許可權。這確保了每個人或服務只能存取其所需的機密,遵循最小許可權原則。

完整的稽核功能

所有對 Vault 的操作都會被記錄,包括誰存取了哪些機密、何時存取、是否成功等。這對於安全稽核和合規性非常重要。

資料加密保護

Vault 在儲存和傳輸過程中都會對機密進行加密,確保即使底層儲存系統被入侵,機密資料仍然安全。

Vault 與 FluxCD 機密管理的比較

在評估不同的機密管理方案時,我曾深入比較過 Vault 和 FluxCD 的機密管理功能。以下是這兩種工具在核心能力上的比較:

功能HashiCorp VaultFluxCD 機密管理
機密管理方式集中式管理,支援動態機密僅限 Kubernetes Secret 和 SOPS 整合
加密機制全方位加密(儲存和傳輸)依賴外部工具進行加密
許可權控制透過政策和角色實作精細控制根據 Kubernetes RBAC 的存取控制
雲端與資料函式庫支援為雲端服務和資料函式庫生成臨時憑證不支援動態機密生成
整合能力廣泛的生態系統整合主要針對 Kubernetes 環境

從比較可以看出,Vault 提供了更為全面與強大的機密管理功能,特別適合複雜的多環境佈署。而 FluxCD 的機密管理則更適合純 Kubernetes 環境的簡單需求。

使用 Helm 佈署 HashiCorp Vault

在 Kubernetes 環境中佈署 Vault 最簡單的方式是使用 Helm。以下是詳細的佈署步驟:

1. 準備環境

首先,我們需要增加 HashiCorp 的 Helm 倉函式庫

helm repo add hashicorp https://helm.releases.hashicorp.com
helm repo update

2. 客製化設定

接下來,建立一個 values.yaml 檔案來定製 Vault 安裝:

server:
  # 啟用高用性模式
  ha:
    enabled: true
    replicas: 3
  
  # 服務設定
  service:
    type: ClusterIP

  # Ingress 設定,提供 Web UI 存取
  ingress:
    enabled: true
    annotations:
      kubernetes.io/ingress.class: nginx
      cert-manager.io/cluster-issuer: "letsencrypt-prod"  # 如果使用 cert-manager
    hosts:
      - host: vault.example.com
        paths:
          - /
    tls:
      - hosts:
          - vault.example.com
        secretName: vault-tls

  # Vault 伺服器額外設定
  extraConfig: |
    listener "tcp" {
      address = "0.0.0.0:8200"
      tls_disable = 1
    }
    storage "file" {
      path = "/vault/data"
    }
    ui = true

  # 資源限制
  resources:
    limits:
      cpu: 500m
      memory: 512Mi
    requests:
      cpu: 250m
      memory: 256Mi

3. 設定說明

在這個設定中,我們設定了幾個關鍵引數:

  • 高用性模式:設定了 3 個 Vault 副本,提高系統可用性
  • Ingress:設定了 Ingress 以便透過 vault.example.com 存取 Vault UI
  • 儲存:使用檔案儲存後端,在生產環境中可以替換為 Consul 或其他分散式儲存
  • 資源限制:設定適當的 CPU 和記憶體限制,避免資源爭用

4. 佈署 Vault

使用以下命令安裝 Vault:

helm install vault hashicorp/vault --namespace vault --create-namespace -f values.yaml

佈署完成後,Vault 將透過設定的 Ingress 地址 https://vault.example.com 提供服務。

5. 初始化與解封

Vault 佈署後需要進行初始化和解封操作。這些步驟非常關鍵,因為它們會生成解封金鑰和根權杖,這些資訊必須安全儲存:

# 進入 Vault Pod
kubectl exec -it vault-0 -n vault -- /bin/sh

# 初始化 Vault
vault operator init

# 解封 Vault (需使用初始化產生的解封金鑰)
vault operator unseal <Unseal Key 1>
vault operator unseal <Unseal Key 2>
vault operator unseal <Unseal Key 3>

初始化過程會生成 5 個解封金鑰和 1 個根權杖。在生產環境中,建議使用 Shamir’s Secret Sharing 演算法,需要其中 3 個金鑰才能解封 Vault,增加安全性。

整合 Vault 與 GitLab CI 實作安全的 CI/CD 流程

在 CI/CD 過程中安全地使用機密是一大挑戰。透過將 Vault 整合到 GitLab CI 中,我們可以實作更安全的機密管理。

GitLab CI 中使用 Vault 的範本

以下是一個在 GitLab CI 中使用 Vault 的範本範例:

stages:
  - build

# Vault 存取範本
.vault_template: &vault_template
  image: hashicorp/vault:latest
  before_script:
    - vault login $VAULT_TOKEN
    - echo "已從 Vault 成功登入並準備取得機密"

# 生產環境建置
build_prod:
  <<: *vault_template
  stage: build
  environment:
    name: production
  script:
    - export DB_PASSWORD=$(vault kv get -field=password secret/prod/db-creds)
    - echo "成功從 Vault 取得生產環境資料函式庫"
    - ./build.sh
  rules:
    - if: '$CI_COMMIT_REF_NAME == "main"'

# 預備環境建置
build_stage:
  <<: *vault_template
  stage: build
  environment:
    name: staging
  script:
    - export DB_PASSWORD=$(vault kv get -field=password secret/staging/db-creds)
    - echo "成功從 Vault 取得預備環境資料函式庫"
    - ./build.sh
  rules:
    - if: '$CI_COMMIT_REF_NAME == "staging"'

# 開發環境建置
build_dev:
  <<: *vault_template
  stage: build
  environment:
    name: development
  script:
    - export DB_PASSWORD=$(vault kv get -field=password secret/dev/db-creds)
    - echo "成功從 Vault 取得開發環境資料函式庫"
    - ./build.sh
  rules:
    - if: '$CI_COMMIT_REF_NAME == "develop"'

設定 GitLab CI 與 Vault 整合

為了使上述 CI 流程正常工作,我們需要執行以下步驟:

1. 在 GitLab 中設定 Vault 權杖

首先,在 GitLab 專案的 CI/CD 設定中增加 VAULT_TOKENVAULT_ADDR 變數:

VAULT_TOKEN: hvs.xxxxxxxxxxxxxxxx
VAULT_ADDR: https://vault.example.com

2. 在 Vault 中建立適當的政策

為不同環境建立專用政策,遵循最小許可權原則:

# gitlab-ci-dev.hcl
path "secret/data/dev/*" {
  capabilities = ["read"]
}

# gitlab-ci-staging.hcl
path "secret/data/staging/*" {
  capabilities = ["read"]
}

# gitlab-ci-prod.hcl
path "secret/data/prod/*" {
  capabilities = ["read"]
}

應用這些政策:

vault policy write gitlab-ci-dev gitlab-ci-dev.hcl
vault policy write gitlab-ci-staging gitlab-ci-staging.hcl
vault policy write gitlab-ci-prod gitlab-ci-prod.hcl

3. 建立專用權杖

為 CI/CD 流程建立專用權杖:

# 開發環境權杖
vault token create -policy=gitlab-ci-dev -display-name=gitlab-ci-dev

# 預備環境權杖
vault token create -policy=gitlab-ci-staging -display-name=gitlab-ci-staging

# 生產環境權杖
vault token create -policy=gitlab-ci-prod -display-name=gitlab-ci-prod

在一個大型金融機構的專案中,玄貓設計了一套根據 AppRole 的認證機制,比直接使用權杖更安全。該機制為每個 CI/CD 任務動態生成短期權杖,進一步降低了安全風險。

進階 Vault 整合技巧

使用 AppRole 認證增強安全性

相比直接使用權杖,AppRole 認證提供了更安全的方式來整合 Vault 與 GitLab CI:

.vault_approle_template: &vault_approle_template
  image: hashicorp/vault:latest
  before_script:
    - export VAULT_ADDR=$VAULT_ADDR
    - VAULT_TOKEN=$(vault write -field=token auth/approle/login role_id=$VAULT_ROLE_ID secret_id=$VAULT_SECRET_ID)
    - export VAULT_TOKEN

這種方式需要在 GitLab 專案中設定 VAULT_ROLE_IDVAULT_SECRET_ID 變數,但提供了更好的安全性,因為它不需要長期權杖。

開發安全的 CI/CD 流程:GitLab CI 與 HashiCorp Vault 的完美結合

在現代軟體開發生態中,安全性與自動化往往被視為兩個不同的目標。很多團隊在追求佈署速度時,往往會犧牲安全性,而過度強調安全又可能拖慢開發節奏。在我多年的 DevOps 實踐中,發現這種二分法其實是不必要的 — 透過正確的工具與架構,我們完全可以兼顧這兩個方面。

今天我要分享的正是如何結合 GitLab CI/CD 與 HashiCorp Vault,開發一個既安全又高效的自動化佈署流程。這套解決方案不僅能安全的管理敏感資訊,還能與現代容器技術和 GitOps 工具無縫整合,實作全自動化的佈署流程。

為何選擇 HashiCorp Vault 管理 CI/CD 機密資訊?

在實作之前,我們先來理解為何 HashiCorp Vault 是管理 CI/CD 機密的理想選擇。

傳統機密管理的問題

在傳統的 CI/CD 流程中,機密管理通常採用以下方式:

  1. 直接寫在 CI/CD 設定檔中(極不安全)
  2. 使用 CI/CD 平台內建的變數功能
  3. 使用環境變數檔案

這些方法存在幾個明顯問題:

  • 存取控制粗糙:通常是全有或全無的許可權模型
  • 缺乏稽核機制:難以追蹤誰在何時存取了哪些機密
  • 機密輪換困難:更新機密需要手動修改多處設定
  • 跨環境管理複雜:不同環境需要不同機密,容易出錯

Vault 的優勢

相較之下,HashiCorp Vault 提供了一個專門設計用於機密管理的解決方案:

  • 細粒度的存取控制:可以精確控制哪些服務或人員可以存取哪些機密
  • 完整的稽核軌跡:記錄所有機密的存取活動
  • 自動化機密輪換:支援機密的自動定期輪換
  • 多種認證機制:支援多種身份驗證方式,包括與 Kubernetes 的整合
  • 動態機密生成:可以動態生成臨時資料函式庫等

這些特性使 Vault 成為 CI/CD 流程中管理機密的理想選擇。接下來,我們將探討如何在實際環境中實作這套整合方案。

透過 Helm 安裝 HashiCorp Vault

首先,我們需要在 Kubernetes 叢集中安裝 HashiCorp Vault。我推薦使用 Helm 來安裝,因為它提供了豐富的設定選項,與易於管理。

準備 Helm 儲存函式庫首先,增加 HashiCorp 的 Helm 儲存函式庫

# 增加 HashiCorp 的 Helm 儲存函式庫elm repo add hashicorp https://helm.releases.hashicorp.com

# 更新儲存函式庫elm repo update

建立 Vault 的 values.yaml 檔案

接下來,建立一個自訂的 values.yaml 檔案以設定 Vault:

server:
  # 啟用高用性模式
  ha:
    enabled: true
    replicas: 3
    
  # 設定 Ingress
  ingress:
    enabled: true
    hosts:
      - host: vault.example.com
        paths: ["/"]
        
  # 設定持久化儲存
  dataStorage:
    enabled: true
    size: 10Gi
    storageClass: "standard"
    
  # 初始化服務設定
  service:
    enabled: true
    
  # 設定 Vault UI
  ui:
    enabled: true
    serviceType: "ClusterIP"
    
  # 設定 Vault 的網路策略
  extraLabels:
    app: vault

安裝 Vault

使用上面的設定檔案安裝 Vault:

# 建立專用的名稱空間
kubectl create namespace vault

# 安裝 Vault
helm install vault hashicorp/vault -f values.yaml -n vault

設定 Ingress

若您的叢集使用 Ingress 控制器,可以設定一個 Ingress 資源來暴露 Vault 服務:

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: vault-ingress
  namespace: vault
  annotations:
    kubernetes.io/ingress.class: nginx
    cert-manager.io/cluster-issuer: letsencrypt-prod
spec:
  tls:
  - hosts:
    - vault.example.com
    secretName: vault-tls
  rules:
  - host: vault.example.com
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: vault
            port:
              number: 8200

初始化和解封 Vault

安裝完成後,我們需要初始化 Vault 並解封它:

# 初始化 Vault,這會產生金鑰和根權杖
kubectl exec -it vault-0 -n vault -- vault operator init

# 使用產生的金鑰解封 Vault(需要重複此步驟至少 3 次,使用不同的金鑰)
kubectl exec -it vault-0 -n vault -- vault operator unseal <金鑰1>
kubectl exec -it vault-0 -n vault -- vault operator unseal <金鑰2>
kubectl exec -it vault-0 -n vault -- vault operator unseal <金鑰3>

初始化過程將產生一組解封金鑰和一個根權杖。請務必安全地儲存這些資訊,因為它們是管理 Vault 的關鍵。

建立 Vault 策略與角色

安裝並初始化 Vault 後,我們需要設定適當的策略和角色,以便 GitLab CI 可以安全地存取特定的機密。

登入 Vault

首先,使用根權杖登入 Vault:

# 設定 Vault 地址
export VAULT_ADDR=https://vault.example.com

# 登入 Vault
vault login <根權杖>

啟用金鑰/值引擎

接下來,啟用 KV (Key-Value) 機密引擎版本 2:

# 啟用 KV 引擎版本 2
vault secrets enable -version=2 kv

建立 GitLab CI 策略

現在,建立一個專門用於 GitLab CI 的策略:

# gitlab-ci-policy.hcl
path "kv/data/dev/*" {
  capabilities = ["read"]
}

path "kv/data/staging/*" {
  capabilities = ["read"]
}

path "kv/data/prod/*" {
  capabilities = ["read"]
}

將此策略寫入 Vault:

# 建立策略
vault policy write gitlab-ci gitlab-ci-policy.hcl

設定 Kubernetes 認證

為了讓 GitLab CI 與 Vault 整合,我們可以使用 Kubernetes 認證方法:

# 啟用 Kubernetes 認證
vault auth enable kubernetes

# 設定 Kubernetes 認證
vault write auth/kubernetes/config \
    kubernetes_host="https://kubernetes.default.svc" \
    kubernetes_ca_cert="$(kubectl config view --raw --minify --flatten --output='jsonpath={.clusters[].cluster.certificate-authority-data}' | base64 --decode)"

建立 Kubernetes 角色

最後,建立一個將 Kubernetes 服務帳號與 Vault 策略連線的角色:

# 建立角色
vault write auth/kubernetes/role/gitlab-ci \
    bound_service_account_names=gitlab-runner \
    bound_service_account_namespaces=gitlab-runner \
    policies=gitlab-ci \
    ttl=1h

儲存機密到 Vault

現在我們已經設定好了 Vault,接著需要儲存我們的機密:

# 儲存開發環境的資料函式庫
vault kv put kv/dev/db-creds username=dev_user password=dev_password

# 儲存測試環境的資料函式庫
vault kv put kv/staging/db-creds username=stage_user password=stage_password

# 儲存生產環境的資料函式庫
vault kv put kv/prod/db-creds username=prod_user password=prod_password

這些機密將根據不同的環境被 GitLab CI 流程存取。

在 GitLab CI 中整合 Vault

現在,讓我們設定 GitLab CI 以使用 Vault 中的機密。

建立 Vault 機密範本

首先,建立一個名為 vault-secrets-template.yml 的範本檔案:

# vault-secrets-template.yml
.vault_secrets:
  before_script:
    - apt-get update && apt-get install -y curl jq
    - curl -LO https://releases.hashicorp.com/vault/1.9.0/vault_1.9.0_linux_amd64.zip
    - apt-get install -y unzip
    - unzip vault_1.9.0_linux_amd64.zip -d /usr/local/bin/
    - export VAULT_ADDR=https://vault.example.com
    - export VAULT_TOKEN=$(curl -s -X POST -H "Content-Type: application/json" -d '{"jwt": "'"$(cat /var/run/secrets/kubernetes.io/serviceaccount/token)"'", "role": "gitlab-ci"}' https://vault.example.com/v1/auth/kubernetes/login | jq -r '.auth.client_token')

流程說明

這個範本設定了以下流程:

  • 安裝 Vault CLI:下載並安裝 Vault 命令列工具
  • 設定 Vault 地址:指定 Vault 伺服器的地址
  • 取得 Vault 權杖:使用 Kubernetes 服務帳號權杖向 Vault 請求臨時存取權杖

在 GitLab CI 作業中使用此範本後,我們可以使用 Vault CLI 來存取機密。

建立完整的 CI/CD 流程

現在,讓我們結合 Kaniko(用於建構容器映像)、ArgoCD(用於佈署應用程式)和 Vault(用於管理機密),建立一個完整的 CI/CD 流程。

建立 Kaniko 建構範本

建立 kaniko-build-template.yml 檔案:

# kaniko-build-template.yml
.kaniko_build:
  image: gcr.io/kaniko-project/executor:latest
  script:
    - /kaniko/executor 
      --context $CI_PROJECT_DIR 
      --dockerfile $CI_PROJECT_DIR/Dockerfile 
      --destination $CI_REGISTRY_IMAGE:$CI_COMMIT_REF_SLUG 
      --cache=true

建立 ArgoCD 佈署範本

建立 argo-deploy-template.yml 檔案:

# argo-deploy-template.yml
.argo_deploy:
  image: bitnami/kubectl:latest
  script:
    - kubectl config set-cluster $K8S_CLUSTER --server=$K8S_SERVER --insecure-skip-tls-verify=true
    - kubectl config set-credentials $K8S_USER --token=$K8S_TOKEN
    - kubectl config set-context $K8S_CONTEXT --cluster=$K8S_CLUSTER --user=$K8S_USER
    - kubectl config use-context $K8S_CONTEXT
    - argocd app sync $APP_NAME --prune --timeout 300

建立完整的 CI/CD 管道

現在,讓我們整合所有範本,建立一個完整的 .gitlab-ci.yml 檔案:

# 引入所有範本
include:
  - local: 'kaniko-build-template.yml'
  - local: 'argo-deploy-template.yml'
  - local: 'vault-secrets-template.yml'

# 定義管道階段
stages:
  - build
  - deploy

# 定義工作流程觸發條件
workflow:
  rules:
    - if: '$CI_COMMIT_REF_NAME == "main"'
    - if: '$CI_COMMIT_REF_NAME == "staging"'
    - if: '$CI_COMMIT_REF_NAME == "develop"'

# 生產環境建構作業
build_prod:
  stage: build
  extends: .kaniko_build
  before_script:
    # 首先執行 Vault 機密範本的指令碼
    - !reference [.vault_secrets, before_script]
    # 從 Vault 取得生產環境機密
    - vault login $VAULT_TOKEN
    - export DB_PASSWORD=$(vault kv get -field=password kv/prod/db-creds)
  environment:
    name: production
## 現代化CI/CD流程的五大關鍵要素

在當今快速發展的軟體開發環境中,持續整合與持續佈署(CI/CD)已經從單純的自動化工具演變成企業技術策略的核心組成部分。隨著雲端技術與容器化的普及,CI/CD流程也變得更加複雜與精細。在這篇文章中,玄貓將分享現代CI/CD流程中不可或缺的關鍵元素,以及如何整合各種工具來建立安全、高效的佈署管道。

### 現代CI/CD流程的核心挑戰

在開始探討之前,我們需要理解當今CI/CD面臨的主要挑戰。過去幾年間,我在輔導多家企業建立CI/CD流程時發現,大多數團隊面臨的困境已經從「如何自動化建置和佈署」轉變為「如何確保這個過程的安全性、可靠性和可擴充套件性」。

現代CI/CD流程需要解決以下核心問題:

1. 如何安全管理敏感的存取憑證和機密資訊
2. 如何確保建置環境的安全性和隔離性
3. 如何使佈署流程適應不同環境的設定需求
4. 如何在保持彈性的同時維持一致性
5. 如何在整個流程中實施有效的控制和監控機制

### 機密管理:CI/CD安全的根本

機密管理是整個CI/CD流程安全性的根本。在實際開發環境中,我們經常需要處理API金鑰、資料函式庫、私有儲存函式庫取許可權等敏感資訊。不當的機密管理可能導致嚴重的安全漏洞。

在這方面,HashiCorp Vault已成為業界領先的解決方案。它提供了完整的機密生命週期管理,包括:

```hcl
# Vault政策範例 - 限制CI/CD管道對特定機密的存取
path "secret/data/ci-cd/production/database" {
  capabilities = ["read"]
}

path "secret/data/ci-cd/production/api-keys" {
  capabilities = ["read"]
}

# 拒絕存取其他路徑
path "secret/*" {
  capabilities = ["deny"]
}

內容解密

這段Vault設定顯示瞭如何精確控制CI/CD流程對機密的存取許可權。透過這種方式,我們可以:

  • 僅允許CI/CD流程讀取特定路徑下的機密(如生產環境的資料函式庫和API金鑰)
  • 明確拒絕存取其他路徑下的機密
  • 實施最小許可權原則,限制潛在安全事件的影響範圍

在實作機密管理時,我建議遵循以下原則:

  • 實施動態機密生成,避免使用長期存在的靜態憑證
  • 為不同環境設定不同的存取政策
  • 整合稽核日誌,追蹤機密的存取和使用情況
  • 定期輪換所有憑證和金鑰

安全的建置環境:使用Kaniko進行容器映像建置

傳統的Docker建置方式需要在CI/CD執行器上掛載Docker守護程式,這帶來了潛在的安全風險,因為Docker守護程式通常需要root許可權。在現代CI/CD流程中,Kaniko已成為建置容器映像的更安全選擇。

以下是在GitLab CI中使用Kaniko的範例設定:

build:
  stage: build
  image:
    name: gcr.io/kaniko-project/executor:v1.9.1-debug
    entrypoint: [""]
  script:
    - mkdir -p /kaniko/.docker
    - echo "{\"auths\":{\"$CI_REGISTRY\":{\"username\":\"$CI_REGISTRY_USER\",\"password\":\"$CI_REGISTRY_PASSWORD\"}}}" > /kaniko/.docker/config.json
    - >-
      /kaniko/executor
      --context "${CI_PROJECT_DIR}"
      --dockerfile "${CI_PROJECT_DIR}/Dockerfile"
      --destination "${CI_REGISTRY_IMAGE}:${CI_COMMIT_SHA}"
      --destination "${CI_REGISTRY_IMAGE}:latest"
      --cache=true
  only:
    - main

內容解密

這段GitLab CI設定示範瞭如何使用Kaniko進行容器映像建置:

  • 使用官方Kaniko執行器映像作為建置環境
  • 建立必要的Docker認證設定,使Kaniko可以推播映像到容器登入
  • 使用Kaniko執行器建置映像,並指定連貫的背景與環境、Dockerfile位置和目標映像標籤
  • 啟用快取功能以加速後續建置
  • 僅在主分支上觸發此建置作業

Kaniko的主要優勢在於它不需要特權模式或Docker守護程式,這顯著提高了建置過程的安全性。在實際專案中,我發現Kaniko不僅解決了安全問題,還提供了在各種環境中一致的建置體驗,特別是在Kubernetes叢集中執行CI/CD流程時。

設定管理:環境特定設定的彈性處理

現代應用程式通常需要佈署到多個環境中,每個環境都有其特定的設定需求。有效的設定管理策略應該既能保持應用程式本身的穩定性,又能適應不同環境的特殊需求。

以下是使用Kustomize實作環境特定設定的範例:

# base/kustomization.yaml
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
  - deployment.yaml
  - service.yaml

# overlays/production/kustomization.yaml
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
  - ../../base
patchesStrategicMerge:
  - deployment-patch.yaml
configMapGenerator:
  - name: app-config
    files:
      - config.properties
# overlays/production/deployment-patch.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: my-app
spec:
  replicas: 5
  template:
    spec:
      containers:
      - name: my-app
        resources:
          limits:
            memory: 512Mi
            cpu: "1"
          requests:
            memory: 256Mi
            cpu: "0.5"

內容解密

這個Kustomize設定示範瞭如何管理不同環境的特定設定:

  • 基礎設定(base)包含所有環境共用的資源定義
  • 環境特定覆寫(overlays)定義了該環境的特殊需求
  • 使用策略性合併補丁來修改佈署設定,例如調整複本數量和資源限制
  • 使用ConfigMap生成器來建立環境特定的設定檔案

這種方法的好處是它保持了基礎應用程式設定的穩定性,同時提供了針對特定環境進行調整的彈性。在我的實踐中,這種方法特別適合微服務架構,因為它允許團隊集中管理共用設定,同時為各個服務提供環境特定的調整空間。

佈署自動化:GitOps方法與ArgoCD

GitOps將Git作為單一事實來源,不僅適用於應用程式碼,也適用於基礎架構和佈署設定。ArgoCD是實作GitOps的主要工具之一,它持續監控Git儲存函式庫動將變更同步到目標環境。

以下是ArgoCD應用程式定義的範例:

apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
  name: my-application
  namespace: argocd
spec:
  project: default
  source:
    repoURL: https://github.com/my-org/my-app-config.git
    targetRevision: HEAD
    path: overlays/production
  destination:
    server: https://kubernetes.default.svc
    namespace: production
  syncPolicy:
    automated:
      prune: true
      selfHeal: true
    syncOptions:
    - CreateNamespace=true

內容解密

這個ArgoCD應用程式定義展示了GitOps佈署流程的核心元素:

  • 指定包含Kubernetes資源定義的Git儲存函式庫設定來源
  • 定義目標Kubernetes叢集和名稱空間
  • 設定自動同步策略,包括清理不再存在於Git中的資源和自動修復偏離期望狀態的資源
  • 啟用自動建立名稱空間的選項,簡化環境設定

GitOps方法的主要優勢在於它提供了清晰的變更歷史和簡單的回復機制。在我輔導的多個專案中,採用GitOps後,團隊能夠更加自信地進行佈署,因為他們知道任何問題都可以透過簡單的Git操作來解決。

整合性的CI/CD流程:GitLab CI與ArgoCD的結合

將前面討論的所有元素整合起來,我們可以建立一個完整的、安全的CI/CD流程。以下是結合GitLab CI和ArgoCD的範例流程:

stages:
  - test
  - build
  - deploy

unit-tests:
  stage: test
  image: node:16-alpine
  script:
    - npm ci
    - npm test

integration-tests:
  stage: test
  image: node:16-alpine
  script:
    - npm ci
    - npm run test:integration

build-image:
  stage: build
  image:
    name: gcr.io/kaniko-project/executor:v1.9.1-debug
    entrypoint: [""]
  script:
    - mkdir -p /kaniko/.docker
    - echo "{\"auths\":{\"$CI_REGISTRY\":{\"username\":\"$CI_REGISTRY_USER\",\"password\":\"$CI_REGISTRY_PASSWORD\"}}}" > /kaniko/.docker/config.json
    - |
      /kaniko/executor \
        --context "${CI_PROJECT_DIR}" \
        --dockerfile "${CI_PROJECT_DIR}/Dockerfile" \
        --destination "${CI_REGISTRY_IMAGE}:${CI_COMMIT_SHA}" \
        --cache=true
  only:
    - main
    - tags

update-deployment:
  stage: deploy
  image: alpine:3.15
  script:
    - apk add --no-cache git openssh-client
    - mkdir -p ~/.ssh
    - echo "$DEPLOY_KEY" | tr -d '\r' > ~/.ssh/id_rsa
    - chmod 600 ~/.ssh/id_rsa
    - ssh-keyscan -H gitlab.com >> ~/.ssh/known_hosts
    - git config --global user.email "ci@example.com"
    - git config --global user.name "GitLab CI"
    - git clone git@gitlab.com:my-org/my-app-config.git
    - cd my-app-config
    - sed -i "s|image: ${CI_REGISTRY_IMAGE}:.*|image: ${CI_REGISTRY_IMAGE}:${CI_COMMIT_SHA}|g" overlays/production/deployment-patch.yaml
    - git add overlays/production/deployment-patch.yaml
    - git commit -m "Update production image to ${CI_COMMIT_SHA}" || echo "No changes"
    - git push
  only:
    - main

內容解密

這個GitLab CI設定實作了一個完整的CI/CD流程:

  • 測試階段:執行單元測試和整合測試
  • 建置階段:使用Kaniko安全地建置容器映像並推播到儲存函式庫 佈署階段:更新Git儲存函式庫佈署設定,觸發ArgoCD自動同步

這種方法的優勢在於:

  1. 明確分離了不同階段的責任
  2. 使用Kaniko確保建置過程的安全性
  3. 遵循GitOps原則,透過Git儲存函式庫佈署設定
  4. 自動化整個流程,減少人為干預

在實際專案中,我發現這種整合方法特別適合跨職能團隊,因為它為開發人員和維運人員提供了清晰的責任界限和協作介面。

現代CI/CD的最佳實踐

根據我在多個專案中的經驗,以下是一些值得考慮的CI/CD最佳實踐:

  1. 採用不可變基礎架構原則:每次變更都應建立全新的環境,而不是修改現有環境
  2. 實施藍/綠佈署或金絲雀釋出:減少佈署風險,提供快速回復能力
  3. 整合安全掃描:在CI/CD流程中加入容器映像掃描、依賴項檢查和靜態程式碼分析
  4. 建立明確的佈署門檻:定義明確的標準,決定何時允許程式碼進入下一個環境
  5. 自動化測試覆寫率報告:持續監控測試覆寫率,防止技術債務累積
  6. 實施漸進式交付:使用功能標誌和A/B測試來控制新功能的釋出

這些實踐不僅可以提高佈署的可靠性和安全性,還可以加速團隊的交付速度。

現代CI/CD流程已經遠超出了簡單的自動化建置和佈署範疇,它已成為一個複雜的系統,涵蓋了機密管理、安全建置環境、靈活的設定管理和全面的控制。透過整合GitLab CI、Kaniko、ArgoCD和HashiCorp Vault等工具,我們能夠建立強大與安全的佈署管道,確保開發流程的穩定性和可預測性。