refactor: Jenkins 流水线改用 Publish Over SSH 插件
- 将 sshagent + rsync 方式替换为 sshPublisher 方式 - 移除手动 SSH 参数配置,使用固定服务器配置 - 更新默认管理员密码 Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
parent
f5143aee09
commit
18c14f18af
@ -9,7 +9,7 @@ QIP_DATA_HOST_DIR=/docker_data
|
|||||||
# 生产务必修改
|
# 生产务必修改
|
||||||
AUTH_TOKEN_SECRET=change-me-to-a-long-random-string
|
AUTH_TOKEN_SECRET=change-me-to-a-long-random-string
|
||||||
DEFAULT_SUPERADMIN_USERNAME=admin
|
DEFAULT_SUPERADMIN_USERNAME=admin
|
||||||
DEFAULT_SUPERADMIN_PASSWORD=change-me-strong-password
|
DEFAULT_SUPERADMIN_PASSWORD=Sto1234@
|
||||||
DEFAULT_SUPERADMIN_DISPLAY_NAME=系统超管
|
DEFAULT_SUPERADMIN_DISPLAY_NAME=系统超管
|
||||||
|
|
||||||
# MCP 鉴权(建议 true)
|
# MCP 鉴权(建议 true)
|
||||||
|
|||||||
243
Jenkinsfile
vendored
243
Jenkinsfile
vendored
@ -3,13 +3,10 @@ pipeline {
|
|||||||
|
|
||||||
environment {
|
environment {
|
||||||
DEPLOY_DIR = '/opt/quality-inspection-platform'
|
DEPLOY_DIR = '/opt/quality-inspection-platform'
|
||||||
DATA_DIR = '/docker_data'
|
SSH_SERVER = '10.20.30.42'
|
||||||
}
|
}
|
||||||
|
|
||||||
parameters {
|
parameters {
|
||||||
string(name: 'DEPLOY_HOST', defaultValue: '', description: '部署目标服务器 SSH 地址,如 root@10.0.0.5')
|
|
||||||
string(name: 'SSH_PORT', defaultValue: '22', description: 'SSH 端口')
|
|
||||||
string(name: 'SSH_CREDENTIALS', defaultValue: 'deploy-ssh-key', description: 'Jenkins 凭据 ID(SSH 私钥或用户名密码)')
|
|
||||||
choice(name: 'ACTION', choices: ['deploy', 'restart', 'stop', 'logs'], description: '执行动作')
|
choice(name: 'ACTION', choices: ['deploy', 'restart', 'stop', 'logs'], description: '执行动作')
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -21,9 +18,6 @@ pipeline {
|
|||||||
|
|
||||||
stages {
|
stages {
|
||||||
|
|
||||||
// ──────────────────────────────────────
|
|
||||||
// 1. 拉取代码
|
|
||||||
// ──────────────────────────────────────
|
|
||||||
stage('Checkout') {
|
stage('Checkout') {
|
||||||
steps {
|
steps {
|
||||||
checkout scm
|
checkout scm
|
||||||
@ -35,146 +29,149 @@ pipeline {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ──────────────────────────────────────
|
stage('Deploy') {
|
||||||
// 2. 推送源码到目标服务器
|
|
||||||
// ──────────────────────────────────────
|
|
||||||
stage('Sync Source') {
|
|
||||||
when { expression { params.ACTION == 'deploy' } }
|
when { expression { params.ACTION == 'deploy' } }
|
||||||
steps {
|
steps {
|
||||||
sshagent(credentials: [params.SSH_CREDENTIALS]) {
|
sshPublisher(
|
||||||
sh """
|
publishers: [
|
||||||
ssh -p ${params.SSH_PORT} -o StrictHostKeyChecking=no ${params.DEPLOY_HOST} \
|
sshPublisherDesc(
|
||||||
'mkdir -p ${DEPLOY_DIR}'
|
configName: "${SSH_SERVER}",
|
||||||
|
verbose: true,
|
||||||
|
transfers: [
|
||||||
|
sshTransfer(
|
||||||
|
sourceFiles: '**/*',
|
||||||
|
excludes: '.git/**, .venv/**, __pycache__/**, node_modules/**, *.pyc, .env',
|
||||||
|
remoteDirectory: "${DEPLOY_DIR}",
|
||||||
|
removePrefix: '',
|
||||||
|
cleanRemote: true,
|
||||||
|
execTimeout: 600000,
|
||||||
|
execCommand: """
|
||||||
|
set -eu
|
||||||
|
cd ${DEPLOY_DIR}
|
||||||
|
|
||||||
rsync -avz --delete \
|
# 首次部署自动创建 .env
|
||||||
--exclude '.git' \
|
if [ ! -f .env ]; then
|
||||||
--exclude '.venv' \
|
cp .env.example .env
|
||||||
--exclude '__pycache__' \
|
echo "首次部署:已从 .env.example 创建 .env,请稍后修改密码和密钥"
|
||||||
--exclude 'node_modules' \
|
fi
|
||||||
--exclude '*.pyc' \
|
|
||||||
--exclude '.env' \
|
# 确保数据目录存在
|
||||||
-e "ssh -p ${params.SSH_PORT} -o StrictHostKeyChecking=no" \
|
DATA_HOST_DIR=\$(grep "^QIP_DATA_HOST_DIR=" .env | cut -d= -f2 || echo "/docker_data")
|
||||||
./ ${params.DEPLOY_HOST}:${DEPLOY_DIR}/
|
[ -z "\$DATA_HOST_DIR" ] && DATA_HOST_DIR="/docker_data"
|
||||||
"""
|
mkdir -p "\$DATA_HOST_DIR"
|
||||||
}
|
|
||||||
echo '源码同步完成'
|
# 备份数据库
|
||||||
|
if [ -f "\$DATA_HOST_DIR/ai_test.db" ]; then
|
||||||
|
cp "\$DATA_HOST_DIR/ai_test.db" \\
|
||||||
|
"\$DATA_HOST_DIR/ai_test.db.bak.\$(date +%Y%m%d%H%M%S)"
|
||||||
|
echo "数据库已备份"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# 构建镜像
|
||||||
|
DOCKER_BUILDKIT=1 docker compose build --pull
|
||||||
|
|
||||||
|
# 重启容器
|
||||||
|
docker compose down --remove-orphans
|
||||||
|
docker compose up -d
|
||||||
|
|
||||||
|
echo "等待容器启动..."
|
||||||
|
sleep 20
|
||||||
|
|
||||||
|
# 健康检查
|
||||||
|
PORT=\$(grep "^QIP_HOST_PORT=" .env | cut -d= -f2 || echo "8000")
|
||||||
|
[ -z "\$PORT" ] && PORT="8000"
|
||||||
|
STATUS=\$(curl -s -o /dev/null -w "%{http_code}" http://127.0.0.1:\$PORT/health || true)
|
||||||
|
if [ "\$STATUS" = "200" ]; then
|
||||||
|
echo "部署成功,健康检查通过 (HTTP 200)"
|
||||||
|
else
|
||||||
|
echo "健康检查未通过 (HTTP \$STATUS),查看日志:"
|
||||||
|
docker compose logs --tail 30
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
"""
|
||||||
|
)
|
||||||
|
]
|
||||||
|
)
|
||||||
|
]
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ──────────────────────────────────────
|
stage('Restart') {
|
||||||
// 3. 远程服务器 build + 启动
|
|
||||||
// ──────────────────────────────────────
|
|
||||||
stage('Remote Build & Start') {
|
|
||||||
when { expression { params.ACTION == 'deploy' } }
|
|
||||||
steps {
|
|
||||||
sshagent(credentials: [params.SSH_CREDENTIALS]) {
|
|
||||||
sh """
|
|
||||||
ssh -p ${params.SSH_PORT} -o StrictHostKeyChecking=no ${params.DEPLOY_HOST} '
|
|
||||||
set -eu
|
|
||||||
cd ${DEPLOY_DIR}
|
|
||||||
|
|
||||||
# 确保 .env 存在(首次部署从 example 复制)
|
|
||||||
if [ ! -f .env ]; then
|
|
||||||
cp .env.example .env
|
|
||||||
echo "首次部署:已从 .env.example 创建 .env,请稍后修改密码和密钥"
|
|
||||||
fi
|
|
||||||
|
|
||||||
# 确保数据目录存在
|
|
||||||
DATA_HOST_DIR=\$(grep "^QIP_DATA_HOST_DIR=" .env | cut -d= -f2 || echo "/docker_data")
|
|
||||||
[ -z "\$DATA_HOST_DIR" ] && DATA_HOST_DIR="/docker_data"
|
|
||||||
mkdir -p "\$DATA_HOST_DIR"
|
|
||||||
|
|
||||||
# 备份数据库
|
|
||||||
if [ -f "\$DATA_HOST_DIR/ai_test.db" ]; then
|
|
||||||
cp "\$DATA_HOST_DIR/ai_test.db" \
|
|
||||||
"\$DATA_HOST_DIR/ai_test.db.bak.\$(date +%Y%m%d%H%M%S)"
|
|
||||||
echo "数据库已备份"
|
|
||||||
fi
|
|
||||||
|
|
||||||
# 构建镜像(使用 BuildKit 加速)
|
|
||||||
DOCKER_BUILDKIT=1 docker compose build --pull
|
|
||||||
|
|
||||||
# 重启容器
|
|
||||||
docker compose down --remove-orphans
|
|
||||||
docker compose up -d
|
|
||||||
|
|
||||||
echo "等待容器启动..."
|
|
||||||
sleep 20
|
|
||||||
|
|
||||||
# 健康检查
|
|
||||||
PORT=\$(grep "^QIP_HOST_PORT=" .env | cut -d= -f2 || echo "8000")
|
|
||||||
[ -z "\$PORT" ] && PORT="8000"
|
|
||||||
STATUS=\$(curl -s -o /dev/null -w "%{http_code}" http://127.0.0.1:\$PORT/health || true)
|
|
||||||
if [ "\$STATUS" = "200" ]; then
|
|
||||||
echo "✓ 部署成功,健康检查通过 (HTTP 200)"
|
|
||||||
else
|
|
||||||
echo "✗ 健康检查未通过 (HTTP \$STATUS),查看日志:"
|
|
||||||
docker compose logs --tail 30
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
'
|
|
||||||
"""
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// ──────────────────────────────────────
|
|
||||||
// 4. 仅重启(不重新 build)
|
|
||||||
// ──────────────────────────────────────
|
|
||||||
stage('Restart Only') {
|
|
||||||
when { expression { params.ACTION == 'restart' } }
|
when { expression { params.ACTION == 'restart' } }
|
||||||
steps {
|
steps {
|
||||||
sshagent(credentials: [params.SSH_CREDENTIALS]) {
|
sshPublisher(
|
||||||
sh """
|
publishers: [
|
||||||
ssh -p ${params.SSH_PORT} -o StrictHostKeyChecking=no ${params.DEPLOY_HOST} '
|
sshPublisherDesc(
|
||||||
cd ${DEPLOY_DIR}
|
configName: "${SSH_SERVER}",
|
||||||
docker compose restart
|
verbose: true,
|
||||||
sleep 10
|
transfers: [
|
||||||
curl -sf http://127.0.0.1:\$(grep "^QIP_HOST_PORT=" .env | cut -d= -f2 || echo 8000)/health \
|
sshTransfer(
|
||||||
&& echo "重启成功" || echo "重启后健康检查失败"
|
execTimeout: 120000,
|
||||||
'
|
execCommand: """
|
||||||
"""
|
cd ${DEPLOY_DIR}
|
||||||
}
|
docker compose restart
|
||||||
|
sleep 10
|
||||||
|
curl -sf http://127.0.0.1:\$(grep "^QIP_HOST_PORT=" .env | cut -d= -f2 || echo 8000)/health \\
|
||||||
|
&& echo "重启成功" || echo "重启后健康检查失败"
|
||||||
|
"""
|
||||||
|
)
|
||||||
|
]
|
||||||
|
)
|
||||||
|
]
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ──────────────────────────────────────
|
|
||||||
// 5. 停止
|
|
||||||
// ──────────────────────────────────────
|
|
||||||
stage('Stop') {
|
stage('Stop') {
|
||||||
when { expression { params.ACTION == 'stop' } }
|
when { expression { params.ACTION == 'stop' } }
|
||||||
steps {
|
steps {
|
||||||
sshagent(credentials: [params.SSH_CREDENTIALS]) {
|
sshPublisher(
|
||||||
sh """
|
publishers: [
|
||||||
ssh -p ${params.SSH_PORT} -o StrictHostKeyChecking=no ${params.DEPLOY_HOST} '
|
sshPublisherDesc(
|
||||||
cd ${DEPLOY_DIR} && docker compose down
|
configName: "${SSH_SERVER}",
|
||||||
echo "容器已停止"
|
verbose: true,
|
||||||
'
|
transfers: [
|
||||||
"""
|
sshTransfer(
|
||||||
}
|
execTimeout: 60000,
|
||||||
|
execCommand: """
|
||||||
|
cd ${DEPLOY_DIR} && docker compose down
|
||||||
|
echo "容器已停止"
|
||||||
|
"""
|
||||||
|
)
|
||||||
|
]
|
||||||
|
)
|
||||||
|
]
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ──────────────────────────────────────
|
stage('Logs') {
|
||||||
// 6. 查看日志
|
|
||||||
// ──────────────────────────────────────
|
|
||||||
stage('View Logs') {
|
|
||||||
when { expression { params.ACTION == 'logs' } }
|
when { expression { params.ACTION == 'logs' } }
|
||||||
steps {
|
steps {
|
||||||
sshagent(credentials: [params.SSH_CREDENTIALS]) {
|
sshPublisher(
|
||||||
sh """
|
publishers: [
|
||||||
ssh -p ${params.SSH_PORT} -o StrictHostKeyChecking=no ${params.DEPLOY_HOST} '
|
sshPublisherDesc(
|
||||||
cd ${DEPLOY_DIR} && docker compose logs --tail 100
|
configName: "${SSH_SERVER}",
|
||||||
'
|
verbose: true,
|
||||||
"""
|
transfers: [
|
||||||
}
|
sshTransfer(
|
||||||
|
execTimeout: 30000,
|
||||||
|
execCommand: """
|
||||||
|
cd ${DEPLOY_DIR} && docker compose logs --tail 100
|
||||||
|
"""
|
||||||
|
)
|
||||||
|
]
|
||||||
|
)
|
||||||
|
]
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
post {
|
post {
|
||||||
success { echo "✓ 流水线执行成功: ${params.ACTION}" }
|
success { echo "流水线执行成功: ${params.ACTION}" }
|
||||||
failure { echo "✗ 流水线执行失败,请检查日志" }
|
failure { echo "流水线执行失败,请检查日志" }
|
||||||
always { cleanWs() }
|
always { cleanWs() }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user