From c21692f91e20a539077adf90d4a69617107c4d07 Mon Sep 17 00:00:00 2001 From: cheney Date: Fri, 28 Aug 2026 09:33:22 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20CICD=E4=B8=8E=E9=83=A8=E7=BD=B2?= =?UTF-8?q?=E8=84=9A=E6=9C=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Jenkinsfile: 构建+部署+健康检查流水线 - start.sh: 服务器启动脚本 --- Jenkinsfile | 83 +++++++++++++++++++++++++++++++++++++++++++++++++++++ start.sh | 41 ++++++++++++++++++++++++++ 2 files changed, 124 insertions(+) create mode 100644 Jenkinsfile create mode 100644 start.sh diff --git a/Jenkinsfile b/Jenkinsfile new file mode 100644 index 0000000..08b83dc --- /dev/null +++ b/Jenkinsfile @@ -0,0 +1,83 @@ +pipeline { + agent any + + environment { + IMAGE_NAME = 'awesome-index' + CONTAINER_NAME = 'awesome' + DEPLOY_PATH = '/app/awesome' + SSH_HOST = '114.134.187.170' + SSH_USER = 'root' + } + + stages { + stage('Build') { + steps { + script { + echo "Building Docker image ${IMAGE_NAME}:${BUILD_NUMBER}" + sh "docker build -t ${IMAGE_NAME}:${BUILD_NUMBER} ." + sh "docker tag ${IMAGE_NAME}:${BUILD_NUMBER} ${IMAGE_NAME}:latest" + } + } + } + + stage('Deploy') { + steps { + withCredentials([usernamePassword(credentialsId: 'baihu-ssh', usernameVariable: 'SSH_USER', passwordVariable: 'SSH_PASS')]) { + script { + def remote = "root@${SSH_HOST}" + + // 创建部署目录 + sh "sshpass -p '${SSH_PASS}' ssh -o StrictHostKeyChecking=no ${remote} 'mkdir -p ${DEPLOY_PATH}/data ${DEPLOY_PATH}/logs'" + + // 复制必要文件 + sh "sshpass -p '${SSH_PASS}' scp -o StrictHostKeyChecking=no docker-compose.yml ${remote}:${DEPLOY_PATH}/" + sh "sshpass -p '${SSH_PASS}' scp -o StrictHostKeyChecking=no .env.example ${remote}:${DEPLOY_PATH}/.env" + sh "sshpass -p '${SSH_PASS}' scp -o StrictHostKeyChecking=no start.sh ${remote}:${DEPLOY_PATH}/" + + // 导入镜像 + sh "docker save ${IMAGE_NAME}:latest | gzip > ${IMAGE_NAME}.tar.gz" + sh "sshpass -p '${SSH_PASS}' scp -o StrictHostKeyChecking=no ${IMAGE_NAME}.tar.gz ${remote}:${DEPLOY_PATH}/" + + // 在服务器上加载并重启 + sh """ + sshpass -p '${SSH_PASS}' ssh -o StrictHostKeyChecking=no ${remote} ' + cd ${DEPLOY_PATH} && + gunzip -c ${IMAGE_NAME}.tar.gz | docker load && + rm -f ${IMAGE_NAME}.tar.gz && + docker compose down --remove-orphans && + docker compose up -d + ' + """ + + // 清理本地 + sh "rm -f ${IMAGE_NAME}.tar.gz" + } + } + } + } + + stage('Health Check') { + steps { + script { + sleep 10 + def status = sh( + script: "curl -sf http://${SSH_HOST}:3000/healthz || echo 'FAILED'", + returnStdout: true + ).trim() + + if (status.contains('ok')) { + echo "Health check PASSED" + } else { + error "Health check FAILED" + } + } + } + } + } + + post { + always { + cleanWs() + } + } +} diff --git a/start.sh b/start.sh new file mode 100644 index 0000000..1aad47c --- /dev/null +++ b/start.sh @@ -0,0 +1,41 @@ +#!/bin/bash +set -e + +# Awesome Index 启动脚本 +DEPLOY_PATH="$(cd "$(dirname "$0")" && pwd)" +cd "$DEPLOY_PATH" + +# 检查 .env 文件 +if [ ! -f .env ]; then + echo "错误: 缺少 .env 文件,请从 .env.example 复制并修改" + exit 1 +fi + +# 停止旧容器 +echo "停止旧容器..." +docker compose down --remove-orphans 2>/dev/null || true + +# 启动新容器 +echo "启动 Awesome Index..." +docker compose up -d + +# 健康检查 +echo "等待服务就绪..." +sleep 5 + +MAX_RETRIES=10 +RETRY_COUNT=0 +while [ $RETRY_COUNT -lt $MAX_RETRIES ]; do + if curl -sf http://localhost:3000/healthz > /dev/null 2>&1; then + echo "✓ 服务已启动: http://localhost:3000" + echo "" + echo "管理后台: http://localhost:3000/admin" + echo "默认账号: admin / (见 .env 中的 ADMIN_INIT_PASSWORD)" + exit 0 + fi + RETRY_COUNT=$((RETRY_COUNT + 1)) + sleep 2 +done + +echo "✗ 服务启动失败,请检查日志: docker compose logs" +exit 1