掌握 Istio 金丝雀部署:使用 Jenkins、GitHub Actions 与 ArgoCD 的完整 CI/CD 流水线

发布: (2025年12月7日 GMT+8 16:31)
6 min read
原文: Dev.to

Source: Dev.to

什么是 CI/CD 中的金丝雀部署?

金丝雀部署指的是缓慢发布新版本,使用少量用户流量进行测试,并逐步增加流量。
典型的进度如下:

流量分配版本
100 % → 0 %v1 → v2
90 % / 10 %v1 / v2
70 % / 30 %v1 / v2
50 % / 50 %v1 / v2
0 % / 100 %v1 / v2

如果检测到任何问题,部署会立即回滚。将 CI/CD 与 Istio 结合可以实现全自动化的流程。

Istio 金丝雀流水线的关键组件

组件职责
CI 流水线构建 Docker 镜像、运行测试、推送制品
CD 流水线部署到 Kubernetes
Istio VirtualService在不同版本之间分流流量
Istio DestinationRule定义子集(v1、v2)
监控(Prometheus)检测故障
回滚逻辑如有需要,将流量切回 v1

该流水线可以使用 JenkinsGitHub ActionsArgoCD 实现——所有方式均使用相同的 Istio YAML 文件。

Git 仓库的目录结构

istio-microservices/
├── services/
│   └── orders-service/
│       ├── src/
│       └── Dockerfile
├── k8s/
│   └── orders/
│       ├── deployment.yaml
│       ├── service.yaml
│       ├── dest-rule.yaml
│       ├── vs-100-0.yaml
│       ├── vs-90-10.yaml
│       ├── vs-70-30.yaml
│       ├── vs-50-50.yaml
│       └── vs-0-100.yaml
└── ci/
    ├── Jenkinsfile
    └── github-actions.yaml

每个 VirtualService 文件对应金丝雀发布的一个阶段。

Istio 金丝雀清单

DestinationRule(子集)

apiVersion: networking.istio.io/v1beta1
kind: DestinationRule
metadata:
  name: orders-service
  namespace: microservices
spec:
  host: orders-service
  subsets:
  - name: v1
    labels:
      version: v1
  - name: v2
    labels:
      version: v2

VirtualService 阶段

阶段 1 – 100 % v1(起始点) – vs-100-0.yaml

apiVersion: networking.istio.io/v1beta1
kind: VirtualService
metadata:
  name: orders-service
  namespace: microservices
spec:
  hosts:
  - orders-service
  http:
  - route:
    - destination:
        host: orders-service
        subset: v1
      weight: 100
    - destination:
        host: orders-service
        subset: v2
      weight: 0

阶段 2 – 90 % v1 / 10 % v2 – vs-90-10.yaml

# similar structure, weights 90 and 10

其余文件(vs-70-30.yamlvs-50-50.yamlvs-0-100.yaml)只需相应调整 weight 值即可。

自动晋升的健康指标

在进入下一个流量分配之前,流水线会检查以下指标(示例在 Jenkinsfile 片段中):

pipeline {
  agent any
  environment {
    NAMESPACE = 'microservices'
  }
  stages {
    stage('Deploy v2') {
      steps {
        sh "kubectl apply -f k8s/orders/deployment.yaml -n $NAMESPACE"
      }
    }

    stage('Canary 10%') {
      steps {
        sh "kubectl apply -f k8s/orders/vs-90-10.yaml -n $NAMESPACE"
      }
    }

    stage('Check Metrics') {
      steps {
        script {
          def errorRate = sh(script: "curl -s http://prometheus/api/v1/query?query=...", returnStdout: true).trim()
          if (errorRate.toDouble() > 0.01) {
            error "High error rate detected — Rolling back"
          }
        }
      }
    }

    stage('Shift to 50/50') {
      steps {
        sh "kubectl apply -f k8s/orders/vs-50-50.yaml -n $NAMESPACE"
      }
    }

    stage('Promote to 100% v2') {
      steps {
        sh "kubectl apply -f k8s/orders/vs-0-100.yaml -n $NAMESPACE"
      }
    }
  }

  post {
    failure {
      sh "kubectl apply -f k8s/orders/vs-100-0.yaml -n $NAMESPACE"
    }
  }
}

