/** * OfferPie Web 前端蓝绿部署流水线 * * 工作目录说明: * - Jenkins 会自动为每个项目创建独立的工作空间:/var/jenkins_home/workspace/<项目名>/ * - docker-compose.yml、Dockerfile、nginx.conf 都在项目根目录,直接执行即可 * - nginx.conf 首次部署时 docker cp 进 nginx 容器 * * 内层 Nginx 职责: * - 蓝绿切换(前端静态文件 serve) * - /api/ 代理到 Java 后端(10202) * - /ai-api/ 代理到 Python AI(10502),rewrite 去掉前缀 */ pipeline { agent any parameters { choice(name: 'BRANCH', choices: ['master', 'pre', 'dev', 'test'], description: '选择要部署的分支') } environment { CONTAINER_PREFIX = 'offerpie-web' // 容器名前缀,拼接 -blue / -green / -nginx HEALTH_URL = 'http://localhost:80/' // 前端静态页面健康检查 } stages { stage('开始提示') { steps { echo "OfferPie Web 前端开始构建" } } stage('拉取代码') { steps { echo "拉取 ${params.BRANCH} 分支代码" git branch: "${params.BRANCH}", credentialsId: 'ef5fffc1-9b35-403d-9ca6-e1b73eb0e45a', url: 'https://codeup.aliyun.com/5f0ed3b9769820a3e817dee2/offerpie/offerpie_web.git' } } /** * 检测部署目标 * - 检查 blue 和 green 容器的运行状态 * - blue 在运行 → 部署 green;green 在运行 → 部署 blue * - 都不在或都在 → 默认部署 blue */ stage('检测部署目标') { steps { echo "检查当前容器状态" script { def blueRunning = sh(script: "docker ps -q -f name=${CONTAINER_PREFIX}-blue", returnStdout: true).trim() def greenRunning = sh(script: "docker ps -q -f name=${CONTAINER_PREFIX}-green", returnStdout: true).trim() env.DEPLOY_TARGET = '' if (blueRunning && !greenRunning) { env.DEPLOY_TARGET = 'green' } if (greenRunning && !blueRunning) { env.DEPLOY_TARGET = 'blue' } if (!env.DEPLOY_TARGET) { echo "当前环境未部署服务或状态异常,默认部署 blue" env.DEPLOY_TARGET = 'blue' } env.OTHER_TARGET = (env.DEPLOY_TARGET == 'blue') ? 'green' : 'blue' echo "当前激活: ${env.OTHER_TARGET},即将部署: ${env.DEPLOY_TARGET}" } } } /** * 构建新版本 * - 先清理目标颜色的旧容器(如果残留) * - docker-compose build 构建新镜像(含 pnpm build) */ stage('构建新版本') { steps { script { def existingContainer = sh(script: "docker ps -aq -f name=${CONTAINER_PREFIX}-${env.DEPLOY_TARGET}", returnStdout: true).trim() if (existingContainer) { echo "清理已存在的容器: ${CONTAINER_PREFIX}-${env.DEPLOY_TARGET}" sh "docker rm -f ${CONTAINER_PREFIX}-${env.DEPLOY_TARGET}" } sh "docker-compose build ${env.DEPLOY_TARGET}" } } } /** * 检查 Nginx * - 检查 nginx 容器是否存在 * - 不存在则启动并复制配置文件(首次部署) * - 存在则确保容器运行中 */ stage('检查Nginx') { steps { script { def nginxExists = sh(script: "docker ps -aq -f name=${CONTAINER_PREFIX}-nginx", returnStdout: true).trim() if (!nginxExists) { echo "首次部署,初始化 Nginx 容器" sh "docker-compose up -d nginx" sh "sleep 3" // 复制配置文件到容器内 sh "docker cp proxy_nginx.conf ${CONTAINER_PREFIX}-nginx:/etc/nginx/nginx.conf" sh "docker exec ${CONTAINER_PREFIX}-nginx nginx -s reload" } else { def nginxRunning = sh(script: "docker ps -q -f name=${CONTAINER_PREFIX}-nginx", returnStdout: true).trim() if (!nginxRunning) { echo "Nginx 容器已停止,重新启动" sh "docker start ${CONTAINER_PREFIX}-nginx" sh "sleep 2" } echo "Nginx 容器已存在且运行中" } } } } stage('启动新版本') { steps { script { sh "docker-compose up -d ${env.DEPLOY_TARGET}" // 等待 Nginx 容器启动 sh 'sleep 5' } } } /** * 健康检查 * - 进入容器检测静态页面是否正常返回 * - 最多重试 3 次,每次间隔 5 秒 * - 全部失败则终止部署流程 */ stage('健康检查') { steps { script { def maxRetries = 3 def retryCount = 0 def healthy = false while (retryCount < maxRetries && !healthy) { try { sh "docker exec ${CONTAINER_PREFIX}-${env.DEPLOY_TARGET} curl -f ${HEALTH_URL}" healthy = true } catch (Exception e) { retryCount++ if (retryCount < maxRetries) { echo "健康检查失败,5秒后重试 (${retryCount}/${maxRetries})" sleep 5 } } } if (!healthy) { error "健康检查失败,部署中止" } echo "✅ ${CONTAINER_PREFIX}-${env.DEPLOY_TARGET} 健康检查通过" } } } /** * 切换流量 * - 修改 nginx 配置中的 proxy_pass 指向新版本 * - nginx 不挂载宿主机文件,直接 sed -i 修改容器内配置 * - reload 使新配置生效,实现零停机切换 */ stage('切换流量') { steps { script { sh "docker exec ${CONTAINER_PREFIX}-nginx sed -i 's/proxy_pass http:\\/\\/\\(blue\\|green\\);/proxy_pass http:\\/\\/${env.DEPLOY_TARGET};/' /etc/nginx/nginx.conf" sh "docker exec ${CONTAINER_PREFIX}-nginx nginx -s reload" echo "✅ 流量已切换到 ${env.DEPLOY_TARGET}" } } } /** * 停止并删除旧版本 * - 不能用 docker-compose down,会删除所有服务(包括 nginx) * - 用 docker rm -f 只删除指定的旧颜色容器 */ stage('删除旧版本') { steps { script { def otherExists = sh(script: "docker ps -aq -f name=${CONTAINER_PREFIX}-${env.OTHER_TARGET}", returnStdout: true).trim() if (otherExists) { sh "docker rm -f ${CONTAINER_PREFIX}-${env.OTHER_TARGET}" echo "旧版本 ${env.OTHER_TARGET} 已删除" } } } } } post { success { echo "✅ 蓝绿部署成功!当前运行: ${env.DEPLOY_TARGET}" } failure { echo '❌ 部署失败,请检查日志' } } }