Jenkins脚本如何部署应用到生产环境

wen 实用脚本 3

本文目录导读:

Jenkins脚本如何部署应用到生产环境

  1. 核心原则
  2. 不同部署方式的Jenkins脚本示例
  3. 生产环境部署的最佳实践
  4. 你可能需要注意的问题

在Jenkins中部署应用到生产环境的脚本编写,核心在于实现自动化、安全、可回滚的流程,以下为你介绍几种主流方式及其对应的Jenkins Pipeline脚本示例。

核心原则

  • 自动化:减少人工干预,从代码提交到部署全流程自动化。
  • 一致性:确保开发、测试、生产环境部署方式相同。
  • 零停机:使用蓝绿部署、滚动更新或金丝雀发布策略。
  • 回滚能力:具备快速回滚到上一个稳定版本的能力。
  • 安全:生产环境凭证(密码、密钥)通过Jenkins凭据管理,不硬编码。

不同部署方式的Jenkins脚本示例

SSH + 传统应用(如JAR包、Node.js应用)

适用于非容器化应用,通过SSH将构建产物传输到生产服务器,执行远程命令。

部署流程:构建 → 打包 → SCP传输 → SSH连接 → 停止旧服务 → 备份旧版本 → 启动新服务。

pipeline {
    agent any
    environment {
        // SSH凭据ID(在Jenkins凭据中配置)
        SSH_CREDENTIALS = credentials('ssh-credentials-id')
        PRODUCTION_HOST = 'your-production-server.com'
        APP_DIR = '/opt/myapp'
        BACKUP_DIR = '/opt/myapp/backups'
    }
    stages {
        stage('构建') {
            steps {
                echo '构建项目...'
                sh 'npm install && npm run build'
            }
        }
        stage('打包构建产物') {
            steps {
                echo '打包分发文件...'
                sh '''
                    tar -czf app.tar.gz dist/
                    # 保存当前构建号作为版本标识
                    echo "${BUILD_NUMBER}" > version.txt
                '''
            }
        }
        stage('部署到生产环境') {
            steps {
                sshagent(['ssh-credentials-id']) {
                    // 1. 传输构建产物
                    sh """
                        scp -o StrictHostKeyChecking=no app.tar.gz version.txt \
                            user@${PRODUCTION_HOST}:${APP_DIR}/
                    """
                    // 2. 远程执行部署脚本
                    sh """
                        ssh user@${PRODUCTION_HOST} "
                            set -e
                            # 备份旧版本
                            mkdir -p ${BACKUP_DIR}
                            cp -r ${APP_DIR}/current ${BACKUP_DIR}/backup_\$(date +%Y%m%d_%H%M%S) 2>/dev/null || true
                            # 解压新版本到临时目录
                            mkdir -p ${APP_DIR}/new_release
                            tar -xzf ${APP_DIR}/app.tar.gz -C ${APP_DIR}/new_release
                            # 停止旧应用
                            systemctl stop myapp || true
                            # 替换应用目录
                            rm -rf ${APP_DIR}/current
                            mv ${APP_DIR}/new_release ${APP_DIR}/current
                            # 启动新应用
                            systemctl start myapp
                            # 清理
                            rm ${APP_DIR}/app.tar.gz ${APP_DIR}/version.txt
                            echo '部署完成'
                        "
                    """
                }
            }
            post {
                failure {
                    echo '部署失败,尝试回滚...'
                    // 回滚逻辑
                    sshagent(['ssh-credentials-id']) {
                        sh """
                            ssh user@${PRODUCTION_HOST} "
                                systemctl stop myapp || true
                                rm -rf ${APP_DIR}/current
                                mv \$(ls -t ${BACKUP_DIR}/ | head -1) ${APP_DIR}/current
                                systemctl start myapp
                            "
                        """
                    }
                }
            }
        }
    }
}

Docker容器化应用(推荐)

适用于微服务架构和现代应用。

