Jenkins脚本如何实现滚动更新策略

wen 实用脚本 4

本文目录导读:

Jenkins脚本如何实现滚动更新策略

  1. Kubernetes 滚动更新(最推荐)
  2. Docker Swarm 滚动更新
  3. 自定义滚动更新脚本(负载均衡器模式)
  4. 完整的Jenkins Pipeline高级示例
  5. 关键配置说明

在Jenkins中实现滚动更新策略,通常需要结合KubernetesDocker Swarm自定义脚本来完成,以下是几种常见场景的实现方式:

Kubernetes 滚动更新(最推荐)

如果你的应用部署在Kubernetes上,Jenkins可以通过kubectl命令直接触发滚动更新。

Pipeline示例:

pipeline {
    agent any
    environment {
        K8S_NAMESPACE = 'production'
        DEPLOYMENT_NAME = 'my-app'
        IMAGE_TAG = "${BUILD_NUMBER}"
        REGISTRY = 'myregistry.com'
    }
    stages {
        stage('构建镜像') {
            steps {
                sh "docker build -t ${REGISTRY}/${DEPLOYMENT_NAME}:${IMAGE_TAG} ."
                sh "docker push ${REGISTRY}/${DEPLOYMENT_NAME}:${IMAGE_TAG}"
            }
        }
        stage('滚动更新部署') {
            steps {
                sh """
                kubectl set image deployment/${DEPLOYMENT_NAME} \
                    ${DEPLOYMENT_NAME}=${REGISTRY}/${DEPLOYMENT_NAME}:${IMAGE_TAG} \
                    --namespace=${K8S_NAMESPACE} \
                    --record
                """
                // 监控滚动更新状态
                sh "kubectl rollout status deployment/${DEPLOYMENT_NAME} --namespace=${K8S_NAMESPACE}"
            }
        }
        stage('验证部署') {
            steps {
                sh "kubectl rollout history deployment/${DEPLOYMENT_NAME} --namespace=${K8S_NAMESPACE}"
            }
        }
    }
}

配置Kubernetes滚动更新策略:

# deployment.yaml
spec:
  replicas: 3
  strategy:
    type: RollingUpdate
    rollingUpdate:
      maxSurge: 1        # 可超出副本数的最大实例数
      maxUnavailable: 0  # 最大不可用实例数(0表示无中断更新)
  template:
    spec:
      containers:
      - name: my-app
        image: myregistry.com/my-app:latest

Docker Swarm 滚动更新

如果使用Docker Swarm管理容器,Jenkins可以执行服务更新命令。

Pipeline示例:

pipeline {
    agent any
    environment {
        STACK_NAME = 'my-stack'
        SERVICE_NAME = 'web'
        IMAGE_TAG = "${BUILD_NUMBER}"
        DOCKER_REGISTRY = 'myregistry.com'
    }
    stages {
        stage('构建镜像') {
            steps {
                sh "docker build -t ${DOCKER_REGISTRY}/${SERVICE_NAME}:${IMAGE_TAG} ."
                sh "docker push ${DOCKER_REGISTRY}/${SERVICE_NAME}:${IMAGE_TAG}"
            }
        }
        stage('滚动更新服务') {
            steps {
                sh """
                docker service update \
                    --image ${DOCKER_REGISTRY}/${SERVICE_NAME}:${IMAGE_TAG} \
                    --update-parallelism 2 \
                    --update-delay 10s \
                    --update-order start-first \
                    ${STACK_NAME}_${SERVICE_NAME}
                """
                // 等待更新完成
                sh "docker service ps ${STACK_NAME}_${SERVICE_NAME}"
            }
        }
    }
}

自定义滚动更新脚本(负载均衡器模式)

如果使用Nginx、HAProxy等负载均衡器,可以实现自定义的滚动更新逻辑。

自定义Shell脚本方式:

pipeline {
    agent any
    stages {
        stage('滚动更新') {
            steps {
                script {
                    def servers = ['server1', 'server2', 'server3']
                    def upstream = 'backend_servers'
                    servers.each { server ->
                        // 1. 从负载均衡器中移除节点
                        sh "ansible-playbook remove-from-upstream.yml -e server=${server}"
                        // 2. 等待连接耗尽
                        sleep(time: 10, unit: 'SECONDS')
                        // 3. 更新节点上的应用
                        sh "ansible-playbook deploy-app.yml -e server=${server} -e version=${BUILD_NUMBER}"
                        // 4. 健康检查
                        sh "curl -f http://${server}:8080/health"
                        // 5. 重新加入负载均衡器
                        sh "ansible-playbook add-to-upstream.yml -e server=${server}"
                        // 6. 等待负载均衡器收敛
                        sleep(time: 5, unit: 'SECONDS')
                    }
                }
            }
        }
    }
}

完整的Jenkins Pipeline高级示例

结合多个组件和错误处理的完整示例:

