隨著軟體開發節奏的加快與部署頻率的提升,持續整合與持續部署已成為現代軟體工程的核心實踐。GitLab 作為一個功能完整的 DevOps 平台,不僅提供強大的版本控制能力,更整合了完整的 CI/CD 解決方案,幫助開發團隊實現從程式碼提交到生產部署的全自動化流程。然而,在實務應用中,開發者經常面臨各種挑戰,包括複雜的 Pipeline 配置、Runner 資源管理、容器化環境整合,以及基礎設施即程式碼的實踐等。

本文將深入探討 GitLab CI/CD 在企業環境中的實務應用,從基礎的語法問題排除與 Pipeline 設計,到進階的 Runner 標籤管理策略與容器化部署最佳實踐,再到企業級的基礎設施管理與 GitOps 理念實踐。我們將透過豐富的程式碼範例與技術細節,幫助讀者建立完整的 GitLab CI/CD 知識體系。此外,文章也將探討 DevOps 文化轉型的關鍵要素,以及自動化、抽象化與開發週期縮短等未來趨勢,為開發者提供全方位的技術視野。

GitLab Pipeline 配置與問題排除

GitLab CI/CD 的核心是 Pipeline,它定義了從程式碼提交到生產部署的完整工作流程。一個設計良好的 Pipeline 不僅能夠確保程式碼品質,更能大幅提升開發效率。然而,隨著專案複雜度的增加,Pipeline 配置也變得越來越複雜,語法錯誤與邏輯問題時常發生。

Pipeline 語法基礎與驗證

GitLab CI/CD 使用 YAML 格式定義 Pipeline 配置,這個配置檔案通常命名為 .gitlab-ci.yml。GitLab 提供了強大的 Pipeline 編輯器,能夠在編輯時即時檢查語法與邏輯錯誤,大幅降低配置錯誤的機率。

# GitLab CI/CD Pipeline 完整範例
# 展示 stages、jobs、rules 與 needs 的正確使用方式

# 定義 Pipeline 的執行階段
# stages 的順序決定了工作的執行順序
stages:
  - build          # 建置階段:編譯程式碼、建立產物
  - test           # 測試階段:執行各類測試
  - security       # 安全階段:執行安全掃描
  - deploy         # 部署階段:部署至目標環境

# 全域變數定義
# 這些變數可在所有 job 中使用
variables:
  DOCKER_DRIVER: overlay2
  MAVEN_OPTS: "-Dmaven.repo.local=.m2/repository"
  NODE_ENV: "production"

# 全域快取配置
# 加速建置過程,避免重複下載依賴
cache:
  paths:
    - .m2/repository/
    - node_modules/
  key: ${CI_COMMIT_REF_SLUG}

# 建置工作:建立應用程式
# 此工作在所有分支上執行
build_application:
  stage: build
  image: maven:3.8-openjdk-17
  script:
    # 顯示 Maven 版本資訊
    - mvn --version
    # 編譯專案並跳過測試
    - mvn clean compile -DskipTests
    # 打包應用程式
    - mvn package -DskipTests
  # 保存建置產物供後續工作使用
  artifacts:
    paths:
      - target/*.jar
    # 產物保存時間
    expire_in: 1 day
  # 只在程式碼變更時執行
  only:
    changes:
      - src/**/*
      - pom.xml

# 單元測試工作
# 執行單元測試並產生覆蓋率報告
unit_tests:
  stage: test
  image: maven:3.8-openjdk-17
  script:
    # 執行單元測試
    - mvn test
    # 產生覆蓋率報告
    - mvn jacoco:report
  # 產生測試報告供 GitLab UI 顯示
  artifacts:
    reports:
      junit: target/surefire-reports/TEST-*.xml
      coverage_report:
        coverage_format: cobertura
        path: target/site/jacoco/jacoco.xml
  # 計算測試覆蓋率
  coverage: '/Total.*?([0-9]{1,3})%/'
  # 此工作依賴建置工作完成
  needs:
    - build_application

# 靜態測試工作
# 只在功能分支上執行,主分支不執行
static_analysis:
  stage: test
  image: maven:3.8-openjdk-17
  script:
    # 執行靜態程式碼分析
    - mvn checkstyle:check
    - mvn pmd:check
    - mvn spotbugs:check
  # 使用 rules 定義複雜的執行條件
  rules:
    # 在主分支上永不執行
    - if: '$CI_COMMIT_REF_NAME == "main"'
      when: never
    # 在功能分支上總是執行
    - if: '$CI_COMMIT_REF_NAME =~ /^feature\//'
      when: always
    # 在開發分支上手動執行
    - if: '$CI_COMMIT_REF_NAME == "develop"'
      when: manual
    # 其他情況下執行
    - when: on_success
  # 允許此工作失敗而不影響 Pipeline
  allow_failure: true

