Files
campus_spider/Jenkinsfile
T
2026-07-30 11:45:08 +08:00

166 lines
6.6 KiB
Groovy
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* OfferPie Spider 部署流水线
*
* Jenkins 容器挂载了宿主机 docker.sock,镜像共享。
* 直接在 workspace 内 build + compose up。
*
* SCRIPT=main 时部署常驻定时任务服务;
* SCRIPT=init_* 时用同一个镜像跑一次性存量初始化脚本,跑完容器自动删除,不影响常驻服务。
*/
pipeline {
agent any
parameters {
choice(name: 'BRANCH', choices: ['master', 'dev'], description: '选择要部署的分支')
choice(name: 'ACTION', choices: ['deploy', 'stop'], description: '操作:deploy=构建部署,stop=停止服务')
choice(name: 'SCRIPT',
choices: ['main', 'init_offershow', 'init_offerqingbaoju', 'init_xiaozhaoya'],
description: '选择要运行的脚本:main=常驻定时任务服务,init_*=一次性存量初始化(跑完退出)')
string(name: 'LIMIT', defaultValue: '',
description: '抓取条数上限,仅对 init_offershow / init_offerqingbaoju 生效;留空用脚本默认值')
}
environment {
IMAGE_NAME = 'offerpie-spider'
IMAGE_TAG = 'latest'
CONTAINER_NAME = 'offerpie-spider'
// 固定 compose 项目名:默认取 workspace 目录名,并发构建时会变成 xxx@2 导致容器名冲突
COMPOSE_PROJECT_NAME = 'offerpie-spider'
// 一次性容器的挂载与资源限制,保持与 docker-compose.yml 一致
LOG_VOLUME = '/opt/offerpie/spider/logs:/app/logs'
}
stages {
stage('参数校验') {
steps {
script {
// LIMIT 会拼进 shell 命令,只允许纯数字,防止注入
def limit = params.LIMIT?.trim()
if (limit && !(limit ==~ /[1-9]\d*/)) {
error "LIMIT 必须是正整数,当前值:${params.LIMIT}"
}
if (limit && params.SCRIPT in ['main', 'init_xiaozhaoya']) {
echo "⚠️ ${params.SCRIPT} 不接受 LIMIT 参数,本次将忽略"
}
}
}
}
stage('停止服务') {
when {
expression { params.ACTION == 'stop' }
}
steps {
sh "docker compose down || true"
}
}
stage('拉取代码') {
when {
expression { params.ACTION == 'deploy' }
}
steps {
echo "拉取 ${params.BRANCH} 分支代码"
git branch: "${params.BRANCH}",
credentialsId: 'gitea-fab089c1-b55d-4b58-9fad',
url: 'https://git.jianshixingqiu.com/offerpai/campus_spider.git'
}
}
stage('构建镜像') {
when {
expression { params.ACTION == 'deploy' }
}
steps {
sh "docker build -t ${IMAGE_NAME}:${IMAGE_TAG} ."
}
}
stage('部署常驻服务') {
when {
expression { params.ACTION == 'deploy' && params.SCRIPT == 'main' }
}
steps {
// 停旧启新,docker-compose.yml 就在当前 workspace
sh "docker compose down || true"
sh "docker compose up -d"
sleep 5
// 精确匹配容器名,避免把 offerpie-spider-init_* 这类一次性容器也列出来
sh "docker ps -f 'name=^${CONTAINER_NAME}\$' --format '{{.Status}}'"
}
}
stage('执行初始化脚本') {
when {
expression { params.ACTION == 'deploy' && params.SCRIPT != 'main' }
}
steps {
script {
def limit = params.LIMIT?.trim()
def runName = "${CONTAINER_NAME}-${params.SCRIPT}"
def cmd = "python ${params.SCRIPT}.py"
if (limit && params.SCRIPT != 'init_xiaozhaoya') {
cmd += " ${limit}"
}
echo "运行一次性脚本:${cmd}"
// 同名任务还在跑就直接失败,不要把正在灌数的任务强杀掉
def running = sh(script: "docker ps -q -f 'name=^${runName}\$'",
returnStdout: true).trim()
if (running) {
error "${runName} 正在运行中,如确认要重跑请先手动执行:docker rm -f ${runName}"
}
// 清掉已退出的同名残留容器(--rm 一般会自动删,异常退出时可能留下)
sh "docker rm -f ${runName} || true"
// 故意用 docker run 而不是 docker compose run
// compose run 起的容器带同一个 project 标签,别人执行 compose down 会把灌数任务一起干掉
// LOG_FILE_NAME 让日志单独落一个文件,不和常驻服务的 spider.log 交叉写入
// --rm:跑完自动删除;不带 -d,日志直接输出到本次构建,脚本退出码决定构建成败
// 实测全量灌数约 12 小时,超时给到 24 小时
timeout(time: 24, unit: 'HOURS') {
sh """docker run --rm --name ${runName} \
--network host \
--shm-size 2g \
--memory 16g \
--cpus 8 \
-e ENV=prod \
-e TZ=Asia/Shanghai \
-e OMP_NUM_THREADS=8 \
-e LOG_FILE_NAME=${params.SCRIPT}.log \
-v ${LOG_VOLUME} \
${IMAGE_NAME}:${IMAGE_TAG} ${cmd}"""
}
}
}
}
stage('清理') {
when {
expression { params.ACTION == 'deploy' }
}
steps {
sh "docker image prune -f || true"
}
}
}
post {
success {
script {
if (params.ACTION == 'stop') {
echo "✅ 服务已停止"
} else if (params.SCRIPT == 'main') {
echo "✅ 部署成功!容器 ${CONTAINER_NAME} 已启动"
} else {
echo "✅ 初始化脚本 ${params.SCRIPT}.py 执行完成"
}
}
}
failure {
echo '❌ 操作失败,请检查日志'
}
}
}