Java面试题51:一文深入了解K8s 环境下企业级 CI/CD 流水线搭建

一、概述与技术栈选型

1.1 什么是 CI/CD

  • CI (持续集成):开发人员频繁将代码合并到主干,自动进行构建、测试和代码质量检查
  • CD (持续交付 / 部署):将经过验证的代码自动部署到测试、预生产和生产环境

1.2 企业级 CI/CD 流水线架构

我们采用 "CI+CD 分离"的现代 GitOps 架构:

  • CI 层:Jenkins 负责代码构建、测试、镜像打包和推送
  • CD 层:ArgoCD 负责基于 Git 仓库的声明式部署和状态同步
  • 制品层:Harbor 作为企业级镜像仓库,Nexus 作为 Maven/NPM 仓库
  • 质量层:SonarQube 进行代码质量检测和安全扫描

1.3 技术栈对比与选型

工具 市场份额 (2025) 优势 劣势 适用场景
Jenkins 48% 插件生态丰富 (2000+)、高度定制化、性能优异 配置复杂、维护成本高 大型企业、复杂流水线需求
GitLab CI 28% 与 GitLab 深度集成、开箱即用、YAML 配置简单 性能较差、定制化有限 中小型团队、GitLab 生态用户
GitHub Actions 42% 开发者体验好、与 GitHub 集成紧密 私有部署成本高 开源项目、GitHub 生态用户
ArgoCD 75%+ GitOps 标准、声明式部署、自动自愈、多集群支持 仅负责 CD 部分 所有 K8s 环境的应用部署

本教程选择:GitLab + Jenkins + Harbor + ArgoCD + SonarQube + Nexus,这是目前企业级最成熟、最灵活的组合。

二、环境准备