# 安全掃描工作
# 掃描依賴套件的已知漏洞
security_scan:
  stage: security
  image: aquasec/trivy:latest
  script:
    # 掃描建置產物
    - trivy fs --severity HIGH,CRITICAL target/
  # 產生安全掃描報告
  artifacts:
    reports:
      dependency_scanning: gl-dependency-scanning-report.json
  # 此工作依賴建置工作完成
  needs:
    - build_application
  # 只在主分支與合併請求上執行
  rules:
    - if: '$CI_COMMIT_BRANCH == "main"'
    - if: '$CI_PIPELINE_SOURCE == "merge_request_event"'

# 部署至測試環境
# 只在主分支上自動執行
deploy_staging:
  stage: deploy
  image: bitnami/kubectl:latest
  script:
    # 配置 kubectl 連線至 Kubernetes 叢集
    - kubectl config set-cluster staging --server=$KUBE_URL --certificate-authority=$KUBE_CA
    - kubectl config set-credentials deployer --token=$KUBE_TOKEN
    - kubectl config set-context staging --cluster=staging --user=deployer
    - kubectl config use-context staging
    # 部署應用程式
    - kubectl apply -f k8s/staging/
    # 等待部署完成
    - kubectl rollout status deployment/app -n staging
  # 定義環境資訊
  environment:
    name: staging
    url: https://staging.example.com
    on_stop: stop_staging
  # 只在主分支上執行
  only:
    - main
  # 此工作依賴所有測試工作完成
  needs:
    - unit_tests
    - static_analysis
    - security_scan

# 停止測試環境
# 手動觸發的清理工作
stop_staging:
  stage: deploy
  image: bitnami/kubectl:latest
  script:
    # 刪除部署
    - kubectl delete -f k8s/staging/
  environment:
    name: staging
    action: stop
  # 手動觸發
  when: manual
  # 只在主分支上可用
  only:
    - main

# 部署至生產環境
# 需要手動核准
deploy_production:
  stage: deploy
  image: bitnami/kubectl:latest
  script:
    # 配置 kubectl 連線至生產叢集
    - kubectl config set-cluster production --server=$PROD_KUBE_URL --certificate-authority=$PROD_KUBE_CA
    - kubectl config set-credentials deployer --token=$PROD_KUBE_TOKEN
    - kubectl config set-context production --cluster=production --user=deployer
    - kubectl config use-context production
    # 部署應用程式
    - kubectl apply -f k8s/production/
    # 等待部署完成
    - kubectl rollout status deployment/app -n production
  environment:
    name: production
    url: https://www.example.com
  # 需要手動核准才能執行
  when: manual
  # 只在主分支上可用
  only:
    - main
  # 此工作依賴測試環境部署成功
  needs:
    - deploy_staging

這個完整的 Pipeline 配置展示了 GitLab CI/CD 的核心功能。透過 stages 定義執行階段順序,使用 rules 實現複雜的條件邏輯,利用 needs 建立工作依賴關係,並透過 artifacts 在工作之間傳遞檔案。每個工作都包含詳細的註解,說明其目的與實作細節。

Pipeline 執行邏輯與工作依賴

理解 Pipeline 的執行邏輯對於設計高效的工作流程至關重要。GitLab 提供了多種機制來控制工作的執行順序與條件。

# Pipeline 執行邏輯進階範例
# 展示複雜的工作依賴與並行執行策略

stages:
  - prepare
  - build
  - test
  - package
  - deploy

# 準備階段:設定環境與依賴
prepare_environment:
  stage: prepare
  script:
    - echo "準備建置環境"
    - npm install
  cache:
    key: ${CI_COMMIT_REF_SLUG}-npm
    paths:
      - node_modules/
  artifacts:
    paths:
      - node_modules/
    expire_in: 1 hour

# 並行建置:前端與後端同時建置
build_frontend:
  stage: build
  script:
    - echo "建置前端應用"
    - npm run build:frontend
  artifacts:
    paths:
      - dist/frontend/
  needs:
    - prepare_environment
  # 並行執行,不需等待其他建置工作
  parallel:
    matrix:
      - BUILD_TYPE: ['production', 'development']