pipeline {
    agent any
    parameters {
        string(name: 'ROLLBACK_ON_FAILURE', defaultValue: 'true', 
               description: '失败时自动回滚')
        choice(name: 'UPDATE_PARALLELISM', 
               choices: ['1', '2', '3', '4'], 
               description: '并行更新实例数')
    }
    environment {
        KUBE_CONFIG = credentials('kubernetes-cluster-config')
        DOCKER_REGISTRY = 'myregistry.com'
        PROJECT_NAME = 'my-app'
        NAMESPACE = 'production'
    }
    stages {
        stage('安全检查') {
            steps {
                script {
                    // 检查部署是否存在
                    def deploymentExists = sh(
                        script: "kubectl get deployment ${PROJECT_NAME} -n ${NAMESPACE} --no-headers",
                        returnStatus: true
                    ) == 0
                    if (!deploymentExists) {
                        error "部署 ${PROJECT_NAME} 在namespace ${NAMESPACE}中不存在"
                    }
                }
            }
        }
        stage('构建与测试') {
            parallel {
                stage('单元测试') {
                    steps {
                        sh 'npm test'
                    }
                }
                stage('构建镜像') {
                    steps {
                        sh """
                        docker build -t ${DOCKER_REGISTRY}/${PROJECT_NAME}:${BUILD_NUMBER} .
                        docker push ${DOCKER_REGISTRY}/${PROJECT_NAME}:${BUILD_NUMBER}
                        """
                    }
                }
            }
        }
        stage('滚动更新') {
            steps {
                script {
                    try {
                        // 设置滚动更新参数
                        sh """
                        kubectl set image deployment/${PROJECT_NAME} \
                            ${PROJECT_NAME}=${DOCKER_REGISTRY}/${PROJECT_NAME}:${BUILD_NUMBER} \
                            -n ${NAMESPACE} \
                            --record
                        """
                        // 监控滚动更新(带超时)
                        timeout(time: 30, unit: 'MINUTES') {
                            sh "kubectl rollout status deployment/${PROJECT_NAME} -n ${NAMESPACE}"
                        }
                        // 验证新版本
                        def newReplicas = sh(
                            script: "kubectl get deployment ${PROJECT_NAME} -n ${NAMESPACE} -o jsonpath='{.spec.replicas}'",
                            returnStdout: true
                        ).trim()
                        if (!newReplicas || newReplicas.toInteger() < 1) {
                            error "部署后副本数为0,滚动更新可能失败"
                        }
                    } catch (Exception e) {
                        currentBuild.result = 'FAILURE'
                        if (params.ROLLBACK_ON_FAILURE == 'true') {
                            echo "滚动更新失败,执行回滚..."
                            // 回滚到上一个版本
                            sh "kubectl rollout undo deployment/${PROJECT_NAME} -n ${NAMESPACE}"
                            // 等待回滚完成
                            sh "kubectl rollout status deployment/${PROJECT_NAME} -n ${NAMESPACE}"
                            // 发送告警
                            emailext(
                                to: 'team@company.com',
                                subject: "🚨 滚动更新失败,已自动回滚 - ${PROJECT_NAME} #${BUILD_NUMBER}",
                                body: "错误: ${e.message}\nJob URL: ${env.BUILD_URL}"
                            )
                        }
                        throw e
                    }
                }
            }
        }
        stage('集成测试') {
            steps {
                sh """
                # 执行自动化集成测试
                ./run-integration-tests.sh ${BUILD_NUMBER}
                """
            }
        }
        stage('性能验证') {
            steps {
                script {
                    // 检查新Pod的CPU/内存使用情况
                    def cpuUsage = sh(
                        script: "kubectl top pods -n ${NAMESPACE} -l app=${PROJECT_NAME} --no-headers | awk '{print \$3}'",
                        returnStdout: true
                    ).trim()
                    echo "新Pod CPU使用率: ${cpuUsage}"
                }
            }
        }
    }
    post {
        success {
            echo "滚动更新成功完成!"
            // 清理旧镜像(保留最近5个版本)
            sh """
            docker images ${DOCKER_REGISTRY}/${PROJECT_NAME} \
                --format '{{.ID}} {{.Tag}}' | \
                sort -k2 -rn | \
                awk 'NR>5 {print \$1}' | \
                xargs -r docker rmi
            """
        }
        failure {
            echo "滚动更新失败,请检查日志"
        }
        always {
            // 清理工作空间
            cleanWs()
        }
    }
}

关键配置说明

Kubernetes滚动更新参数:

  • maxSurge: 滚动更新期间可以超出预期副本数的最大实例数(百分比或绝对数)
  • maxUnavailable: 滚动更新期间最大不可用实例数(百分比或绝对数)
  • minReadySeconds: 新Pod被视为可用前的最小运行时间

最佳实践:

  1. 使用Blue/Green或Canary策略:对于敏感应用,考虑先部署少量新版本验证
  2. 设置合理的超时时间:避免滚动更新无限等待
  3. 自动回滚机制:失败时自动回滚到上一个稳定版本
  4. 健康检查:确保应用有正确的readiness和liveness探针
  5. 渐进式更新:通过parallelismdelay控制更新速度

选择哪种实现方式主要取决于你的基础设施,Kubernetes是当前最主流的容器编排平台,强烈推荐使用它的内置滚动更新机制。

抱歉,评论功能暂时关闭!