部署流程:构建镜像 → 推送到镜像仓库 → SSH连接生产服务器 → 拉取新镜像 → 重启容器。

pipeline {
    agent any
    environment {
        DOCKER_REGISTRY = 'your-registry.com'
        IMAGE_NAME = 'myapp'
        IMAGE_TAG = "${BUILD_NUMBER}-${env.GIT_COMMIT.take(7)}"
        PRODUCTION_HOST = 'your-production-server.com'
    }
    stages {
        stage('构建Docker镜像') {
            steps {
                script {
                    docker.build("${DOCKER_REGISTRY}/${IMAGE_NAME}:${IMAGE_TAG}")
                }
            }
        }
        stage('推送镜像到仓库') {
            steps {
                withDockerRegistry([credentialsId: 'docker-registry-credentials', url: "https://${DOCKER_REGISTRY}"]) {
                    script {
                        docker.image("${DOCKER_REGISTRY}/${IMAGE_NAME}:${IMAGE_TAG}").push()
                        // 同时标记为latest方便回滚
                        docker.image("${DOCKER_REGISTRY}/${IMAGE_NAME}:${IMAGE_TAG}").push('latest')
                    }
                }
            }
        }
        stage('部署到生产环境') {
            steps {
                sshagent(['ssh-credentials-id']) {
                    sh """
                        ssh user@${PRODUCTION_HOST} "
                            set -e
                            # 拉取新镜像
                            docker pull ${DOCKER_REGISTRY}/${IMAGE_NAME}:${IMAGE_TAG}
                            # 停止并删除旧容器(保留旧版本标签)
                            docker stop myapp || true
                            docker rm myapp || true
                            # 启动新容器
                            docker run -d \\
                                --name myapp \\
                                --restart always \\
                                -p 8080:8080 \\
                                -e ENV=production \\
                                ${DOCKER_REGISTRY}/${IMAGE_NAME}:${IMAGE_TAG}
                            # 清理旧镜像(保留最近5个版本)
                            docker image prune -a --filter 'until=24h' -f
                        "
                    """
                }
            }
        }
        stage('健康检查') {
            steps {
                sh """
                    sleep 10
                    curl -f http://${PRODUCTION_HOST}:8080/health || exit 1
                """
                echo '健康检查通过'
            }
        }
    }
    post {
        failure {
            echo '部署失败或健康检查不通过,执行回滚...'
            sshagent(['ssh-credentials-id']) {
                sh """
                    ssh user@${PRODUCTION_HOST} "
                        # 回滚到上一个版本(假设上次使用的标签是latest或上一个构建号)
                        docker pull ${DOCKER_REGISTRY}/${IMAGE_NAME}:previous
                        docker stop myapp || true
                        docker rm myapp || true
                        docker run -d --name myapp -p 8080:8080 ${DOCKER_REGISTRY}/${IMAGE_NAME}:previous
                    "
                """
            }
        }
    }
}

Kubernetes集群部署

适用于容器编排场景,最强大也最复杂。

部署流程:构建镜像 → 推送到仓库 → 更新K8s部署配置文件 → 应用配置。