build_backend:
  stage: build
  script:
    - echo "建置後端應用"
    - npm run build:backend
  artifacts:
    paths:
      - dist/backend/
  needs:
    - prepare_environment

# 並行測試:多種測試類型同時執行
test_unit:
  stage: test
  script:
    - npm run test:unit
  needs:
    - build_backend
  # 可與其他測試工作並行

test_integration:
  stage: test
  script:
    - npm run test:integration
  needs:
    - build_backend
    - build_frontend

test_e2e:
  stage: test
  script:
    - npm run test:e2e
  needs:
    - build_backend
    - build_frontend
  # 端對端測試需要完整環境
  services:
    - postgres:13
    - redis:6

# 打包階段:需要所有測試通過
package_docker:
  stage: package
  image: docker:latest
  services:
    - docker:dind
  script:
    - docker build -t $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA .
    - docker push $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA
  # 需要所有測試工作完成
  needs:
    - test_unit
    - test_integration
    - test_e2e

這個範例展示了如何使用 needs 關鍵字建立精確的工作依賴關係,實現並行執行以加速 Pipeline。透過 parallel 關鍵字,我們可以對同一工作使用不同參數執行多次,進一步提升效率。

@startuml
!define PLANTUML_FORMAT svg
!theme _none_

skinparam dpi auto
skinparam shadowing false
skinparam linetype ortho
skinparam roundcorner 5
skinparam defaultFontName "Microsoft JhengHei UI"
skinparam defaultFontSize 16
skinparam minClassWidth 100

title GitLab Pipeline 執行流程圖

start
:準備環境;
fork
  :建置前端;
fork again
  :建置後端;
end fork

fork
  :單元測試;
  note right
    依賴後端建置
  end note
fork again
  :整合測試;
  note right
    依賴前端與後端建置
  end note
fork again
  :端對端測試;
  note right
    依賴前端與後端建置
    需要完整服務環境
  end note
end fork

:打包 Docker 映像;
note right
  需要所有測試通過
end note

if (部署目標?) then (測試環境)
  :自動部署至測試環境;
elseif (生產環境)
  :等待手動核准;
  :部署至生產環境;
endif

stop

@enduml

這個流程圖清楚展示了 Pipeline 的執行邏輯,包括並行執行的工作與依賴關係。

常見問題診斷與解決

在實務應用中,開發者經常遇到各種 Pipeline 問題。以下是一些常見問題與解決方案。

# 常見問題範例與解決方案

# 問題 1: 工作未被執行
# 原因: rules 條件不符或 only/except 設定錯誤
conditional_job:
  stage: test
  script:
    - echo "此工作可能不會執行"
  # 錯誤的 rules 設定
  rules:
    # 條件永遠為 false
    - if: '$CI_COMMIT_BRANCH == "main" && $CI_COMMIT_BRANCH == "develop"'
      when: always
  
  # 正確的設定應該是:
  # rules:
  #   - if: '$CI_COMMIT_BRANCH == "main"'
  #     when: always
  #   - if: '$CI_COMMIT_BRANCH == "develop"'
  #     when: always

# 問題 2: 產物未能在工作間傳遞
# 原因: artifacts 路徑設定錯誤或過期時間太短
producer_job:
  stage: build
  script:
    - mkdir -p build/output
    - echo "產物內容" > build/output/artifact.txt
  artifacts:
    # 正確設定產物路徑
    paths:
      - build/output/
    # 設定合理的過期時間
    expire_in: 1 day

consumer_job:
  stage: test
  script:
    # 產物會自動下載至工作目錄
    - cat build/output/artifact.txt
  needs:
    - producer_job

# 問題 3: 快取未生效
# 原因: cache key 設定不當或路徑錯誤
cache_example:
  stage: build
  script:
    - npm install
  cache:
    # 使用分支名稱作為快取鍵
    # 確保不同分支使用不同快取
    key: ${CI_COMMIT_REF_SLUG}
    paths:
      # 正確的快取路徑
      - node_modules/
    # 設定快取策略
    policy: pull-push

# 問題 4: 環境變數未正確傳遞
# 原因: 變數作用域設定錯誤
variable_example:
  stage: test
  # 定義工作層級變數
  variables:
    LOCAL_VAR: "工作專屬變數"
  script:
    # 存取全域變數
    - echo $CI_COMMIT_SHA
    # 存取工作變數
    - echo $LOCAL_VAR
    # 存取 GitLab CI/CD 設定中的變數
    - echo $SECRET_TOKEN

