本篇用一个 Vue 前端项目结合 Claude Code 实现一次完整的 CI/CD 发布流程,实现的效果:从内网 GitLab 推送 → Kaniko 构建 → Harbor 推送 → Kubernetes 部署 → Istio 网关暴露,外加 Claude 自动 MR Review、Release Notes、部署失败根因分析。

完整的拓扑流程:

开发提交 → GitLab MR
  ↓ Claude MR Review(评论挂到 MR)
  ↓ MR 合并
开发打 tag → GitLab Pipeline
  ↓ Kaniko 构建 → Harbor
  ↓ kubectl apply → k8s(Deployment + Service + VirtualService)
  ↓ Istio Gateway → aiops-lab.eeo-inc.com
  ↓ Claude 自动写 Release Notes(写到 GitLab Release)
失败时 → Claude 拉 pod logs/events → 根因分析(写到 Job log)

image.png

关键技术选型

  • 构建:Kaniko(不需要 dind,Runner 不开特权)
  • 镜像仓库:内网 Harbor,机器人账号(robot account)
  • 部署:原生 kubectl + 三个 yaml(Deployment / Service / VirtualService)
  • 网关:复用集群里已有的 istio-system/test-gateway,挂域名
  • AI 接入:LiteLLM 中转 OpenAI 兼容协议(/v1/chat/completions)

CI/CD 完整 Pipeline 配置

image.png

变量与阶段定义

variables:
  HARBOR_REGISTRY: "registry.xxx.com"
  HARBOR_PROJECT: "ops/aiops"
  IMAGE_NAME: "trace-analysis-app"
  IMAGE_FULL: "${HARBOR_REGISTRY}/${HARBOR_PROJECT}/${IMAGE_NAME}"
  HARBOR_USER: $CI_REGISTRY_NAME
  HARBOR_PASSWORD: $CI_REGISTRY_PASSWD
  K8S_NAMESPACE: "devops"
  K8S_APP_NAME: "trace-analysis-app"
  CLAUDE_MODEL: $ANTHROPIC_MODEL

stages:
  - review
  - build
  - deploy
  - release
  - notify

所有敏感参数(Harbor 账号、kubeconfig、Claude key、Bot token)都通过 GitLab CI/CD Variables 注入,不进 yaml。Settings → CI/CD → Variables,并打 Masked + Protected。

Kaniko 构建(main 分支 / tag 触发)

build-and-push:
  stage: build
  image:
    name: gcr.io/kaniko-project/executor:v1.23.2-debug
    entrypoint: [""]
  rules:
    - if: '$CI_COMMIT_BRANCH == "main"'
    - if: '$CI_COMMIT_TAG'
  script:
    - export DOCKER_CONFIG="${CI_PROJECT_DIR}/.docker"
    - mkdir -p "${DOCKER_CONFIG}"
    - |
      AUTH=$(echo -n "${HARBOR_USER}:${HARBOR_PASSWORD}" | base64 | tr -d '\n')
      cat > "${DOCKER_CONFIG}/config.json" <<EOF
      {"auths":{"${HARBOR_REGISTRY}":{"auth":"${AUTH}"}}}
      EOF
    - BUILD_TAG="${CI_COMMIT_TAG:-${CI_COMMIT_BRANCH}-${CI_COMMIT_SHORT_SHA}}"
    - /kaniko/executor
        --context "${CI_PROJECT_DIR}"
        --dockerfile "${CI_PROJECT_DIR}/Dockerfile"
        --destination "${IMAGE_FULL}:${BUILD_TAG}"
        --destination "${IMAGE_FULL}:latest"
        --cache=true
        --cache-repo "${IMAGE_FULL}/cache"
    - echo "BUILD_TAG=${BUILD_TAG}" > build.env
  artifacts:
    reports:
      dotenv: build.env

K8s 部署(tag 触发)

deploy-k8s:
  stage: deploy
  image: alpine:3.20
  needs:
    - job: build-and-push
      artifacts: true
  rules:
    - if: '$CI_COMMIT_TAG'
  before_script:
    - apk add --no-cache curl ca-certificates
    - |
      ARCH=$(uname -m); KARCH=amd64
      [ "$ARCH" = "aarch64" ] && KARCH=arm64
      curl -fsSL -o /usr/local/bin/kubectl \
        "https://dl.k8s.io/release/v1.29.10/bin/linux/${KARCH}/kubectl"
      chmod +x /usr/local/bin/kubectl
    - export KUBECONFIG="$KUBECONFIG_FILE"
  script:
    - cd deploy/k8s
    - kubectl apply -f namespace.yaml
    - |
      kubectl create secret docker-registry harbor-pull-secret \
        --docker-server="${HARBOR_REGISTRY}" \
        --docker-username="${HARBOR_USER}" \
        --docker-password="${HARBOR_PASSWORD}" \
        --namespace="${K8S_NAMESPACE}" \
        --dry-run=client -o yaml | kubectl apply -f -
    - sed -i "s|__IMAGE_TAG__|${BUILD_TAG:-${CI_COMMIT_TAG}}|g" deployment.yaml
    - kubectl apply -f deployment.yaml -f service.yaml -f virtualservice.yaml
    - kubectl rollout status deployment/${K8S_APP_NAME} -n ${K8S_NAMESPACE} --timeout=180s