pipeline {
    agent any
    environment {
        K8S_NAMESPACE = 'production'
        APP_NAME = 'myapp'
        IMAGE_TAG = "${BUILD_NUMBER}-${env.GIT_COMMIT.take(7)}"
        DOCKER_REGISTRY = 'your-registry.com'
    }
    stages {
        stage('构建并推送Docker镜像') {
            steps {
                script {
                    docker.build("${DOCKER_REGISTRY}/${APP_NAME}:${IMAGE_TAG}")
                    withDockerRegistry([credentialsId: 'docker-registry-credentials']) {
                        docker.image("${DOCKER_REGISTRY}/${APP_NAME}:${IMAGE_TAG}").push()
                    }
                }
            }
        }
        stage('更新Kubernetes部署') {
            steps {
                // 使用kubectl更新镜像版本
                withKubeConfig([credentialsId: 'kubeconfig-id']) {
                    sh """
                        # 更新部署文件中的镜像版本
                        sed -i 's|image:.*|image: ${DOCKER_REGISTRY}/${APP_NAME}:${IMAGE_TAG}|' k8s/deployment.yaml
                        # 应用配置
                        kubectl apply -f k8s/deployment.yaml -n ${K8S_NAMESPACE}
                        # 监控滚动更新状态
                        kubectl rollout status deployment/${APP_NAME} -n ${K8S_NAMESPACE} --timeout=5m
                    """
                }
            }
        }
        stage('验证部署') {
            steps {
                withKubeConfig([credentialsId: 'kubeconfig-id']) {
                    sh """
                        # 获取pod状态
                        kubectl get pods -l app=${APP_NAME} -n ${K8S_NAMESPACE}
                        # 将成功版本记录为稳定版本
                        kubectl annotate deployment/${APP_NAME} -n ${K8S_NAMESPACE} \
                            "rollback-version=${IMAGE_TAG}"
                    """
                }
            }
        }
    }
    post {
        failure {
            echo '部署失败,执行回滚...'
            withKubeConfig([credentialsId: 'kubeconfig-id']) {
                sh "kubectl rollout undo deployment/${APP_NAME} -n ${K8S_NAMESPACE}"
            }
        }
    }
}

生产环境部署的最佳实践

分阶段发布策略

在pipeline中添加审批环节:

stage('部署到生产环境') {
    input {
        message "确认部署到生产环境 ${BUILD_NUMBER}?"
        ok "确认部署"
        parameters {
            string(name: 'VERSION_TAG', defaultValue: 'latest', description: '部署版本标签')
        }
    }
    steps {
        // 实际部署逻辑
    }
}

蓝绿部署实现

stage('蓝绿部署') {
    steps {
        // 思路:维护两个环境(blue/green),每次部署到非活跃环境
        script {
            currentActive = sh(script: 'kubectl get svc myapp -n production -o jsonpath="{.spec.selector.color}"', returnStdout: true).trim()
            targetColor = (currentActive == 'blue') ? 'green' : 'blue'
            // 部署到目标环境
            sh "kubectl apply -f k8s/${targetColor}-deployment.yaml -n production"
            // 切换流量
            sh "kubectl patch svc myapp -n production -p '{\"spec\":{\"selector\":{\"color\":\"${targetColor}\"}}}'"
        }
    }
}

配置管理

  • 敏感信息:通过Jenkins凭据管理,使用credentials()函数。
  • 环境变量:尽量不硬编码,使用全局变量或外部配置中心。
  • 配置文件:存放到专门的配置仓库,部署时拉取。

日志和监控

stage('部署完成') {
    steps {
        // 将部署信息推送到监控系统
        sh """
            curl -X POST https://monitoring.yourcompany.com/api/deploy \
                -H 'Content-Type: application/json' \
                -d '{"app":"${APP_NAME}","version":"${BUILD_NUMBER}","env":"production"}'
        """
    }
}

权限控制

pipeline {
    // 使用Folder/Job级别的权限控制
    options {
        // 只有特定用户可以触发部署
        buildDiscarder(logRotator(numToKeepStr: '10'))
    }
}

你可能需要注意的问题

  • 回滚策略:始终保留最近N个版本的备份。
  • 数据库变更:向后兼容,新代码应兼容旧数据库结构。
  • 依赖服务:部署前检查依赖服务是否正常。
  • 并发部署:使用lock资源限制同时部署数量。
  • 通知机制:部署成功/失败通知相关团队(邮件、Slack等)。
部署方式 适用场景 复杂度 优势
SSH + 传统应用 中小型项目、传统应用 简单直接
Docker容器化 微服务、现代应用 一致性高
Kubernetes 大规模容器编排 弹性、自动化

选择哪种方式取决于你的技术栈、团队能力和生产环境要求,对于新项目,Docker容器化 + Kubernetes 是当前的主流趋势。

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