這些範例展示了常見的配置錯誤與正確的解決方式,幫助開發者快速診斷與修復問題。

Runner 管理與標籤策略

GitLab Runner 是執行 CI/CD 工作的代理程式,合理的 Runner 管理與標籤策略對於優化 Pipeline 執行效率至關重要。

Runner 標籤的作用與配置

標籤是 GitLab 用來將工作分配給特定 Runner 的機制。透過精心設計的標籤策略,我們能夠確保工作在適當的環境中執行。

# Runner 標籤使用策略範例

# 範例 1: 基於作業系統的標籤
deploy_linux:
  stage: deploy
  script:
    - ./deploy.sh
  # 此工作只會在 Linux Runner 上執行
  tags:
    - linux
    - x86_64

deploy_windows:
  stage: deploy
  script:
    - .\deploy.ps1
  # 此工作只會在 Windows Runner 上執行
  tags:
    - windows
    - powershell

# 範例 2: 基於硬體需求的標籤
heavy_computation:
  stage: process
  script:
    - python3 heavy_task.py
  # 此工作需要高效能 Runner
  tags:
    - high-cpu
    - 32gb-ram
    - gpu

# 範例 3: 基於環境的標籤
deploy_staging:
  stage: deploy
  script:
    - kubectl apply -f staging.yaml
  # 此工作需要能存取測試環境的 Runner
  tags:
    - kubernetes
    - staging-cluster
    - network-internal

deploy_production:
  stage: deploy
  script:
    - kubectl apply -f production.yaml
  # 此工作需要能存取生產環境的 Runner
  tags:
    - kubernetes
    - production-cluster
    - network-dmz
  when: manual

# 範例 4: 基於軟體需求的標籤
android_build:
  stage: build
  script:
    - ./gradlew assembleRelease
  # 此工作需要 Android 開發環境
  tags:
    - android-sdk
    - java-17
    - gradle

ios_build:
  stage: build
  script:
    - xcodebuild -scheme MyApp -configuration Release
  # 此工作需要 macOS 與 Xcode
  tags:
    - macos
    - xcode-15
    - ios-deployment

# 範例 5: 複合標籤策略
docker_build_multiarch:
  stage: build
  image: docker:latest
  services:
    - docker:dind
  script:
    - docker buildx build --platform linux/amd64,linux/arm64 -t myapp:latest .
  # 此工作需要支援多架構建置的 Runner
  tags:
    - docker
    - buildx
    - privileged
    - ssd-storage

# 範例 6: 動態標籤選擇
dynamic_deployment:
  stage: deploy
  script:
    - ./deploy.sh $ENVIRONMENT
  # 使用變數動態選擇環境
  tags:
    - kubernetes
    - ${ENVIRONMENT}-cluster
  variables:
    ENVIRONMENT: staging
  rules:
    - if: '$CI_COMMIT_BRANCH == "main"'
      variables:
        ENVIRONMENT: production

標籤策略的設計需要考慮多個維度,包括作業系統、硬體資源、網路環境、軟體需求等。合理的標籤設計能夠確保工作在最適合的環境中執行,提升效率並降低失敗率。

Runner 註冊與配置

Runner 的註冊與配置是使用 GitLab CI/CD 的第一步。以下是完整的 Runner 配置流程。

#!/bin/bash

#############################################
# GitLab Runner 自動化註冊與配置指令碼
# 版本:1.0.0
# 作者:玄貓(BlackCat)
# 功能:自動化配置多種類型的 Runner
#############################################

# 全域配置
GITLAB_URL="https://gitlab.example.com"
REGISTRATION_TOKEN="YOUR_REGISTRATION_TOKEN"
RUNNER_NAME="docker-runner-01"
RUNNER_TAGS="docker,linux,amd64"

# 日誌函式
log() {
    echo "[$(date +'%Y-%m-%d %H:%M:%S')] $*"
}

# 安裝 GitLab Runner
install_runner() {
    log "開始安裝 GitLab Runner..."
    
    # 加入 GitLab Runner repository
    curl -L "https://packages.gitlab.com/install/repositories/runner/gitlab-runner/script.deb.sh" | bash
    
    # 安裝 GitLab Runner
    apt-get install -y gitlab-runner
    
    log "GitLab Runner 安裝完成"
}