GitHub Actions 工作流(简洁、云原生)

name: Istio Canary Deployment
on:
  push:
    branches: [ main ]

jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout
        uses: actions/checkout@v3

      - name: Build Image
        run: |
          docker build -t myrepo/orders-service:${{ github.run_number }} services/orders-service
          docker push myrepo/orders-service:${{ github.run_number }}

      - name: Deploy v2
        run: |
          sed -i "s|image:.*|image: myrepo/orders-service:${{ github.run_number }}|g" k8s/orders/deployment.yaml
          kubectl apply -f k8s/orders/deployment.yaml -n microservices

      - name: Canary 10%
        run: kubectl apply -f k8s/orders/vs-90-10.yaml -n microservices

      - name: Wait & Check Prometheus
        run: |
          sleep 60
          ERR=$(curl -s "http://prometheus/api/v1/query?query=sum(rate(istio_requests_total{response_code=~\"5.*\"}[1m]))/sum(rate(istio_requests_total[1m]))")
          ERR_VAL=$(echo $ERR | jq '.data.result[0].value[1]' | tr -d '"')
          if (( $(echo "$ERR_VAL > 0.01" | bc -l) )); then exit 1; fi

      - name: Shift to 50/50
        run: kubectl apply -f k8s/orders/vs-50-50.yaml -n microservices

      - name: Promote 100% v2
        run: kubectl apply -f k8s/orders/vs-0-100.yaml -n microservices

使用 ArgoCD 的 GitOps 版本

ArgoCD 持续监视仓库并应用 Istio 清单,实现全自动化的 GitOps 工作流。

ArgoCD 应用定义(argo-app.yaml

apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
  name: istio-canary-app
  namespace: argocd
spec:
  project: default
  source:
    repoURL: https://github.com/myrepo/istio-microservices
    path: k8s/orders
  destination:
    server: https://kubernetes.default.svc/
    namespace: microservices
  syncPolicy:
    automated:
      prune: true
      selfHeal: true

部署该应用:

kubectl apply -f argo-app.yaml

要切换流量,只需提交想要的 VirtualService 文件(例如 vs-90-10.yaml)并推送;ArgoCD 会立即检测到变化并应用。

自动回滚策略

当满足以下任意条件时,回滚会立即触发:

  • 错误率 > 1 %
  • p95 延迟超过设定阈值
  • Pod 重启次数急剧上升
  • Prometheus 检测到流量异常

回滚命令(流水线在失败时使用):

kubectl apply -f k8s/orders/vs-100-0.yaml -n microservices

CI/CD 中的可观测性

  • Grafana – 指标仪表盘
  • Jaeger – 按版本的分布式追踪
  • Kiali – 可视化流量图(颜色区分 v1 与 v2)
  • Prometheus – 原始指标与告警

在金丝雀发布期间,Kiali 会展示流量分配情况,帮助运维人员实时了解新版本的影响。

最终总结

本指南中,你构建了一个完整的 Istio 金丝雀部署流水线,包含:

  • 自动化 Docker 镜像构建
  • 新微服务版本(v2)的部署
  • 使用 Istio VirtualServiceDestinationRule 的流量切换
  • 基于 Prometheus 的健康检查后晋升
  • 失败时的自动回滚
  • 使用 ArgoCD 的 GitOps 实现
  • Jenkins 与 GitHub Actions 示例用于 CI/CD
  • 可观测性栈(Grafana、Jaeger、Kiali、Prometheus)

这些模式已在 Netflix、IBM、Red Hat、Airbnb 等公司的生产环境中得到实践。

Back to Blog

相关文章

阅读更多 »