2.1 基础环境要求

  • Kubernetes 集群:v1.24+,3 个以上节点,每个节点至少 4C8G
  • Helm:v3.8+
  • kubectl:与集群版本匹配
  • 存储类:支持动态 PV 供应 (如 Longhorn、Ceph 或云存储)
  • 域名:至少 3 个域名 (jenkins.example.comharbor.example.comargocd.example.com)
  • 证书:SSL 证书 (可使用 Let's Encrypt)

2.2 命名空间规划

bash 复制代码
# 创建统一的devops命名空间
kubectl create namespace devops

# 创建应用环境命名空间
kubectl create namespace dev
kubectl create namespace staging
kubectl create namespace production

三、核心组件部署

3.1 部署 Harbor 企业级镜像仓库

Harbor 是 VMware 开源的企业级 Docker 镜像仓库,支持镜像签名、漏洞扫描、权限管控等功能。

javascript 复制代码
# 添加Harbor Helm仓库
helm repo add harbor https://helm.goharbor.io
helm repo update

# 创建Harbor values.yaml文件
cat > harbor-values.yaml << EOF
expose:
  type: ingress
  tls:
    enabled: true
    secretName: harbor-tls
  ingress:
    hosts:
      core: harbor.example.com
    annotations:
      kubernetes.io/ingress.class: nginx
      cert-manager.io/cluster-issuer: letsencrypt-prod

persistence:
  persistentVolumeClaim:
    registry:
      storageClass: longhorn
      size: 100Gi
    database:
      storageClass: longhorn
      size: 10Gi
    redis:
      storageClass: longhorn
      size: 5Gi

# 启用Trivy漏洞扫描
trivy:
  enabled: true
  skipUpdate: false

# 启用OIDC认证(可选,与GitLab集成)
oidc:
  enabled: false

# 管理员密码
harborAdminPassword: "Harbor12345"
EOF

# 安装Harbor
helm install harbor harbor/harbor -f harbor-values.yaml -n devops

验证部署:

bash 复制代码
kubectl get pods -n devops | grep harbor
# 访问 https://harbor.example.com,使用admin/Harbor12345登录

3.2 部署 Jenkins (支持 K8s 动态代理)

Jenkins 是最流行的 CI 工具,我们将部署支持 K8s 动态代理的 Jenkins 集群,实现构建资源的按需分配。

javascript 复制代码
# 添加Jenkins Helm仓库
helm repo add jenkins https://charts.jenkins.io
helm repo update

# 创建Jenkins values.yaml文件
cat > jenkins-values.yaml << EOF
controller:
  image:
    repository: jenkins/jenkins
    tag: "2.440.3-lts"
  ingress:
    enabled: true
    hostName: jenkins.example.com
    annotations:
      kubernetes.io/ingress.class: nginx
      cert-manager.io/cluster-issuer: letsencrypt-prod
    tls:
      - secretName: jenkins-tls
        hosts:
          - jenkins.example.com
  
  # 资源配置
  resources:
    requests:
      cpu: "2"
      memory: "4Gi"
    limits:
      cpu: "4"
      memory: "8Gi"
  
  # 持久化存储
  persistence:
    enabled: true
    storageClass: longhorn
    size: 20Gi
  
  # 安装必要插件
  installPlugins:
    - kubernetes:4229.v0a_55a_3d269b_
    - git:5.2.1
    - pipeline:596.v8c21c963d92d
    - workflow-aggregator:596.v8c21c963d92d
    - blueocean:1.27.11
    - sonar:2.15
    - docker-plugin:1.5
    - docker-workflow:563.vd5d2e5c4007f
    - credentials-binding:636.v55f1275c755d
    - configuration-as-code:1775.v810dc2758832
    - role-strategy:685.v2e40f0a_46b_31

  # Jenkins配置即代码(JCasC)
  JCasC:
    defaultConfig: true
    configScripts:
      kubernetes-cloud: |
        jenkins:
          clouds:
            - kubernetes:
                name: "kubernetes"
                serverUrl: "https://kubernetes.default"
                namespace: "devops"
                jenkinsUrl: "http://jenkins.devops.svc.cluster.local:8080"
                jenkinsTunnel: "jenkins-agent.devops.svc.cluster.local:50000"
                maxRequestsPerHost: 32
                podRetention: "Never"
                templates:
                  - name: "maven-agent"
                    label: "maven"
                    containers:
                      - name: "jnlp"
                        image: "jenkins/inbound-agent:3206.vb_15dcf73f6a_9-3"
                        args: "^${computer.jnlpmac} ^${computer.name}"
                        resourceRequestCpu: "500m"
                        resourceRequestMemory: "512Mi"
                        resourceLimitCpu: "1"
                        resourceLimitMemory: "1Gi"
                      - name: "maven"
                        image: "maven:3.8.6-eclipse-temurin-17"
                        command: "sleep"
                        args: "99d"
                        resourceRequestCpu: "2"
                        resourceRequestMemory: "4Gi"
                        resourceLimitCpu: "4"
                        resourceLimitMemory: "8Gi"
                        volumeMounts:
                          - mountPath: "/root/.m2"
                            name: "maven-cache"
                  - name: "docker-agent"
                    label: "docker"
                    containers:
                      - name: "jnlp"
                        image: "jenkins/inbound-agent:3206.vb_15dcf73f6a_9-3"
                        args: "^${computer.jnlpmac} ^${computer.name}"
                        resourceRequestCpu: "500m"
                        resourceRequestMemory: "512Mi"
                        resourceLimitCpu: "1"
                        resourceLimitMemory: "1Gi"
                      - name: "docker"
                        image: "docker:24.0.9-dind"
                        privileged: true
                        command: "dockerd"
                        args: "--host=tcp://0.0.0.0:2375 --tls=false"
                        resourceRequestCpu: "2"
                        resourceRequestMemory: "4Gi"
                        resourceLimitCpu: "4"
                        resourceLimitMemory: "8Gi"
                    volumes:
                      - hostPathVolume:
                          hostPath: "/var/run/docker.sock"
                          mountPath: "/var/run/docker.sock"
                volumes:
                  - persistentVolumeClaim:
                      claimName: "jenkins-maven-cache"
                      mountPath: "/root/.m2"
                      name: "maven-cache"

# 创建Maven缓存PVC
persistentVolumeClaims:
  - name: jenkins-maven-cache
    storageClassName: longhorn
    accessModes:
      - ReadWriteOnce
    resources:
      requests:
        storage: 20Gi

agent:
  enabled: true
  resources:
    requests:
      cpu: "500m"
      memory: "512Mi"
    limits:
      cpu: "1"
      memory: "1Gi"
EOF

# 安装Jenkins
helm install jenkins jenkins/jenkins -f jenkins-values.yaml -n devops

获取 Jenkins 管理员密码:

bash 复制代码
kubectl exec -n devops -it svc/jenkins -c jenkins -- cat /var/jenkins_home/secrets/initialAdminPassword

验证部署:

bash 复制代码
kubectl get pods -n devops | grep jenkins
# 访问 https://jenkins.example.com,使用admin和上面获取的密码登录

3.3 部署 ArgoCD (GitOps 持续交付)

ArgoCD 是 GitOps 的事实标准,它监听 Git 仓库中的应用定义,并自动将集群状态同步到期望状态。

javascript 复制代码
# 创建ArgoCD命名空间
kubectl create namespace argocd

# 安装ArgoCD
kubectl apply -n argocd -f https://raw.githubusercontent.com/argoproj/argo-cd/v2.10.7/manifests/install.yaml

# 创建ArgoCD Ingress
cat > argocd-ingress.yaml << EOF
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: argocd-server-ingress
  namespace: argocd
  annotations:
    kubernetes.io/ingress.class: nginx
    cert-manager.io/cluster-issuer: letsencrypt-prod
    nginx.ingress.kubernetes.io/force-ssl-redirect: "true"
    nginx.ingress.kubernetes.io/ssl-passthrough: "true"
    nginx.ingress.kubernetes.io/backend-protocol: "HTTPS"
spec:
  tls:
    - hosts:
        - argocd.example.com
      secretName: argocd-tls
  rules:
    - host: argocd.example.com
      http:
        paths:
          - path: /
            pathType: Prefix
            backend:
              service:
                name: argocd-server
                port:
                  name: https
EOF

kubectl apply -f argocd-ingress.yaml

获取 ArgoCD 管理员密码:

bash 复制代码
kubectl -n argocd get secret argocd-initial-admin-secret -o jsonpath="{.data.password}" | base64 -d

验证部署:

bash 复制代码
kubectl get pods -n argocd
# 访问 https://argocd.example.com,使用admin和上面获取的密码登录

3.4 部署 SonarQube 代码质量检测

javascript 复制代码
# 添加SonarQube Helm仓库
helm repo add sonarqube https://SonarSource.github.io/helm-chart-sonarqube
helm repo update

# 创建SonarQube values.yaml文件
cat > sonarqube-values.yaml << EOF
ingress:
  enabled: true
  hosts:
    - name: sonar.example.com
  annotations:
    kubernetes.io/ingress.class: nginx
    cert-manager.io/cluster-issuer: letsencrypt-prod
  tls:
    - secretName: sonar-tls
      hosts:
        - sonar.example.com

postgresql:
  enabled: true
  persistence:
    storageClass: longhorn
    size: 10Gi

persistence:
  storageClass: longhorn
  size: 20Gi

resources:
  requests:
    cpu: "2"
    memory: "4Gi"
  limits:
    cpu: "4"
    memory: "8Gi"
EOF

# 安装SonarQube
helm install sonarqube sonarqube/sonarqube -f sonarqube-values.yaml -n devops

验证部署:

bash 复制代码
kubectl get pods -n devops | grep sonarqube
# 访问 https://sonar.example.com,使用admin/admin登录

四、组件集成配置

4.1 Jenkins 与 Harbor 集成

  1. 在 Harbor 中创建项目:

    • 登录 Harbor 控制台
    • 创建项目:myproject(设置为公开或私有)
    • 创建机器人账户:jenkins-robot,赋予推送权限
    • 保存机器人账户的用户名和密码
  2. 在 Jenkins 中添加 Harbor 凭证:

    • 登录 Jenkins 控制台
    • 进入 "Manage Jenkins" → "Credentials" → "System" → "Global credentials"
    • 添加 "Username with password" 类型凭证:
      • ID: harbor-credentials
      • Username: 机器人账户用户名
      • Password: 机器人账户密码

4.2 Jenkins 与 SonarQube 集成

  1. 在 SonarQube 中生成令牌:

    • 登录 SonarQube 控制台
    • 进入 "My Account" → "Security" → "Generate Tokens"
    • 生成令牌:jenkins-token,保存令牌值
  2. 在 Jenkins 中添加 SonarQube 凭证:

    • 添加 "Secret text" 类型凭证:
      • ID: sonar-token
      • Secret: 上面生成的令牌值
  3. 配置 SonarQube 服务器:

    • 进入 "Manage Jenkins" → "System" → "SonarQube servers"
    • 添加 SonarQube 服务器:
      • Name: SonarQube
      • Server URL: https://sonar.example.com
      • Server authentication token: 选择上面添加的sonar-token凭证

4.3 Jenkins 与 GitLab 集成

  1. 在 GitLab 中生成个人访问令牌:

    • 登录 GitLab 控制台
    • 进入 "User Settings" → "Access Tokens"
    • 生成令牌,勾选apiread_repositorywrite_repository权限
    • 保存令牌值
  2. 在 Jenkins 中添加 GitLab 凭证:

    • 添加 "Username with password" 类型凭证:
      • ID: gitlab-credentials
      • Username: GitLab 用户名
      • Password: 上面生成的令牌值
  3. 配置 GitLab Webhook:

    • 在 GitLab 项目中进入 "Settings" → "Webhooks"
    • 添加 Webhook URL: https://jenkins.example.com/project/my-pipeline
    • 勾选 "Push events" 和 "Merge request events"
    • 点击 "Add webhook"

五、完整 CI/CD 流水线实现

5.1 项目结构设计

采用 "应用代码与部署配置分离" 的最佳实践:

bash 复制代码
# 应用代码仓库
myapp/
├── src/                    # 应用源代码
├── pom.xml                 # Maven配置文件
├── Dockerfile              # Docker镜像构建文件
└── Jenkinsfile             # CI流水线定义

# 部署配置仓库(GitOps)
myapp-manifests/
├── base/                   # 基础配置(所有环境共享)
│   ├── deployment.yaml
│   ├── service.yaml
│   ├── hpa.yaml
│   └── kustomization.yaml
└── overlays/               # 环境覆盖配置
    ├── dev/
    │   ├── kustomization.yaml
    │   └── configmap.yaml
    ├── staging/
    │   ├── kustomization.yaml
    │   └── configmap.yaml
    └── production/
        ├── kustomization.yaml
        └── configmap.yaml

5.2 CI 流水线 (Jenkinsfile)

这是一个完整的 Java 应用 CI 流水线,包含代码拉取、编译、单元测试、代码质量检测、镜像构建和推送、更新 GitOps 仓库等阶段。

javascript 复制代码
pipeline {
    agent {
        kubernetes {
            label 'maven && docker'
            yaml """
apiVersion: v1
kind: Pod
spec:
  containers:
  - name: maven
    image: maven:3.8.6-eclipse-temurin-17
    command: ['sleep']
    args: ['99d']
    volumeMounts:
    - mountPath: /root/.m2
      name: maven-cache
  - name: docker
    image: docker:24.0.9-dind
    command: ['dockerd']
    args: ['--host=tcp://0.0.0.0:2375', '--tls=false']
    privileged: true
    volumeMounts:
    - mountPath: /var/run/docker.sock
      name: docker-sock
  volumes:
  - name: maven-cache
    persistentVolumeClaim:
      claimName: jenkins-maven-cache
  - name: docker-sock
    hostPath:
      path: /var/run/docker.sock
"""
        }
    }
    
    environment {
        // 项目配置
        PROJECT_NAME = 'myapp'
        HARBOR_REGISTRY = 'harbor.example.com'
        HARBOR_PROJECT = 'myproject'
        GITOPS_REPO = 'git@gitlab.example.com:myteam/myapp-manifests.git'
        
        // 版本号(使用Git提交哈希)
        IMAGE_TAG = "${env.GIT_COMMIT.substring(0, 8)}"
        FULL_IMAGE_NAME = "${HARBOR_REGISTRY}/${HARBOR_PROJECT}/${PROJECT_NAME}:${IMAGE_TAG}"
        
        // 凭证
        HARBOR_CREDENTIALS = credentials('harbor-credentials')
        GITLAB_CREDENTIALS = credentials('gitlab-credentials')
    }
    
    stages {
        stage('代码拉取') {
            steps {
                checkout scm
                script {
                    // 获取当前分支
                    env.BRANCH_NAME = env.GIT_BRANCH.replace('origin/', '')
                    echo "当前分支: ${env.BRANCH_NAME}"
                }
            }
        }
        
        stage('编译与单元测试') {
            steps {
                container('maven') {
                    sh 'mvn clean package -DskipTests=false'
                }
            }
            post {
                always {
                    junit '**/target/surefire-reports/*.xml'
                }
            }
        }
        
        stage('代码质量检测') {
            steps {
                container('maven') {
                    withSonarQubeEnv('SonarQube') {
                        sh 'mvn sonar:sonar'
                    }
                }
            }
        }
        
        stage('质量门禁检查') {
            steps {
                timeout(time: 1, unit: 'HOURS') {
                    waitForQualityGate abortPipeline: true
                }
            }
        }
        
        stage('构建Docker镜像') {
            steps {
                container('docker') {
                    sh """
                        docker build -t ${FULL_IMAGE_NAME} .
                    """
                }
            }
        }
        
        stage('推送镜像到Harbor') {
            steps {
                container('docker') {
                    sh """
                        docker login ${HARBOR_REGISTRY} -u ${HARBOR_CREDENTIALS_USR} -p ${HARBOR_CREDENTIALS_PSW}
                        docker push ${FULL_IMAGE_NAME}
                        docker logout ${HARBOR_REGISTRY}
                    """
                }
            }
        }
        
        stage('更新GitOps仓库') {
            steps {
                container('maven') {
                    // 安装Git
                    sh 'apt-get update && apt-get install -y git'
                    
                    // 配置Git
                    sh """
                        git config --global user.name "Jenkins CI"
                        git config --global user.email "jenkins@example.com"
                    """
                    
                    // 克隆GitOps仓库
                    sh "git clone ${GITOPS_REPO}"
                    
                    // 根据分支更新对应环境
                    script {
                        def envDir = ''
                        if (env.BRANCH_NAME == 'develop') {
                            envDir = 'dev'
                        } else if (env.BRANCH_NAME == 'staging') {
                            envDir = 'staging'
                        } else if (env.BRANCH_NAME == 'main') {
                            envDir = 'production'
                        }
                        
                        if (envDir) {
                            // 更新镜像标签
                            sh """
                                cd myapp-manifests/overlays/${envDir}
                                sed -i "s|image:.*|image: ${FULL_IMAGE_NAME}|g" kustomization.yaml
                                git add kustomization.yaml
                                git commit -m "Update ${envDir} image to ${IMAGE_TAG}"
                                git push origin main
                            """
                        }
                    }
                }
            }
        }
    }
    
    post {
        success {
            echo "流水线执行成功!镜像版本: ${IMAGE_TAG}"
        }
        failure {
            echo "流水线执行失败!"
        }
    }
}

5.3 CD 流水线 (ArgoCD 应用配置)

创建 ArgoCD 应用配置文件,实现多环境自动部署:

javascript 复制代码
# argocd-applications.yaml
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
  name: myapp-dev
  namespace: argocd
spec:
  project: default
  source:
    repoURL: git@gitlab.example.com:myteam/myapp-manifests.git
    targetRevision: main
    path: overlays/dev
  destination:
    server: https://kubernetes.default.svc
    namespace: dev
  syncPolicy:
    automated:
      prune: true
      selfHeal: true
    syncOptions:
      - CreateNamespace=true

---
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
  name: myapp-staging
  namespace: argocd
spec:
  project: default
  source:
    repoURL: git@gitlab.example.com:myteam/myapp-manifests.git
    targetRevision: main
    path: overlays/staging
  destination:
    server: https://kubernetes.default.svc
    namespace: staging
  syncPolicy:
    automated:
      prune: true
      selfHeal: true
    syncOptions:
      - CreateNamespace=true

---
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
  name: myapp-production
  namespace: argocd
spec:
  project: default
  source:
    repoURL: git@gitlab.example.com:myteam/myapp-manifests.git
    targetRevision: main
    path: overlays/production
  destination:
    server: https://kubernetes.default.svc
    namespace: production
  syncPolicy:
    # 生产环境禁用自动同步,需要手动审批
    syncOptions:
      - CreateNamespace=true

应用配置:

bash 复制代码
kubectl apply -f argocd-applications.yaml

5.4 Kustomize 配置示例

base/kustomization.yaml:

javascript 复制代码
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
  - deployment.yaml
  - service.yaml
  - hpa.yaml

base/deployment.yaml:

javascript 复制代码
apiVersion: apps/v1
kind: Deployment
metadata:
  name: myapp
spec:
  replicas: 1
  selector:
    matchLabels:
      app: myapp
  template:
    metadata:
      labels:
        app: myapp
    spec:
      containers:
      - name: myapp
        image: harbor.example.com/myproject/myapp:latest
        ports:
        - containerPort: 8080
        resources:
          requests:
            cpu: "500m"
            memory: "512Mi"
          limits:
            cpu: "1"
            memory: "1Gi"
        livenessProbe:
          httpGet:
            path: /actuator/health
            port: 8080
          initialDelaySeconds: 60
          periodSeconds: 10
        readinessProbe:
          httpGet:
            path: /actuator/health
            port: 8080
          initialDelaySeconds: 30
          periodSeconds: 5

overlays/dev/kustomization.yaml:

javascript 复制代码
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
bases:
  - ../../base
images:
  - name: harbor.example.com/myproject/myapp:latest
    newTag: "abc1234"  # 这个标签会被Jenkins自动更新
patches:
  - path: configmap.yaml

六、高级部署策略详解

在企业级应用发布过程中,直接替换所有 Pod 的滚动更新 (RollingUpdate) 虽然简单,但存在以下问题:

  • 无法在发布过程中进行流量控制
  • 新版本问题会直接影响所有用户
  • 回滚速度慢,需要重新拉取旧版本镜像
  • 无法进行 A/B 测试和灰度验证

Argo Rollouts 是 Argo 项目的子项目,专门为 Kubernetes 提供高级部署能力,支持蓝绿部署、金丝雀发布、渐进式交付等策略,与 ArgoCD 完美集成,是 GitOps 模式下的最佳部署解决方案。

6.1 Argo Rollouts 基础

6.1.1 什么是 Argo Rollouts

Argo Rollouts 是一个 Kubernetes 控制器,扩展了原生 Deployment 的功能,提供了更强大的部署策略。它通过自定义资源定义 (CRD) 来管理应用的发布过程,能够:

  • 逐步将流量切换到新版本
  • 自动分析应用指标判断发布是否成功
  • 在检测到问题时自动回滚
  • 支持手动审批和暂停
  • 与服务网格 (Istio、Linkerd) 和入口控制器 (Nginx、ALB) 集成
6.1.2 安装与配置
javascript 复制代码
# 安装Argo Rollouts控制器
kubectl create namespace argo-rollouts
kubectl apply -n argo-rollouts -f https://github.com/argoproj/argo-rollouts/releases/download/v1.6.6/install.yaml

# 安装kubectl argo rollouts插件(推荐)
curl -LO https://github.com/argoproj/argo-rollouts/releases/download/v1.6.6/kubectl-argo-rollouts-linux-amd64
chmod +x kubectl-argo-rollouts-linux-amd64
sudo mv kubectl-argo-rollouts-linux-amd64 /usr/local/bin/kubectl-argo-rollouts

# 验证安装
kubectl argo rollouts version
6.1.3 核心概念
  • Rollout:替代原生 Deployment 的自定义资源,定义应用的部署策略
  • Revision:每次发布生成的版本,对应一个 ReplicaSet
  • Canary:金丝雀版本,接收部分流量进行验证
  • Stable:稳定版本,接收大部分流量
  • AnalysisRun:用于自动分析发布质量的资源
  • Experiment:用于同时运行多个版本进行对比测试的资源

6.2 蓝绿部署 (Blue/Green Deployment) 详解

6.2.1 蓝绿部署原理

蓝绿部署是一种零停机部署策略,它同时维护两个完全相同的环境:

  • 蓝色环境:当前运行的稳定版本,接收所有生产流量
  • 绿色环境:即将发布的新版本,不接收任何流量

部署流程:

  1. 部署新版本到绿色环境
  2. 在绿色环境进行全面测试
  3. 测试通过后,将流量从蓝色环境切换到绿色环境
  4. 观察绿色环境运行情况
  5. 确认无误后,销毁蓝色环境
  6. 下一次发布时,角色互换

优势

  • 零停机时间
  • 发布过程风险低,可随时回滚
  • 支持完整的发布前测试
  • 避免版本共存问题

劣势

  • 需要双倍的资源
  • 数据库变更处理复杂
  • 不适合大型单体应用

适用场景

  • 资源充足的中小型应用
  • 对可用性要求极高的服务
  • 数据库变更较少的应用
  • 需要完整测试新版本的场景
6.2.2 完整蓝绿部署配置示例
javascript 复制代码
# rollout-bluegreen.yaml
apiVersion: argoproj.io/v1alpha1
kind: Rollout
metadata:
  name: myapp-bluegreen
  namespace: production
spec:
  replicas: 3
  selector:
    matchLabels:
      app: myapp
  template:
    metadata:
      labels:
        app: myapp
    spec:
      containers:
      - name: myapp
        image: harbor.example.com/myproject/myapp:v1.0.0
        ports:
        - containerPort: 8080
        resources:
          requests:
            cpu: "500m"
            memory: "512Mi"
          limits:
            cpu: "1"
            memory: "1Gi"
        livenessProbe:
          httpGet:
            path: /actuator/health
            port: 8080
          initialDelaySeconds: 60
          periodSeconds: 10
        readinessProbe:
          httpGet:
            path: /actuator/health
            port: 8080
          initialDelaySeconds: 30
          periodSeconds: 5

  # 蓝绿部署策略配置
  strategy:
    blueGreen:
      # 活跃服务(接收生产流量)
      activeService: myapp-active
      # 预览服务(仅用于内部测试)
      previewService: myapp-preview
      
      # 是否自动提升新版本为活跃版本
      # false表示需要手动审批
      autoPromotionEnabled: false
      
      # 提升新版本后,等待多久再销毁旧版本(秒)
      # 给旧版本足够时间处理正在进行的请求
      scaleDownDelaySeconds: 300
      
      # 旧版本保留的数量(用于快速回滚)
      revisionHistoryLimit: 5
      
      # 预热期:新版本就绪后等待多久再允许提升
      # 给应用足够时间完成初始化
      prePromotionAnalysis:
        templates:
        - templateName: success-rate
        args:
        - name: service-name
          value: myapp-preview
        interval: 30s
        count: 3
        failureLimit: 1

创建对应的 Service:

javascript 复制代码
# services.yaml
apiVersion: v1
kind: Service
metadata:
  name: myapp-active
  namespace: production
spec:
  selector:
    app: myapp
  ports:
  - port: 80
    targetPort: 8080
  type: ClusterIP

---
apiVersion: v1
kind: Service
metadata:
  name: myapp-preview
  namespace: production
spec:
  selector:
    app: myapp
  ports:
  - port: 80
    targetPort: 8080
  type: ClusterIP

应用配置:

javascript 复制代码
kubectl apply -f rollout-bluegreen.yaml
kubectl apply -f services.yaml

# 查看Rollout状态
kubectl argo rollouts get rollout myapp-bluegreen -n production
6.2.3 蓝绿部署完整流程演示

步骤 1:初始部署

bash 复制代码
# 部署v1.0.0版本
kubectl apply -f rollout-bluegreen.yaml

# 查看状态,此时所有流量都指向v1.0.0(蓝色环境)
kubectl argo rollouts get rollout myapp-bluegreen -n production

步骤 2:发布新版本 v1.1.0

bash 复制代码
# 更新镜像版本
kubectl argo rollouts set image myapp-bluegreen \
  myapp=harbor.example.com/myproject/myapp:v1.1.0 \
  -n production

# 查看状态,此时会创建绿色环境(v1.1.0)
# 但所有流量仍然指向蓝色环境(v1.0.0)
kubectl argo rollouts get rollout myapp-bluegreen -n production

步骤 3:测试预览版本

bash 复制代码
# 访问预览服务测试新版本
kubectl port-forward svc/myapp-preview 8080:80 -n production
curl http://localhost:8080/version
# 应该返回v1.1.0

# 访问活跃服务确认仍然是旧版本
kubectl port-forward svc/myapp-active 8081:80 -n production
curl http://localhost:8081/version
# 应该返回v1.0.0

步骤 4:手动提升新版本

bash 复制代码
# 测试通过后,提升新版本为活跃版本
kubectl argo rollouts promote myapp-bluegreen -n production

# 查看状态,此时流量已切换到绿色环境(v1.1.0)
kubectl argo rollouts get rollout myapp-bluegreen -n production

步骤 5:观察运行情况

bash 复制代码
# 持续观察新版本运行情况
kubectl argo rollouts get rollout myapp-bluegreen -n production --watch

# 300秒后,旧版本(蓝色环境)会自动销毁
6.2.4 蓝绿部署回滚操作

如果发现新版本有问题,可以随时回滚:

bash 复制代码
# 查看发布历史
kubectl argo rollouts history myapp-bluegreen -n production

# 回滚到上一个版本
kubectl argo rollouts undo myapp-bluegreen -n production

# 回滚到指定版本
kubectl argo rollouts undo myapp-bluegreen --to-revision=2 -n production

6.3 金丝雀部署 (Canary Deployment) 详解

6.3.1 金丝雀部署原理

金丝雀部署是一种渐进式发布策略,它将流量逐步从旧版本切换到新版本:

  1. 先部署一小部分新版本实例
  2. 将一小部分流量 (如 5%) 路由到新版本
  3. 观察新版本的运行情况和指标
  4. 如果一切正常,逐步增加新版本的流量比例
  5. 最终将 100% 流量切换到新版本
  6. 销毁旧版本实例

优势

  • 风险低,问题只影响少量用户
  • 资源消耗少,不需要双倍资源
  • 可以基于真实用户流量进行验证
  • 支持自动回滚

劣势

  • 发布周期长
  • 流量控制复杂
  • 需要处理版本共存问题
  • 数据库变更需要兼容

适用场景

  • 大型应用和微服务架构
  • 资源有限的环境
  • 对发布风险敏感的场景
  • 需要进行 A/B 测试的场景
6.3.2 金丝雀部署配置示例 (基础版)
javascript 复制代码
# rollout-canary-basic.yaml
apiVersion: argoproj.io/v1alpha1
kind: Rollout
metadata:
  name: myapp-canary
  namespace: production
spec:
  replicas: 10
  selector:
    matchLabels:
      app: myapp
  template:
    metadata:
      labels:
        app: myapp
    spec:
      containers:
      - name: myapp
        image: harbor.example.com/myproject/myapp:v1.0.0
        ports:
        - containerPort: 8080
        resources:
          requests:
            cpu: "500m"
            memory: "512Mi"
          limits:
            cpu: "1"
            memory: "1Gi"
        livenessProbe:
          httpGet:
            path: /actuator/health
            port: 8080
          initialDelaySeconds: 60
          periodSeconds: 10
        readinessProbe:
          httpGet:
            path: /actuator/health
            port: 8080
          initialDelaySeconds: 30
          periodSeconds: 5

  # 金丝雀部署策略配置
  strategy:
    canary:
      # 金丝雀服务(可选,用于直接访问金丝雀版本)
      canaryService: myapp-canary
      # 稳定服务
      stableService: myapp-stable
      
      # 流量切换步骤
      steps:
      - setWeight: 10    # 将10%流量切换到金丝雀版本
      - pause: {duration: 10m}  # 观察10分钟
      
      - setWeight: 30    # 将30%流量切换到金丝雀版本
      - pause: {duration: 10m}  # 观察10分钟
      
      - setWeight: 50    # 将50%流量切换到金丝雀版本
      - pause: {duration: 10m}  # 观察10分钟
      
      - setWeight: 70    # 将70%流量切换到金丝雀版本
      - pause: {duration: 10m}  # 观察10分钟
      
      - setWeight: 100   # 将100%流量切换到金丝雀版本
      
      # 最大不可用实例比例
      maxUnavailable: 1
      # 最大超额实例比例
      maxSurge: 1
      
      # 旧版本保留数量
      revisionHistoryLimit: 5
6.3.3 金丝雀部署配置示例 (高级版 - 带自动分析)

这是生产环境推荐的配置,集成了自动指标分析和自动回滚:

javascript 复制代码
# rollout-canary-advanced.yaml
apiVersion: argoproj.io/v1alpha1
kind: Rollout
metadata:
  name: myapp-canary
  namespace: production
spec:
  replicas: 10
  selector:
    matchLabels:
      app: myapp
  template:
    metadata:
      labels:
        app: myapp
    spec:
      containers:
      - name: myapp
        image: harbor.example.com/myproject/myapp:v1.0.0
        ports:
        - containerPort: 8080
        resources:
          requests:
            cpu: "500m"
            memory: "512Mi"
          limits:
            cpu: "1"
            memory: "1Gi"
        livenessProbe:
          httpGet:
            path: /actuator/health
            port: 8080
          initialDelaySeconds: 60
          periodSeconds: 10
        readinessProbe:
          httpGet:
            path: /actuator/health
            port: 8080
          initialDelaySeconds: 30
          periodSeconds: 5

  strategy:
    canary:
      canaryService: myapp-canary
      stableService: myapp-stable
      
      # 流量切换步骤
      steps:
      - setWeight: 5
      - pause: {}  # 无限暂停,需要手动审批
      
      - setWeight: 10
      - pause: {duration: 5m}
      - analysis:
          templates:
          - templateName: success-rate
          - templateName: error-rate
          - templateName: response-time
          args:
          - name: service-name
            value: myapp-canary
          interval: 30s
          count: 5
          failureLimit: 1
          inconclusiveLimit: 2
      
      - setWeight: 30
      - pause: {duration: 10m}
      - analysis:
          templates:
          - templateName: success-rate
          - templateName: error-rate
          args:
          - name: service-name
            value: myapp-canary
          interval: 30s
          count: 10
          failureLimit: 1
      
      - setWeight: 50
      - pause: {duration: 10m}
      - analysis:
          templates:
          - templateName: success-rate
          args:
          - name: service-name
            value: myapp-canary
          interval: 30s
          count: 10
          failureLimit: 1
      
      - setWeight: 100
      
      # 发布后分析
      postPromotionAnalysis:
        templates:
        - templateName: success-rate
        args:
        - name: service-name
          value: myapp-stable
        interval: 1m
        count: 10
        failureLimit: 1
      
      # 自动回滚配置
      abortScaleDownDelaySeconds: 30
6.3.5 金丝雀部署完整流程演示

步骤 1:初始部署

bash 复制代码
# 部署v1.0.0版本
kubectl apply -f rollout-canary-advanced.yaml
kubectl apply -f analysis-templates.yaml

# 查看状态
kubectl argo rollouts get rollout myapp-canary -n production

步骤 2:发布新版本 v1.1.0

bash 复制代码
# 更新镜像版本
kubectl argo rollouts set image myapp-canary \
  myapp=harbor.example.com/myproject/myapp:v1.1.0 \
  -n production

# 查看状态,此时会创建1个金丝雀实例(10%流量)
# 并进入无限暂停状态,等待手动审批
kubectl argo rollouts get rollout myapp-canary -n production

步骤 3:手动审批第一阶段

bash 复制代码
# 测试金丝雀版本
kubectl port-forward svc/myapp-canary 8080:80 -n production
curl http://localhost:8080/version
# 应该返回v1.1.0

# 审批通过,继续发布流程
kubectl argo rollouts promote myapp-canary -n production

步骤 4:自动执行后续阶段

bash 复制代码
# 观察发布过程
kubectl argo rollouts get rollout myapp-canary -n production --watch

# 系统会自动执行以下步骤:
# 1. 将流量增加到10%,观察5分钟
# 2. 运行第一次自动分析
# 3. 如果分析通过,将流量增加到30%,观察10分钟
# 4. 运行第二次自动分析
# 5. 如果分析通过,将流量增加到50%,观察10分钟
# 6. 运行第三次自动分析
# 7. 如果分析通过,将流量增加到100%
# 8. 运行发布后分析
6.3.6 金丝雀部署回滚操作

如果在发布过程中发现问题:

bash 复制代码
# 立即中止发布并回滚
kubectl argo rollouts abort myapp-canary -n production

# 查看回滚状态
kubectl argo rollouts get rollout myapp-canary -n production

# 如果已经完成发布,回滚到上一个版本
kubectl argo rollouts undo myapp-canary -n production

6.4 蓝绿部署 vs 金丝雀部署对比

对比维度 蓝绿部署 金丝雀部署
资源消耗 高 (需要双倍资源) 低 (只需要少量额外资源)
发布风险 低 (发布前完整测试) 中 (问题只影响少量用户)
发布速度 快 (一次性切换) 慢 (逐步切换)
回滚速度 极快 (瞬间切换) 快 (逐步回滚)
流量控制 粗粒度 (0% 或 100%) 细粒度 (0-100% 任意比例)
版本共存 短时间共存 长时间共存
数据库变更 复杂 (需要兼容两个版本) 更复杂 (需要长期兼容)
A/B 测试支持
适用规模 中小型应用 大型应用和微服务
学习曲线 简单 复杂

6.5 部署策略选择建议

选择蓝绿部署的情况

  • 应用规模较小,资源充足
  • 对可用性要求极高
  • 发布频率较低
  • 数据库变更较少
  • 需要在发布前进行完整测试

选择金丝雀部署的情况

  • 应用规模较大,采用微服务架构
  • 资源有限
  • 发布频率较高
  • 对发布风险非常敏感
  • 需要进行 A/B 测试或用户反馈收集

混合使用策略

  • 开发和测试环境使用滚动更新
  • 预生产环境使用蓝绿部署
  • 生产环境使用金丝雀部署

6.6 与 CI/CD 流水线集成

将高级部署策略与之前的 CI/CD 流水线集成:

6.6.1 更新 GitOps 仓库结构
javascript 复制代码
myapp-manifests/
├── base/
│   ├── rollout.yaml          # 替换原来的deployment.yaml
│   ├── service.yaml
│   ├── hpa.yaml
│   └── kustomization.yaml
└── overlays/
    ├── dev/
    │   ├── kustomization.yaml
    │   └── configmap.yaml
    ├── staging/
    │   ├── kustomization.yaml
    │   └── configmap.yaml
    └── production/
        ├── kustomization.yaml
        ├── configmap.yaml
        └── analysis-templates.yaml  # 生产环境专用分析模板

创建 Rollout 配置(蓝绿部署):

javascript 复制代码
apiVersion: argoproj.io/v1alpha1
kind: Rollout
metadata:
  name: myapp
spec:
  replicas: 3
  selector:
    matchLabels:
      app: myapp
  template:
    metadata:
      labels:
        app: myapp
    spec:
      containers:
      - name: myapp
        image: harbor.example.com/myproject/myapp:latest
        ports:
        - containerPort: 8080
  strategy:
    blueGreen:
      activeService: myapp-active
      previewService: myapp-preview
      autoPromotionEnabled: false  # 手动审批后才切换流量
      scaleDownDelaySeconds: 30    # 切换后30秒再销毁旧版本

创建 Rollout 配置(金丝雀发布):

javascript 复制代码
apiVersion: argoproj.io/v1alpha1
kind: Rollout
metadata:
  name: myapp
spec:
  replicas: 10
  selector:
    matchLabels:
      app: myapp
  template:
    metadata:
      labels:
        app: myapp
    spec:
      containers:
      - name: myapp
        image: harbor.example.com/myproject/myapp:latest
        ports:
        - containerPort: 8080
  strategy:
    canary:
      steps:
      - setWeight: 10    # 先将10%流量切换到新版本
      - pause: {duration: 10m}  # 观察10分钟
      - setWeight: 30    # 再将30%流量切换到新版本
      - pause: {duration: 10m}  # 观察10分钟
      - setWeight: 50    # 再将50%流量切换到新版本
      - pause: {duration: 10m}  # 观察10分钟
      - setWeight: 100   # 最后全量切换
6.6.2 更新 Jenkinsfile

在 CI 流水线中添加自动审批和通知功能:

javascript 复制代码
stage('触发部署') {
    steps {
        script {
            if (env.BRANCH_NAME == 'main') {
                // 生产环境:触发金丝雀发布并等待第一阶段审批
                sh """
                    kubectl argo rollouts set image myapp-canary \
                      myapp=${FULL_IMAGE_NAME} \
                      -n production
                    
                    # 等待发布进入暂停状态
                    kubectl argo rollouts wait myapp-canary \
                      -n production --for=paused
                    
                    # 发送审批通知
                    echo "金丝雀发布已启动,请访问ArgoCD控制台进行审批"
                    echo "ArgoCD地址: https://argocd.example.com/applications/myapp-production"
                """
            } else if (env.BRANCH_NAME == 'staging') {
                // 预生产环境:自动完成蓝绿部署
                sh """
                    kubectl argo rollouts set image myapp-bluegreen \
                      myapp=${FULL_IMAGE_NAME} \
                      -n staging
                    
                    # 等待发布完成
                    kubectl argo rollouts wait myapp-bluegreen \
                      -n staging --for=completed
                    
                    # 自动提升新版本
                    kubectl argo rollouts promote myapp-bluegreen \
                      -n staging
                """
            } else {
                // 开发环境:直接更新
                sh """
                    kubectl rollout restart deployment myapp -n dev
                """
            }
        }
    }
}
6.6.3 更新 ArgoCD 应用配置
javascript 复制代码
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
  name: myapp-production
  namespace: argocd
spec:
  project: default
  source:
    repoURL: git@gitlab.example.com:myteam/myapp-manifests.git
    targetRevision: main
    path: overlays/production
  destination:
    server: https://kubernetes.default.svc
    namespace: production
  syncPolicy:
    # 生产环境禁用自动同步,由Jenkins触发
    syncOptions:
      - CreateNamespace=true
  ignoreDifferences:
  - group: argoproj.io
    kind: Rollout
    jsonPointers:
    - /spec/template/spec/containers/0/image
相关推荐
琪露诺大湿2 小时前
网页聊天系统——测试报告
java·软件测试·功能测试·websocket·html·项目·测试报告
小园子的小菜2 小时前
生产实战|冷热数据转换:从识别、触发到落地全流程解析
java·开发语言·spring
invicinble2 小时前
对于线程的思路
java
iwS2o90XT2 小时前
WebSocket编程:Java实现实时双向通信应用
java·websocket·网络协议
Highcharts.js2 小时前
技术组合分析:Highcharts 的数据集成能力解析
java·前端·金融·echarts·saas·bi·highcharts
shaoFan13 小时前
关于java 调用阿里千问大模型,流式返回,并返回给前端
java·前端·状态模式
雪碧聊技术3 小时前
Java历史—沙箱安全机制
java·安全·沙箱机制
java1234_小锋3 小时前
Spring AI 2.0 开发Java Agent智能体 - Spring AI 2.0简介
java·人工智能·spring·spring ai
Hesionberger3 小时前
LeetCode72.编辑距离(多维动态规划)
java·开发语言·c++·python·算法