# 註冊 Docker Runner
register_docker_runner() {
    log "註冊 Docker Runner..."
    
    gitlab-runner register \
        --non-interactive \
        --url "${GITLAB_URL}" \
        --registration-token "${REGISTRATION_TOKEN}" \
        --name "${RUNNER_NAME}" \
        --executor "docker" \
        --docker-image "alpine:latest" \
        --docker-privileged \
        --docker-volumes "/var/run/docker.sock:/var/run/docker.sock" \
        --docker-volumes "/cache" \
        --tag-list "${RUNNER_TAGS}" \
        --run-untagged="false" \
        --locked="false"
    
    log "Docker Runner 註冊完成"
}

# 註冊 Kubernetes Runner
register_k8s_runner() {
    log "註冊 Kubernetes Runner..."
    
    gitlab-runner register \
        --non-interactive \
        --url "${GITLAB_URL}" \
        --registration-token "${REGISTRATION_TOKEN}" \
        --name "k8s-runner-01" \
        --executor "kubernetes" \
        --kubernetes-namespace "gitlab-runner" \
        --kubernetes-image "alpine:latest" \
        --kubernetes-cpu-request "100m" \
        --kubernetes-cpu-limit "1" \
        --kubernetes-memory-request "128Mi" \
        --kubernetes-memory-limit "512Mi" \
        --tag-list "kubernetes,k8s,cloud" \
        --run-untagged="false"
    
    log "Kubernetes Runner 註冊完成"
}

# 配置 Runner 並行數
configure_concurrent() {
    log "配置 Runner 並行數..."
    
    # 修改 config.toml
    sed -i 's/concurrent = .*/concurrent = 10/' /etc/gitlab-runner/config.toml
    
    # 重啟 Runner 服務
    gitlab-runner restart
    
    log "並行數配置完成"
}

# 主要執行流程
main() {
    log "開始 GitLab Runner 配置流程..."
    
    install_runner
    register_docker_runner
    configure_concurrent
    
    log "GitLab Runner 配置完成"
    log "可使用 'gitlab-runner list' 查看已註冊的 Runner"
}

main "$@"

這個指令碼展示了如何自動化 Runner 的安裝與註冊流程,支援 Docker 與 Kubernetes 兩種執行器類型。

容器化 Pipeline 管理

容器化技術如 Docker 已成為現代 CI/CD 的標準做法。GitLab CI/CD 對容器化環境提供了完整的支援。

Docker-in-Docker 配置

Docker-in-Docker (DinD) 允許在容器中建置 Docker 映像,這是 CI/CD 中常見的需求。

# Docker-in-Docker 完整配置範例

# 全域變數定義
variables:
  # Docker 驅動程式配置
  DOCKER_DRIVER: overlay2
  # TLS 配置
  DOCKER_TLS_CERTDIR: "/certs"
  # 映像 registry 配置
  CI_REGISTRY: registry.gitlab.com
  CI_REGISTRY_IMAGE: $CI_REGISTRY/$CI_PROJECT_PATH

# Docker 映像建置工作
build_docker_image:
  stage: build
  # 使用 Docker 映像作為建置環境
  image: docker:24-cli
  # 使用 Docker-in-Docker 服務
  services:
    - docker:24-dind
  # 建置前準備
  before_script:
    # 等待 Docker daemon 就緒
    - until docker info; do sleep 1; done
    # 登入 GitLab Container Registry
    - echo $CI_REGISTRY_PASSWORD | docker login -u $CI_REGISTRY_USER --password-stdin $CI_REGISTRY
  script:
    # 建置 Docker 映像
    - docker build -t $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA .
    - docker build -t $CI_REGISTRY_IMAGE:latest .
    # 推送映像至 Registry
    - docker push $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA
    - docker push $CI_REGISTRY_IMAGE:latest
  # 建置後清理
  after_script:
    # 清理本地映像以節省空間
    - docker image prune -f
  # 只在主分支與標籤上執行
  only:
    - main
    - tags

# 多階段建置優化範例
build_optimized:
  stage: build
  image: docker:24-cli
  services:
    - docker:24-dind
  before_script:
    - docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY
  script:
    # 使用 BuildKit 進行優化建置
    - export DOCKER_BUILDKIT=1
    # 使用快取加速建置
    - docker build 
        --cache-from $CI_REGISTRY_IMAGE:latest 
        --build-arg BUILDKIT_INLINE_CACHE=1 
        -t $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA 
        -t $CI_REGISTRY_IMAGE:latest 
        .
    - docker push $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA
    - docker push $CI_REGISTRY_IMAGE:latest

