添加jenkins
This commit is contained in:
@@ -0,0 +1,164 @@
|
||||
// ============================================================
|
||||
// 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-prod(Secret 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} 部署失败,请检查日志"
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user