AI 輔助開發 CI/CD 工作流:Jenkins、K8s、ISO 27001 完整設計

重點摘要

  • AI 只負責寫 code、提 PR,不碰版本決策和 Production 部署,人類保留最終控制權
  • 透過 Git tag 觸發 Jenkins,Staging 全自動部署、Production 手動 helm 執行,兩階段驗證才上線
  • 敏感資訊三層隔離:.gitignore → K8s Secrets → etcd 加密,密碼永遠不進 repo
  • 補齊 RBAC、Audit Log、鏡像簽名、Secrets Scan 四大安全缺口,達到 ISO 27001 合規

AI 輔助開發越來越普遍,但大多數團隊面臨同一個問題:AI 寫的 code 要怎麼安全地上線? 誰決定部署時機?密碼怎麼管?如果 AI 出錯了,有什麼防護網?

本篇文章完整說明 ONEEC OMS 系統實際採用的 AI 協作工作流設計,包含完整的 User Story、Jenkins Pipeline 架構、三環境部署策略,以及通過安全審查後補齊的 RBAC、Audit Log、鏡像簽名等安全強化配置。

核心設計理念:人類掌控節奏,AI 加速執行

這套工作流的核心原則只有一句話:AI 是高效能的執行者,不是決策者。具體體現在以下四點:

  • AI 負責:寫 code、建 Dockerfile、提 PR、提供 Jenkins script 和 Helm chart
  • 用戶負責:code review、創建 git tag(決定版本和部署時機)、手動 helm 部署到 Production
  • 運維負責:管理 K8s Secrets、設定 Jenkins credentials、維護集群
  • 敏感資訊:密碼、API Key、SSL 憑證永遠不進入 Git repo

完整 User Story:從需求到上線的 10 個步驟

以下用一個真實場景說明整個流程:場景:優化訂單 API 的查詢效能

Step 1:AI 開發(feature branch)

AI 從 dev 分支切出 feature branch,完成開發後推送 PR:

# AI 執行
git checkout dev && git pull origin dev
git checkout -b feature/order-api-optimize

# 編寫程式碼...

# 本地驗證
docker-compose up -d
curl http://localhost:8080/api/orders?status=pending
# ✅ 回傳正確,效能提升 30%

# 提交並推送
git add . && git commit -m "feat(order-api): optimize query performance"
git push origin feature/order-api-optimize
# 建立 PR → dev

Step 2:用戶 Code Review & Merge

用戶在 GitHub UI 審查 PR:確認邏輯正確、有測試覆蓋、無敏感資訊後 approve 並 merge 到 dev。此時沒有任何自動化觸發,代碼靜靜等待部署決策。

Step 3:用戶創建 Staging Tag → Jenkins 自動觸發

用戶決定要部署到測試環境時,創建一個 staging-v* tag:

# 用戶執行
git tag staging-v1.0.1
git push origin staging-v1.0.1

# GitHub Webhook → Jenkins 自動執行:
# ├─ Secrets 掃描(gitleaks)
# ├─ docker build(所有 pods)
# ├─ cosign 簽名鏡像
# ├─ docker push to registry
# ├─ helm deploy to Staging K8s(使用 values-staging.yaml)
# └─ 通知用戶:Staging v1.0.1 is live

Step 4:用戶在 Staging 驗證

kubectl get pods -n staging
curl https://staging-api.example.com/api/orders?status=pending
# ✅ 功能正常,效能優化生效
# ✅ 錯誤率 0%
# ✅ 回應時間 < 100ms

Step 5:用戶創建 Production Tag → Jenkins 構建正式鏡像

# 用戶執行(確認 Staging 無誤後)
git tag v1.0.1
git push origin v1.0.1

# Jenkins 執行:
# ├─ Secrets 掃描
# ├─ docker build(所有 pods,tag 改為 v1.0.1)
# ├─ cosign 簽名鏡像
# ├─ docker push to registry
# ├─ 生成 Helm values(不含敏感資訊)
# └─ 通知用戶:Images ready, run helm command

Step 6:用戶手動部署到 Production

Production 部署是整個流程中唯一純手動的步驟,這是刻意設計的——確保每一次正式上線都有人類判斷:

# 用戶在本機執行
helm upgrade --install order-api \
  /path/to/your/prod-configs/order-api/values-prod.yaml \
  --set image.tag=v1.0.1 \
  -n production

# K8s 自動從 Secrets 注入密碼、API Key
# Kyverno 自動驗證鏡像簽名(未簽名直接拒絕)
# Deployment 完成 ✅