# 多平台映像建置
build_multiarch:
  stage: build
  image: docker:24-cli
  services:
    - docker:24-dind
  before_script:
    - docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY
    # 啟用 buildx
    - docker buildx create --use
  script:
    # 建置多平台映像
    - docker buildx build 
        --platform linux/amd64,linux/arm64 
        -t $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA 
        -t $CI_REGISTRY_IMAGE:latest 
        --push 
        .
  # 需要特定標籤的 Runner
  tags:
    - docker
    - buildx

這些範例展示了不同場景下的 Docker 建置策略,從基本的 DinD 配置到進階的多平台建置。

Infrastructure as Code 與 GitOps

基礎設施即程式碼是現代維運的重要實踐,GitOps 則將這個概念與 Git 工作流程深度整合。

Terraform 基礎設施管理

Terraform 是業界最廣泛使用的 IaC 工具之一,能夠以宣告式語法管理各種雲端資源。

# GitLab CI/CD 整合 Terraform 完整範例

stages:
  - validate
  - plan
  - apply
  - destroy

# 全域變數
variables:
  TF_ROOT: ${CI_PROJECT_DIR}/terraform
  TF_VERSION: "1.6.0"

# Terraform 驗證
terraform_validate:
  stage: validate
  image:
    name: hashicorp/terraform:${TF_VERSION}
    entrypoint: [""]
  before_script:
    - cd ${TF_ROOT}
    - terraform --version
    # 初始化 Terraform
    - terraform init -backend-config="address=${TF_STATE_ADDRESS}" 
        -backend-config="lock_address=${TF_STATE_LOCK_ADDRESS}" 
        -backend-config="unlock_address=${TF_STATE_UNLOCK_ADDRESS}" 
        -backend-config="username=${TF_STATE_USERNAME}" 
        -backend-config="password=${TF_STATE_PASSWORD}" 
        -backend-config="lock_method=POST" 
        -backend-config="unlock_method=DELETE" 
        -backend-config="retry_wait_min=5"
  script:
    # 格式驗證
    - terraform fmt -check
    # 配置驗證
    - terraform validate
  # 在所有分支上執行
  except:
    - schedules

# Terraform 計畫
terraform_plan:
  stage: plan
  image:
    name: hashicorp/terraform:${TF_VERSION}
    entrypoint: [""]
  before_script:
    - cd ${TF_ROOT}
    - terraform init 
        -backend-config="address=${TF_STATE_ADDRESS}" 
        -backend-config="lock_address=${TF_STATE_LOCK_ADDRESS}" 
        -backend-config="unlock_address=${TF_STATE_UNLOCK_ADDRESS}" 
        -backend-config="username=${TF_STATE_USERNAME}" 
        -backend-config="password=${TF_STATE_PASSWORD}"
  script:
    # 產生執行計畫
    - terraform plan -out=tfplan
    # 顯示計畫細節
    - terraform show -json tfplan > tfplan.json
  artifacts:
    # 保存計畫檔案供後續使用
    paths:
      - ${TF_ROOT}/tfplan
      - ${TF_ROOT}/tfplan.json
    expire_in: 7 days
  # 在合併請求與主分支上執行
  only:
    - merge_requests
    - main

# Terraform 應用
terraform_apply:
  stage: apply
  image:
    name: hashicorp/terraform:${TF_VERSION}
    entrypoint: [""]
  before_script:
    - cd ${TF_ROOT}
    - terraform init 
        -backend-config="address=${TF_STATE_ADDRESS}" 
        -backend-config="lock_address=${TF_STATE_LOCK_ADDRESS}" 
        -backend-config="unlock_address=${TF_STATE_UNLOCK_ADDRESS}" 
        -backend-config="username=${TF_STATE_USERNAME}" 
        -backend-config="password=${TF_STATE_PASSWORD}"
  script:
    # 應用已產生的計畫
    - terraform apply -auto-approve tfplan
  # 定義環境資訊
  environment:
    name: production
    on_stop: terraform_destroy
  # 需要手動核准
  when: manual
  # 只在主分支上可用
  only:
    - main
  # 依賴計畫工作
  dependencies:
    - terraform_plan

