🛡️ + Jenkins 全链路代码质量门禁实战(含完整 Pipeline)
本文覆盖:
SonarQube 部署 → Jenkins 配置 → Pipeline 集成 → Webhook 优化 → 生产级 CI/CD
一、SonarQube 部署(Docker Compose)
1️⃣ 系统内核参数(必须)
SonarQube 内置 Elasticsearch,不配置会直接启动失败。
sysctl -w vm.max_map_count=524288
sysctl -w fs.file-max=131072
ulimit -n 131072
ulimit -u 8192
📌 备注
- 生产环境建议写入
/etc/sysctl.conf - 否则宿主机重启后参数失效
2️⃣ Docker Compose 编排文件
version: '3'
services:
sonar-db:
image: postgres:16
container_name: sonar-db
restart: always
environment:
POSTGRES_USER: sonar
POSTGRES_PASSWORD: sonar
POSTGRES_DB: sonarqube
volumes:
# PostgreSQL 数据持久化,防止升级或重启丢数据
- ./data/postgres:/var/lib/postgresql/data
networks:
- sonar-net
sonarqube:
image: sonarqube:10.2-community
container_name: sonarqube
depends_on:
- sonar-db
restart: always
ports:
- "9000:9000"
environment:
# JDBC 数据库连接信息
SONAR_JDBC_URL: jdbc:postgresql://sonar-db:5432/sonarqube
SONAR_JDBC_USERNAME: sonar
SONAR_JDBC_PASSWORD: sonar
# 禁用 ES 引导检查(仅测试/学习环境)
SONAR_ES_BOOTSTRAP_CHECKS_DISABLE: "true"
TZ: Asia/Shanghai
volumes:
# SonarQube 数据、插件、日志持久化
- ./data/sonarqube_data:/opt/sonarqube/data
- ./data/sonarqube_extensions:/opt/sonarqube/extensions
- ./data/sonarqube_logs:/opt/sonarqube/logs
networks:
- sonar-net
networks:
sonar-net:
driver: bridge
📌 备注
- PostgreSQL 与 SonarQube 通过自定义桥接网络通信
- 不建议在生产环境关闭 ES 检查
3️⃣ 初始化 SonarQube
- 访问:
http://<IP>:9000 - 默认账号:
admin / admin - 首次登录强制修改密码
二、生成 SonarQube Token(CI/CD 最关键)
右上角头像 → My Account → Security → Generate Tokens
Token 名称:jenkins
⚠️ Token 只显示一次,务必保存
三、Jenkins 配置 SonarQube
1️⃣ 安装插件
Manage Jenkins → Plugins
搜索:SonarQube Scanner
安装 → 重启 Jenkins
2️⃣ 添加凭据
|-------------|-----------------|
| 项 | 值 |
| Kind | Secret text |
| Secret | 粘贴 Sonar Token |
| ID | sonarqube-token |
| Description | SonarQube Token |
📌 备注
- 不要在 Jenkinsfile 中明文写 Token
- 多环境可创建多个 Token
3️⃣ 配置 SonarQube Server
Manage Jenkins → Configure System → SonarQube servers
|----------------------|--------------------|
| 项 | 值 |
| Name | sonarqube |
| Server URL | http://<IP>:9000 |
| Authentication Token | sonarqube-token |
四、Jenkins Pipeline(完整 + 详细注释)
✅ 支持 正常发布 + 版本回滚
✅ 包含 SonarQube 扫描 + 质量门禁
✅ 包含 钉钉审批 + K8s 部署
pipeline {
agent any
// ================== 流水线参数 ==================
parameters {
choice(
name: 'DEPLOY_TYPE',
choices: ['deploy', 'rollback'],
description: 'deploy = 正常发布,rollback = 版本回滚'
)
string(
name: 'ROLLBACK_VERSION',
defaultValue: '',
description: '回滚时必须填写版本号,如:1.0.0-5-a1b2c3d'
)
}
// ================== 全局环境变量 ==================
environment {
APP_NAME = "cice-testing"
REGISTRY = "crpi-7amxwknx8zozpm09.cn-beijing.personal.cr.aliyuncs.com/sunlipeng"
IMAGE = "${REGISTRY}/${APP_NAME}"
BASE_VERSION = "1.0.0"
NAMESPACE = "default"
// Maven 本地仓库缓存,加速构建
MAVEN_CACHE_DIR = '/var/lib/jenkins/maven_cache'
}
// ================== 流水线全局选项 ==================
options {
timeout(time: 30, unit: 'MINUTES') // 防止流水线无限挂起
disableConcurrentBuilds() // 禁止并发构建
buildDiscarder(logRotator(numToKeepStr: '10')) // 保留最近 10 次构建
}
stages {
// ============================================
// 分支一:正常发布流程
// ============================================
stage('Normal Deploy Flow') {
when { expression { params.DEPLOY_TYPE == 'deploy' } }
stages {
// ---------- Git 拉取代码 ----------
stage('Git Pull') {
steps {
script {
git branch: 'master',
url: 'https://gitee.com/sun-lipeng123/cice-testing.git',
credentialsId: 'git-creds'
}
}
}
// ---------- 生成唯一版本号 ----------
stage('Generate Version') {
steps {
script {
def gitShortHash = sh(
script: 'git rev-parse --short HEAD',
returnStdout: true
).trim()
env.FINAL_TAG = "${BASE_VERSION}-${BUILD_NUMBER}-${gitShortHash}"
echo "✅ 生成新版本号: ${FINAL_TAG}"
}
}
}
// ---------- 编译 & 单元测试 ----------
stage('Build & Unit Test') {
steps {
sh """
mkdir -p ${MAVEN_CACHE_DIR}
mvn clean test package -Dmaven.repo.local=${MAVEN_CACHE_DIR}
"""
}
post {
success {
// 归档构建产物,便于回滚或下载
archiveArtifacts artifacts: 'target/*.jar', fingerprint: true
}
}
}
// ---------- SonarQube 代码扫描 ----------
stage('SonarQube Analysis') {
steps {
withSonarQubeEnv('sonarqube') {
sh """
mvn sonar:sonar \
-Dsonar.projectKey=${APP_NAME} \
-Dsonar.projectName=${APP_NAME} \
-Dsonar.sources=.
"""
}
// 等待 SonarQube 质量门禁结果
timeout(time: 3, unit: 'MINUTES') {
waitForQualityGate abortPipeline: false
}
}
}
// ---------- 人工审批 ----------
stage('Production Approval') {
steps {
script {
withCredentials([string(credentialsId: 'dingtalk-webhook', variable: 'TOKEN')]) {
def msg = """【上线审批】
项目:${APP_NAME}
版本:${FINAL_TAG}
构建号:${BUILD_NUMBER}
请登录 Jenkins 点击【确认部署】
构建地址:${BUILD_URL}"""
def json = '{"msgtype":"text","text":{"content":"' + msg.replaceAll("\n", "\\\\n") + '"}}'
sh "curl -s -X POST https://oapi.dingtalk.com/robot/send?access_token=\${TOKEN} -H 'Content-Type: application/json' -d '${json}'"
}
// 人工确认后才允许继续
input message: '确认将此版本部署到生产环境吗?', ok: '确认部署', submitter: 'admin'
}
}
}
// ---------- 构建并推送 Docker 镜像 ----------
stage('Docker Build & Push') {
steps {
withCredentials([usernamePassword(
credentialsId: 'docker-aliyun-creds',
usernameVariable: 'DOCKER_USER',
passwordVariable: 'DOCKER_PASS'
)]) {
sh """
echo \$DOCKER_PASS | docker login -u \$DOCKER_USER --password-stdin \$REGISTRY
docker build -t \${IMAGE}:\${FINAL_TAG} .
docker push \${IMAGE}:\${FINAL_TAG}
"""
}
}
}
// ---------- 部署到 Kubernetes ----------
stage('Deploy to K8s') {
steps {
withCredentials([file(credentialsId: 'kubeconfig-prod', variable: 'KUBECONFIG_FILE')]) {
sh """
export KUBECONFIG=\$KUBECONFIG_FILE
sed -i "s|image: .*|image: \${IMAGE}:\${FINAL_TAG}|g" k8s.yaml
kubectl apply -f k8s.yaml
# 确保 Pod 真正启动成功
kubectl rollout status deployment/jenkins-demo -n ${NAMESPACE} --timeout=300s
"""
}
}
}
}
}
// ============================================
// 分支二:版本回滚流程
// ============================================
stage('Rollback Flow') {
when { expression { params.DEPLOY_TYPE == 'rollback' } }
steps {
script {
if (params.ROLLBACK_VERSION == '') {
error "❌ 回滚模式下,必须填写 ROLLBACK_VERSION 参数!"
}
echo "🚀 开始回滚 ${APP_NAME} 到版本: ${params.ROLLBACK_VERSION}"
withCredentials([file(credentialsId: 'kubeconfig-prod', variable: 'KUBECONFIG_FILE')]) {
sh """
export KUBECONFIG=\$KUBECONFIG_FILE
sed -i "s|image: .*|image: \${IMAGE}:${params.ROLLBACK_VERSION}|g" k8s.yaml
kubectl apply -f k8s.yaml
kubectl rollout status deployment/jenkins-demo -n ${NAMESPACE} --timeout=300s
"""
}
}
}
}
}
// ================== 构建结果通知 ==================
post {
success {
script {
def actionType = params.DEPLOY_TYPE == 'rollback' ? "✅ 回滚成功" : "✅ 部署成功"
def targetVersion = params.DEPLOY_TYPE == 'rollback' ? params.ROLLBACK_VERSION : env.FINAL_TAG
withCredentials([string(credentialsId: 'dingtalk-webhook', variable: 'TOKEN')]) {
def msg = """${actionType}
项目:${APP_NAME}
目标版本:${targetVersion}
访问地址:http://<你的节点IP>:30080"""
def json = '{"msgtype":"text","text":{"content":"' + msg.replaceAll("\n", "\\\\n") + '"}}'
sh "curl -s -X POST https://oapi.dingtalk.com/robot/send?access_token=\${TOKEN} -H 'Content-Type: application/json' -d '${json}'"
}
}
}
failure {
script {
withCredentials([string(credentialsId: 'dingtalk-webhook', variable: 'TOKEN')]) {
def actionType = params.DEPLOY_TYPE == 'rollback' ? "❌ 回滚失败" : "❌ 构建/部署失败"
def msg = """${actionType}
项目:${APP_NAME}
构建地址:${BUILD_URL}
请及时排查问题!"""
def json = '{"msgtype":"text","text":{"content":"' + msg.replaceAll("\n", "\\\\n") + '"}}'
sh "curl -s -X POST https://oapi.dingtalk.com/robot/send?access_token=\${TOKEN} -H 'Content-Type: application/json' -d '${json}'"
}
}
}
}
}
五、启用 SonarQube Webhook(强烈推荐)
SonarQube 端配置
Administration → Configuration → Webhooks
新增 Webhook:
http://<jenkins-url>/sonarqube-webhook/
✅ 分析完成后 Sonar 主动通知 Jenkins
✅ 不再依赖 waitForQualityGate轮询
✅ 彻底解决 PENDING / 超时问题
六、总结
✅ 本文完整覆盖了:
- SonarQube 部署
- Jenkins 集成
- 自动化代码扫描
- 质量门禁
- 生产审批
- K8s 发布 & 回滚
📌 一句话总结
**没有 SonarQube 的 CI/CD,只是"能跑";
有了 SonarQube,才是"敢上线"。**