Step 7:監控確認上線成功

kubectl get pods -n production
curl https://api.example.com/api/orders?status=pending
# ✅ 正式環境驗證通過,上線成功

三個部署環境的定義與分工

環境 用途 部署方式 配置來源 觸發者
Dev 本地開發驗證 docker-compose up .env.dev AI(開發時)
Staging 測試環境(K8s) Jenkins 自動部署 values-staging.yaml(在 repo) 用戶(tag 觸發)
Production 正式環境(K8s) 手動 helm 部署 values-prod.yaml(用戶維護)+ K8s Secrets 用戶(手動執行)

Jenkins Pipeline 完整架構

Jenkins Pipeline 由 GitHub Webhook(tag push)觸發,整個流程分為 6 個 Stage:

Stage 0:Secrets 掃描(安全門控)

這是整個 Pipeline 的第一道防線,也是最重要的安全門控。使用 gitleaks 掃描 repo 中是否含有密碼、API Key 等敏感資訊,發現即中止構建並通知安全告警

stage('Secrets Scan') {
    steps {
        sh '''
            gitleaks detect \
              --source . \
              --config .gitleaks.toml \
              --exit-code 1 \
              --report-format json \
              --report-path gitleaks-report.json
        '''
    }
    post {
        failure {
            sh 'sh scripts/notify-security-alert.sh ${TAG_NAME} gitleaks-report.json'
            error('❌ Secrets 掃描發現敏感資訊,構建中止!')
        }
    }
}

Stage 1:Tag 偵測(決定部署目標)

根據 tag 名稱判斷本次構建的部署目標:

stage('Detect Tag') {
    steps {
        script {
            if (env.TAG_NAME =~ /^staging-v.*/) {
                env.DEPLOYMENT_ENV = 'staging'
            } else if (env.TAG_NAME =~ /^v.*/) {
                env.DEPLOYMENT_ENV = 'production'
            } else {
                error("❌ 未知 tag 格式: ${env.TAG_NAME}")
            }
        }
    }
}

Stage 2:Build Images

構建所有 Pod 的 Docker 鏡像。鏡像本身不含任何配置、密碼、API Key,這是配置與代碼分離的核心原則:

#!/bin/bash
# scripts/build-docker.sh
TAG=$1

docker build -t registry.example.com/order-api:${TAG} ./simpleec-api
docker build -t registry.example.com/user-app:${TAG} ./user-app
docker build -t registry.example.com/channel-job:${TAG} ./simpleec-channel-job
# ... 所有 pods

Stage 3:Sign Images(供應鏈安全)

使用 cosign 為每個鏡像簽名,確保 Production 只能部署來自 Jenkins 的受信任鏡像:

stage('Sign Images') {
    steps {
        withCredentials([file(credentialsId: 'cosign-private-key', variable: 'COSIGN_KEY')]) {
            sh 'sh scripts/sign-docker.sh ${TAG_NAME} ${COSIGN_KEY}'
        }
    }
}

# scripts/sign-docker.sh
for IMAGE in "${IMAGES[@]}"; do
    cosign sign --key "${COSIGN_KEY}" \
      --tlog-upload=false \
      "${IMAGE}"
done

Stage 4:Push Images

推送到 Docker Registry。Registry 啟用 Immutable Tags,同一個 tag 無法被覆蓋,確保版本不可篡改:

stage('Push Images') {
    steps {
        withCredentials([usernamePassword(
            credentialsId: 'docker-registry-creds',
            usernameVariable: 'REGISTRY_USER',
            passwordVariable: 'REGISTRY_PASS'
        )]) {
            sh 'sh scripts/push-docker.sh ${TAG_NAME}'
        }
    }
}

Stage 5a(Staging):自動部署到 Staging K8s

stage('Deploy to Staging') {
    when { expression { env.DEPLOYMENT_ENV == 'staging' } }
    steps {
        withCredentials([file(credentialsId: 'kubeconfig-staging', variable: 'KUBECONFIG')]) {
            sh '''
                helm upgrade --install order-api ./k8s/helm/order-api \
                  --values ./k8s/helm/order-api/values-staging.yaml \
                  --set image.tag=${TAG_NAME} \
                  -n staging
            '''
        }
    }
}

Stage 5b(Production):生成 Helm Values,通知用戶手動部署

對於 Production tag,Jenkins 不自動部署,而是生成配置檔並通知用戶手動執行:

stage('Generate Helm Values') {
    when { expression { env.DEPLOYMENT_ENV == 'production' } }
    steps {
        sh 'sh scripts/generate-helm-values.sh ${TAG_NAME}'
        // 生成 values-v${TAG_NAME}.yaml(不含敏感資訊)
        // 通知用戶:Images ready, run helm command
    }
}

Helm 配置隔離:敏感資訊三層防護

配置分為三層,層層隔離:

第一層:values-staging.yaml(在 repo,測試配置)

# 主機名用占位符,從 Jenkins 環境變數注入,不硬編碼內網地址
env:
  DATABASE_HOST: "${POSTGRES_STAGING_HOST}"
  DATABASE_NAME: simpleec_test
  REDIS_HOST: "${REDIS_STAGING_HOST}"
  API_LOG_LEVEL: DEBUG

第二層:values-prod.yaml(用戶本機維護,不進 repo)

# 用戶的私密文件,只在本機
env:
  DATABASE_HOST: postgres-prod.example.com
  API_LOG_LEVEL: WARN
  # ⚠️ 資料庫密碼不在這裡!從 K8s Secrets 注入

envFrom:
  - secretRef:
      name: database-prod-creds  # K8s Secret(運維管理)
  - secretRef:
      name: api-keys-prod        # K8s Secret(運維管理)

第三層:K8s Secrets + etcd 加密

# 運維在 Production K8s 上創建
kubectl create secret generic database-prod-creds \
  --from-literal=username=prod_user \
  --from-literal=password=<secure-password> \
  -n production

# K8s 預設 Secrets 以 base64 存在 etcd(並非加密!)
# 必須啟用 encryption at rest
# /etc/kubernetes/encryption-config.yaml
apiVersion: apiserver.config.k8s.io/v1
kind: EncryptionConfiguration
resources:
  - resources: ["secrets"]
    providers:
      - aescbc:
          keys:
            - name: key1
              secret: <base64-encoded-32-byte-key>

安全強化:補齊四大缺口

原始設計經過安全審查後,發現四個必須在投產前補足的缺口:

缺口一:K8s RBAC 未定義

三個角色各有最小權限(文件放在 k8s/rbac/):

角色 允許操作 明確禁止
Jenkins SA(staging) update/patch Deployments, get Pods 讀取任何 Secrets
用戶(production) helm 部署相關資源 讀取業務 Secrets(DB 密碼、API Key)
運維(production) Secrets 完整管理權
# 驗證 Jenkins SA 無法讀取 Secrets(應輸出 no)
kubectl auth can-i get secrets \
  --as=system:serviceaccount:staging:jenkins-deployer \
  -n staging

缺口二:K8s Audit Log 未配置

ISO 27001 A.12.4.1 要求所有敏感操作都要有日誌。以下 Audit Policy 至少記錄 Secrets 訪問和 Deployment 變更:

# /etc/kubernetes/audit-policy.yaml
apiVersion: audit.k8s.io/v1
kind: Policy
rules:
  - level: Metadata
    resources:
      - group: ""
        resources: ["secrets"]  # 所有 Secrets 訪問都記錄

  - level: Request
    verbs: ["create", "update", "delete", "patch"]
    resources:
      - group: "apps"
        resources: ["deployments"]

  - level: None
    users: ["system:kube-proxy"]
    verbs: ["watch", "list"]

缺口三:鏡像簽名驗證(Kyverno 準入控制)

確保集群只能部署來自 Jenkins 簽名的鏡像,防止鏡像替換攻擊:

apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
  name: verify-image-signatures
spec:
  validationFailureAction: Enforce  # 未簽名鏡像直接拒絕
  rules:
    - name: check-image-signature
      match:
        any:
          - resources:
              kinds: ["Pod"]
              namespaces: ["staging", "production"]
      verifyImages:
        - imageReferences:
            - "registry.example.com/*"
          attestors:
            - count: 1
              entries:
                - keys:
                    publicKeys: |-
                      -----BEGIN PUBLIC KEY-----
                      # cosign.pub 內容
                      -----END PUBLIC KEY-----

缺口四:GitHub Branch Protection 口頭約定 → 技術強制

分支 Required Reviews CI 必須通過 Push 限制
main 2 人 approve ✅ jenkins-build + secrets-scan 僅 team-lead
staging 1 人 approve ✅ jenkins-build + secrets-scan 僅 team-lead
dev 1 人 approve 必須透過 PR(AI 不能直接 push)