# Terraform 銷毀
terraform_destroy:
  stage: destroy
  image:
    name: hashicorp/terraform:${TF_VERSION}
    entrypoint: [""]
  before_script:
    - cd ${TF_ROOT}
    - terraform init 
        -backend-config="address=${TF_STATE_ADDRESS}" 
        -backend-config="lock_address=${TF_STATE_LOCK_ADDRESS}" 
        -backend-config="unlock_address=${TF_STATE_UNLOCK_ADDRESS}" 
        -backend-config="username=${TF_STATE_USERNAME}" 
        -backend-config="password=${TF_STATE_PASSWORD}"
  script:
    # 銷毀所有資源
    - terraform destroy -auto-approve
  environment:
    name: production
    action: stop
  # 需要手動觸發
  when: manual
  # 只在主分支上可用
  only:
    - main

這個完整的 Terraform Pipeline 展示了從驗證、計畫到應用的完整流程,並整合了 GitLab 的狀態管理功能。

Ansible 配置管理整合

Ansible 提供了強大的配置管理能力,能夠自動化伺服器的配置與維護。

# GitLab CI/CD 整合 Ansible 完整範例

stages:
  - lint
  - deploy
  - verify

# Ansible 語法檢查
ansible_lint:
  stage: lint
  image: cytopia/ansible-lint:latest
  script:
    # 檢查 playbook 語法
    - ansible-lint playbooks/*.yml
    # 檢查角色語法
    - ansible-lint roles/*/tasks/*.yml
  # 產生檢查報告
  artifacts:
    reports:
      codequality: gl-code-quality-report.json

# 部署至測試環境
deploy_staging:
  stage: deploy
  image: cytopia/ansible:latest
  before_script:
    # 配置 SSH 金鑰
    - mkdir -p ~/.ssh
    - echo "$SSH_PRIVATE_KEY" | tr -d '\r' > ~/.ssh/id_rsa
    - chmod 600 ~/.ssh/id_rsa
    # 配置已知主機
    - ssh-keyscan -H $STAGING_HOST >> ~/.ssh/known_hosts
  script:
    # 執行 Ansible playbook
    - ansible-playbook -i inventories/staging 
        playbooks/deploy.yml 
        --extra-vars "env=staging version=$CI_COMMIT_SHA"
  environment:
    name: staging
    url: https://staging.example.com
  # 只在主分支上執行
  only:
    - main

# 部署至生產環境
deploy_production:
  stage: deploy
  image: cytopia/ansible:latest
  before_script:
    - mkdir -p ~/.ssh
    - echo "$SSH_PRIVATE_KEY" | tr -d '\r' > ~/.ssh/id_rsa
    - chmod 600 ~/.ssh/id_rsa
    - ssh-keyscan -H $PRODUCTION_HOST >> ~/.ssh/known_hosts
  script:
    # 執行生產環境部署
    - ansible-playbook -i inventories/production 
        playbooks/deploy.yml 
        --extra-vars "env=production version=$CI_COMMIT_SHA" 
        --check
    # 確認無誤後執行實際部署
    - ansible-playbook -i inventories/production 
        playbooks/deploy.yml 
        --extra-vars "env=production version=$CI_COMMIT_SHA"
  environment:
    name: production
    url: https://www.example.com
  # 需要手動核准
  when: manual
  # 只在主分支上可用
  only:
    - main

# 部署後驗證
verify_deployment:
  stage: verify
  image: curlimages/curl:latest
  script:
    # 健康檢查
    - curl -f https://www.example.com/health || exit 1
    # 版本驗證
    - curl https://www.example.com/version | grep $CI_COMMIT_SHA || exit 1
  # 依賴生產部署工作
  needs:
    - deploy_production

這個 Ansible Pipeline 展示了完整的配置管理流程,包括語法檢查、分階段部署與部署後驗證。

@startuml
!define PLANTUML_FORMAT svg
!theme _none_

skinparam dpi auto
skinparam shadowing false
skinparam linetype ortho
skinparam roundcorner 5
skinparam defaultFontName "Microsoft JhengHei UI"
skinparam defaultFontSize 16
skinparam minClassWidth 100

title GitOps 工作流程

actor 開發者
participant "Git Repository" as Git
participant "GitLab CI/CD" as GitLab
participant "Terraform" as TF
participant "Ansible" as AN
participant "Infrastructure" as Infra

開發者 -> Git: 提交基礎設施程式碼
Git -> GitLab: 觸發 Pipeline
GitLab -> TF: 驗證 Terraform 配置
TF --> GitLab: 驗證結果

alt 驗證通過
    GitLab -> TF: 產生執行計畫
    TF --> GitLab: 計畫細節
    
    開發者 -> GitLab: 核准變更
    GitLab -> TF: 應用變更
    TF -> Infra: 建立/更新資源
    Infra --> TF: 操作結果
    TF --> GitLab: 應用結果
    
    GitLab -> AN: 執行配置管理
    AN -> Infra: 配置資源
    Infra --> AN: 配置結果
    AN --> GitLab: 執行結果
    
    GitLab --> 開發者: 部署完成通知
else 驗證失敗
    GitLab --> 開發者: 錯誤通知
    開發者 -> Git: 修正程式碼
end

@enduml

這個流程圖展示了 GitOps 的完整工作流程,從程式碼提交到基礎設施變更的自動化過程。

DevOps 文化與未來趨勢

技術工具只是 DevOps 成功的一部分,更重要的是組織文化的轉變與持續改進的思維。

DevOps 文化的核心要素

DevOps 不僅是一套工具與實踐,更是一種文化理念,強調開發與維運團隊的協作、自動化與持續改進。成功的 DevOps 轉型需要組織在多個層面進行變革,包括打破團隊壁壘、建立共同責任、擁抱失敗作為學習機會,以及持續優化流程與工具。

透過 GitLab CI/CD 這樣的平台,團隊能夠實現從程式碼提交到生產部署的完全自動化,大幅縮短開發週期並提升交付品質。然而,工具的引入必須伴隨著文化的轉變,否則只會帶來更多的流程負擔而非效率提升。組織需要培養開發者的維運意識,同時提升維運人員的開發技能,最終實現真正的 DevOps 一體化。

自動化的未來發展

自動化是 DevOps 的核心驅動力,隨著技術的進步,自動化的範圍與深度都在持續擴大。從最初的建置自動化,到測試自動化、部署自動化,再到現在的基礎設施自動化與安全自動化,每個階段都大幅提升了軟體交付的效率與品質。未來,我們將看到更多 AI 驅動的自動化實踐,例如智慧化的測試案例生成、自動化的程式碼審查、預測性的系統維護等。

然而,自動化並非萬能,過度自動化可能導致系統複雜度增加,反而降低可維護性。關鍵在於找到自動化與人工介入的平衡點,在重複性高、規則明確的任務上實施自動化,而在需要判斷與創造力的環節保留人工決策。GitLab CI/CD 提供了靈活的配置機制,能夠支援這種平衡策略。

雲端原生與容器化趨勢

容器技術與 Kubernetes 已成為現代應用部署的事實標準,提供了一致的執行環境與強大的編排能力。無伺服器架構則進一步簡化了基礎設施管理,讓開發者能夠專注於業務邏輯。這些技術趨勢都指向一個方向:更高層次的抽象,更少的基礎設施管理負擔。

GitLab CI/CD 對這些技術提供了完整的支援,從容器建置、多平台映像支援,到 Kubernetes 部署、Auto DevOps 等功能。隨著雲端原生技術的持續演進,GitLab 也在不斷優化其對這些技術的整合,提供更流暢的開發體驗。

結語

GitLab CI/CD 作為一個功能完整的 DevOps 平台,不僅提供了強大的持續整合與持續部署能力,更支援完整的軟體開發生命週期管理。從程式碼版本控制、Issue 追蹤、程式碼審查,到 CI/CD Pipeline、容器 Registry、基礎設施管理,GitLab 提供了一站式的解決方案。

本文深入探討了 GitLab CI/CD 在實務應用中的各個面向,從基礎的 Pipeline 配置與問題排除,到進階的 Runner 管理、容器化策略,再到企業級的 IaC 整合與 GitOps 實踐。透過豐富的程式碼範例與技術細節,我們展示了如何充分利用 GitLab 的各項功能,建立高效、可靠的軟體交付流程。

然而,技術工具只是成功的一部分,更重要的是 DevOps 文化的建立與持續改進的思維。組織需要打破傳統的部門壁壘,建立開發與維運的共同責任,擁抱自動化與創新,並將失敗視為學習與改進的機會。只有在文化與技術並重的前提下,DevOps 轉型才能真正成功。

展望未來,隨著 AI 技術的進步、雲端原生架構的普及,以及自動化程度的提升,軟體開發與部署將變得更加智慧化與高效化。GitLab 作為 DevOps 領域的領導平台,將持續演進,為開發者提供更強大的工具與更流暢的體驗。持續學習、擁抱變化、積極實踐,是每位技術從業人員在這個快速演進的領域中保持競爭力的關鍵。