Claude MR 评审(任一 MR 触发)

image.png

Claude 调用模板(复用基础镜像 + 依赖安装):

.claude-call:
  image: alpine:3.20
  before_script:
    - apk add --no-cache curl jq ca-certificates git

实际调用(拼 prompt → jq 装 JSON → curl LiteLLM → 评论挂回 MR):

claude-mr-review:
  stage: review
  extends: .claude-call
  rules:
    - if: '$CI_PIPELINE_SOURCE == "merge_request_event"'
  script:
    - git fetch origin "$CI_MERGE_REQUEST_TARGET_BRANCH_NAME" --depth=200
    - DIFF=$(git diff "origin/${CI_MERGE_REQUEST_TARGET_BRANCH_NAME}...HEAD" | head -c 200000)
    - echo "$DIFF" > /tmp/diff.txt
    - |
      {
        printf '%s\n' '你是资深前端 + DevOps 工程师,正在评审 GitLab MR。请用中文输出 Markdown:'
        printf '%s\n' '## 总体评价'
        printf '%s\n' '## 必须修改 (🚫)'
        printf '%s\n' '## 建议改进 (💡)'
        printf '%s\n' '## 测试覆盖'
        printf '%s\n' '下面是 diff:'
        printf '%s\n' '\`\`\`diff'
        cat /tmp/diff.txt
        printf '%s\n' '\`\`\`'
      } > /tmp/prompt.txt
    - |
      jq -n --arg model "$ANTHROPIC_MODEL" --rawfile user /tmp/prompt.txt \
        '{model: $model, max_tokens: 2048, messages: [
          {role: "system", content: "你是严谨的代码评审者,输出简洁、可执行。"},
          {role: "user",   content: $user}
        ]}' > /tmp/body.json
    - |
      curl -fsS --max-time 120 -X POST "${ANTHROPIC_BASE_URL%/}/chat/completions" \
        -H "Authorization: Bearer ${ANTHROPIC_API_KEY}" \
        -H "Content-Type: application/json" \
        -d @/tmp/body.json > /tmp/resp.json
    - REVIEW=$(jq -r '.choices[0].message.content' /tmp/resp.json)
    - |
      jq -n --rawfile body <(echo "## 🤖 Claude Review"; echo; echo "$REVIEW") \
        '{body: $body}' > /tmp/note.json
      curl -fsS -X POST -H "PRIVATE-TOKEN: ${CLAUDE_BOT_TOKEN}" \
        -H "Content-Type: application/json" -d @/tmp/note.json \
        "${CI_API_V4_URL}/projects/${CI_PROJECT_ID}/merge_requests/${CI_MERGE_REQUEST_IID}/notes"
  allow_failure: true

部署失败 → 自动根因分析

deploy-failure-rca:
  stage: notify
  extends: .claude-call
  rules:
    - if: '$CI_COMMIT_TAG'
      when: on_failure
  script:
    - kubectl -n "$K8S_NAMESPACE" describe pods -l app=$K8S_APP_NAME > /tmp/diag/describe.txt
    - kubectl -n "$K8S_NAMESPACE" get events --sort-by=.lastTimestamp > /tmp/diag/events.txt
    - kubectl -n "$K8S_NAMESPACE" logs -l app=$K8S_APP_NAME --tail=200 > /tmp/diag/logs.txt
    # 拼 prompt → jq → curl → 解析返回,把 Markdown RCA 直接打印到 job 日志

Tag 推送 → 自动写 Release Notes

release-notes:
  stage: release
  extends: .claude-call
  needs: [deploy-k8s]
  rules:
    - if: '$CI_COMMIT_TAG'
  script:
    - PREV_TAG=$(git tag --sort=-creatordate | grep -v "^${CI_COMMIT_TAG}$" | head -n 1)
    - COMMITS=$(git log --pretty=format:"- %s (%h)" "${PREV_TAG}..${CI_COMMIT_TAG}")
    # → 让 Claude 按"✨新功能 / 🐛修复 / 🔧改进 / 📦CI"分类
    # → 调 GitLab Releases API 自动建 Release

核心要点

  • 每次合 MR 自动跑 lint / build
  • 打 tag 时自动构建镜像 → 推 Harbor → 部署到 K8s
  • 走 Istio Gateway 用域名访问,namespace 单独隔离
  • AI 自动评审代码、写 Release Notes、部署失败时自动给根因分析
  • 所有敏感信息都不能写到 yaml 里,全部走 GitLab CI/CD Variables(Masked + Protected)

image.png

image.png