Git 分支策略與 Tag 命名規範

整個工作流的分支拓撲如下:

main                     # Production 對應,受嚴格保護
 └─ tag: v1.0.0, v1.0.1  # 觸發 Jenkins 構建 Production 鏡像

staging                  # 測試環境,中度保護
 └─ tag: staging-v1.0.0  # 觸發 Jenkins 自動部署到 Staging K8s

dev                      # 開發積累,AI 透過 PR 提交
 └─ 來源:feature/* 合入

feature/*                # AI 的工作分支(每個功能一個)
 ├─ feature/user-auth
 ├─ feature/order-api
 └─ feature/channel-job-momo

敏感資訊完整隔離架構

存放位置 可以存什麼 絕對不能存什麼 管理者
Git Repository 代碼、Dockerfile、values-staging.yaml、Helm chart 模板 密碼、API Key、SSL 憑證、values-prod.yaml AI + 用戶
Docker Registry 不含配置的乾淨鏡像(cosign 簽名) 任何敏感資訊 Jenkins(push)
K8s Secrets(etcd 加密) database-prod-creds、api-keys-prod、SSL 憑證 運維
Jenkins Credentials GitHub token、Registry credentials、cosign key、kubeconfig 運維

回滾策略

Staging 環境回滾

# 快速回滾到上一個版本
helm rollback order-api 0 -n staging

# 或指定版本
helm upgrade order-api ./k8s/helm/order-api \
  --values ./k8s/helm/order-api/values-staging.yaml \
  --set image.tag=staging-v1.0.0 \
  -n staging

Production 環境回滾

# 查看部署歷史
helm history order-api -n production

# 回滾到上一個版本
helm rollback order-api 0 -n production

# 所有 tag 在 Git 可追溯
git log --oneline --all | grep "v1.0"

投產前安全檢查清單

在正式上線前,以下所有項目必須確認通過:

代碼倉庫安全

  • ✅ .gitignore 包含 .env, .env.dev, **/values-prod.yaml
  • ✅ repo 根目錄存在 .gitleaks.toml 配置文件
  • ✅ pre-commit hook 已安裝
  • ✅ git log –all — ‘*.env’ 確認歷史中無敏感文件

Jenkins Pipeline

  • ✅ 第一個 Stage 為 Secrets Scan(gitleaks)
  • ✅ Sign Images Stage 已配置(cosign)
  • ✅ Push Images 使用 Jenkins Credentials(非明文)
  • ✅ GitHub Webhook Secret 已配置(Jenkins + GitHub 雙端)

K8s 訪問控制

  • ✅ k8s/rbac/ 三個 RBAC 文件已 apply
  • ✅ Jenkins SA 驗證:kubectl auth can-i get secrets … → no
  • ✅ Kyverno 已安裝,鏡像簽名驗證策略已 apply
  • ✅ etcd encryption at rest 已啟用(運維確認)

審計和監控

  • ✅ K8s Audit Log 已配置(audit-policy.yaml)
  • ✅ Audit Log 保留策略 ≥ 90 天
  • ✅ 告警規則已配置(部署失敗、Secrets 掃描失敗)

總結:這套工作流解決了什麼問題?

AI 輔助開發的核心挑戰不是技術,而是信任邊界:誰能做什麼?誰為每個決定負責?這套工作流的答案很清楚:

  • AI 的邊界:寫 code、提 PR、建 Docker image — 技術執行層
  • 用戶的邊界:review 代碼、創建 tag、手動部署 Production — 決策層
  • 運維的邊界:管理 Secrets、維護集群、配置 credentials — 基礎設施層
  • 自動化的邊界:Jenkins 在 tag 觸發後執行既定腳本 — 不越界,不決策

這種分層設計讓 AI 協作既高效又安全,每一個部署都有完整的審計軌跡,每一個敏感操作都需要人類授權。

留言

在〈AI 輔助開發 CI/CD 工作流:Jenkins、K8s、ISO 27001 完整設計〉中有 1 則留言

  1. […] 想了解新軌道的 CI/CD 具體設計,可以參考上一篇文章:AI 輔助開發 CI/CD 工作流:Jenkins、K8s、ISO 27001 完整設計。 […]

發佈回覆給「10 年舊系統如何安全導入 AI 開發:Strangler Fig 遷移方法論 – 小丁的家」的留言 取消回覆

發佈留言必須填寫的電子郵件地址不會公開。 必填欄位標示為 *