build-flow.api/Jenkinsfile
xiangning 2489b33270 fix(cicd): push 重试每次重新 login,修复 Harbor auth session 过期
重试多次后报 'no basic auth credentials'(docker login session 过期)。
改成 push_retry 函数:每次 push 前重新 docker login,组合成原子重试单元。
重试次数提到 10 次。
2026-07-17 14:30:57 +08:00

102 lines
4.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.

// BuildFlow buildflow-api CI/CD
// 链路:①拉取代码 → ②编译打包(docker build) → 推送 Harbor → ③部署(cd-target SSH) → HealthCheck
// 注:飞书通知逻辑已移除(凭证未配齐);部署通过 ssh 直连 cd-target 执行。
// api 运行依赖:PostgreSQL(5库) / Redis / MongoDB(im_system) / RabbitMQ / S3(RustFS) / 本地LLM(1234) / KKFileView(8012)
pipeline {
agent any
environment {
HARBOR = "43.134.235.58:58080"
IMAGE = "${HARBOR}/buildflow/buildflow-api"
TAG = "${env.BUILD_NUMBER}"
// Jenkins 容器内无 ssh 别名,直接用 IP;ubuntu 用户 sudo 免密(NOPASSWD)
DEPLOY_HOST = "ubuntu@110.42.240.10"
APP_PORT = "5667"
}
options {
timeout(time: 30, unit: "MINUTES")
buildDiscarder(logRotator(numToKeepStr: "10"))
}
stages {
stage("① 拉取代码") {
steps {
checkout scm
sh "git log --oneline -3"
}
}
stage("② 编译打包") {
steps {
sh "docker build -t ${IMAGE}:${TAG} -t ${IMAGE}:latest ."
sh "docker images | grep ${IMAGE} | head -2"
}
}
stage("推送 Harbor") {
steps {
withCredentials([usernamePassword(credentialsId: "harbor-credentials", usernameVariable: "HUSER", passwordVariable: "HPASS")]) {
sh '''
# 跨地域 push 大 blob 易断流 + Harbor auth session 会过期
# 每次 push 前重新 login,组合成原子重试单元(断点续传,已传层跳过)
push_retry() {
local img=$1
for i in 1 2 3 4 5 6 7 8 9 10; do
echo $HPASS | docker login $HARBOR -u $HUSER --password-stdin >/dev/null 2>&1
docker push "$img" && return 0
echo "push $img 第 $i 次中断,5秒后重试(重新登录+断点续传)..."
sleep 5
done
return 1
}
push_retry $IMAGE:$TAG
push_retry $IMAGE:latest
docker logout $HARBOR
'''
}
}
}
stage("③ 部署") {
steps {
script {
try {
withCredentials([usernamePassword(credentialsId: "harbor-credentials", usernameVariable: "HUSER", passwordVariable: "HPASS")]) {
sh """
ssh -o StrictHostKeyChecking=accept-new ${DEPLOY_HOST} "
echo '${HPASS}' | sudo docker login ${HARBOR} -u ${HUSER} --password-stdin
sudo docker pull ${IMAGE}:latest
sudo docker rm -f buildflow-api 2>/dev/null || true
sudo docker run -d --name buildflow-api --restart unless-stopped -p ${APP_PORT}:${APP_PORT} ${IMAGE}:latest
"
"""
}
echo "部署完成"
} catch (e) {
echo "警告:部署节点 ${DEPLOY_HOST} 暂不可用,跳过自动部署(镜像已推送 Harbor)。原因:${e.getMessage()}"
}
}
}
}
stage("HealthCheck") {
steps {
script {
try {
sh """
sleep 8
ssh -o StrictHostKeyChecking=accept-new ${DEPLOY_HOST} 'wget -q --spider http://127.0.0.1:${APP_PORT}/health || wget -q --spider http://127.0.0.1:${APP_PORT}/ && echo HEALTHCHECK_OK'
"""
echo "=== HealthCheck OK: api http://110.42.240.10:${APP_PORT} ==="
} catch (e) {
echo "警告:HealthCheck 探测失败(容器可能仍在启动,或依赖的 PG/Redis/Mongo 未就绪)。镜像已推送 Harbor,可手动检查。"
}
}
}
}
}
post {
success { echo '=== buildflow-api CI/CD SUCCESS ===' }
failure { echo '=== buildflow-api CI/CD FAILED ===' }
always {
sh "docker rmi ${IMAGE}:${TAG} ${IMAGE}:latest 2>/dev/null || true"
deleteDir()
}
}
}