本文目录导读:

在Jenkins中实现滚动更新策略,通常需要结合Kubernetes、Docker 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被视为可用前的最小运行时间
最佳实践:
- 使用Blue/Green或Canary策略:对于敏感应用,考虑先部署少量新版本验证
- 设置合理的超时时间:避免滚动更新无限等待
- 自动回滚机制:失败时自动回滚到上一个稳定版本
- 健康检查:确保应用有正确的readiness和liveness探针
- 渐进式更新:通过
parallelism和delay控制更新速度
选择哪种实现方式主要取决于你的基础设施,Kubernetes是当前最主流的容器编排平台,强烈推荐使用它的内置滚动更新机制。