Files
juwan-backend/deploy/jenkins/Jenkinsfile.webhook
T
2026-05-03 22:41:40 +08:00

165 lines
6.1 KiB
Plaintext
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.
// ============================================================
// juwan-backend CD Pipeline — Harbor Webhook 模式
// 适用场景:生产环境,Harbor 推送镜像后主动通知 Jenkins
//
// 工作原理:
// 1. Harbor 配置 Webhook,推送事件发送 POST 到 Jenkins
// 2. Jenkins Generic Webhook Trigger 插件解析 payload
// 3. 提取镜像名称,触发对应 Deployment 的滚动更新
//
// 前置条件(见 docs/jenkins-cd/02-production-webhook-setup.md):
// - Jenkins 插件:Generic Webhook Trigger
// - Jenkins 凭据:kubeconfig-prodSecret file,生产 kubeconfig
// - Harbor Webhook 配置指向本 Jenkins URL
// ============================================================
pipeline {
agent any
// ── Generic Webhook Trigger 插件配置 ─────────────────────
// Harbor push 事件 payload 结构:
// {
// "type": "PUSH_ARTIFACT",
// "event_data": {
// "resources": [{
// "resource_url": "harbor.example.com/juwan/user-api:abc1234"
// }],
// "repository": { "name": "user-api" }
// }
// }
triggers {
GenericTrigger(
genericVariables: [
// 提取镜像仓库名(即服务名,如 user-api)
[key: 'REPO_NAME', value: '$.event_data.repository.name'],
// 提取完整镜像 URL(含 tag)
[key: 'RESOURCE_URL', value: '$.event_data.resources[0].resource_url'],
// 提取事件类型(只处理 PUSH_ARTIFACT
[key: 'EVENT_TYPE', value: '$.type']
],
// Webhook token,在 Harbor 配置 Webhook URL 时附加:
// http://jenkins.example.com/generic-webhook-trigger/invoke?token=JUWAN_CD_TOKEN
token: 'JUWAN_CD_TOKEN',
// 只处理推送事件
regexpFilterText: '$EVENT_TYPE',
regexpFilterExpression: 'PUSH_ARTIFACT',
causeString: 'Harbor push: $REPO_NAME ($RESOURCE_URL)',
printContributedVariables: true,
printPostContent: true
)
}
parameters {
string(
name: 'HARBOR_REGISTRY',
defaultValue: 'harbor.example.com',
description: 'Harbor 镜像仓库地址'
)
string(
name: 'HARBOR_PROJECT',
defaultValue: 'juwan',
description: 'Harbor 项目名'
)
string(
name: 'K8S_NAMESPACE',
defaultValue: 'juwan',
description: 'Kubernetes 命名空间'
)
string(
name: 'KUBECONFIG_CREDENTIAL',
defaultValue: 'kubeconfig-prod',
description: 'Jenkins 中存储生产 kubeconfig 的凭据 ID'
)
}
environment {
KUBECTL = 'kubectl'
}
stages {
stage('Validate Trigger') {
steps {
script {
echo "=== Harbor Webhook 触发 ==="
echo "事件类型: ${env.EVENT_TYPE}"
echo "仓库名称: ${env.REPO_NAME}"
echo "镜像 URL: ${env.RESOURCE_URL}"
if (!env.REPO_NAME?.trim()) {
error("Webhook payload 中未找到 repository.name,请检查 Harbor Webhook 配置")
}
}
}
}
stage('Deploy') {
steps {
script {
def svc = env.REPO_NAME.trim()
withCredentials([
file(credentialsId: params.KUBECONFIG_CREDENTIAL, variable: 'KUBECONFIG_FILE')
]) {
// 验证 Deployment 存在
def exists = sh(
script: """
KUBECONFIG=\${KUBECONFIG_FILE} ${KUBECTL} get deployment ${svc} \\
-n ${params.K8S_NAMESPACE} \\
--ignore-not-found \\
-o name 2>/dev/null || echo ""
""",
returnStdout: true
).trim()
if (!exists) {
error("Deployment ${svc} 不存在于命名空间 ${params.K8S_NAMESPACE}")
}
echo "触发滚动更新: ${svc}"
// 用 image tag 中的 digest/sha 更新镜像,确保精确版本
// resource_url 格式: harbor.example.com/juwan/user-api:abc1234
def imageRef = env.RESOURCE_URL.trim()
sh """
KUBECONFIG=\${KUBECONFIG_FILE} ${KUBECTL} set image deployment/${svc} \\
${svc}=${imageRef} \\
-n ${params.K8S_NAMESPACE}
"""
def rolloutStatus = sh(
script: """
KUBECONFIG=\${KUBECONFIG_FILE} ${KUBECTL} rollout status deployment/${svc} \\
-n ${params.K8S_NAMESPACE} \\
--timeout=300s
""",
returnStatus: true
)
if (rolloutStatus == 0) {
echo "✅ ${svc} 部署成功 → ${imageRef}"
} else {
echo "❌ ${svc} 部署失败,执行回滚..."
sh """
KUBECONFIG=\${KUBECONFIG_FILE} ${KUBECTL} rollout undo deployment/${svc} \\
-n ${params.K8S_NAMESPACE}
"""
error("${svc} 部署失败,已回滚")
}
}
}
}
}
}
post {
success {
echo "✅ ${env.REPO_NAME} 部署完成"
}
failure {
echo "❌ ${env.REPO_NAME} 部署失败,请检查日志"
}
}
}