Skip to content

Jenkins&Sonarqube代码质量平台

一、SonarQube在jenkins中应用

将SonarQube集成到Jenkins中,可以在CI/CD流水线中自动执行代码质量分析

1.1 JenkinsPipeline集成

1.1.1 命令行方式

在Jenkinsfile中使用命令行方式调用SonarScanner

TIP

需要将sonar的token凭证存储到Jenkins 凭据中

  • 创建sonar 的token凭证
    • 进入Jenkins管理界面 > 系统配置 > credentials > 全局凭证 > + Add Credentials
    • 选择凭证类型 > Secret text > 范围-全局 > 贴入Secret token > 添加一个描述

  • 生成jenkins 凭证的流水线语法

groovy
withCredentials([string(credentialsId: '3ce7da72-90ae-4af9-a2e4-2b8eb83ec0df', variable: 'sonar_token')]) {
    // some block
}

Maven项目示例

groovy
pipeline {
    agent any
    stages{
        stage('checkout'){
            steps{
                script {
                checkout scmGit(branches: [[name: '*/main']], extensions: [], userRemoteConfigs: [[credentialsId: 'a11aa99b-7bba-48fd-bd01-46b68732578a', url: 'http://code.srebro.cn/opforge/maven-demo.git']])
                       }
                }
        }
        stage('sonarqube analysis'){
            steps{
				script{
				withCredentials([string(credentialsId: '3ce7da72-90ae-4af9-a2e4-2b8eb83ec0df', variable: 'sonar_token')]) {
                sh """
                    source /etc/profile > /dev/null 2>&1
                    sonar-scanner \
                    -Dsonar.token=${sonar_token} \
					-Dsonar.projectKey=my-project-1 \
					-Dsonar.projectName=my-project-1 \
					-Dsonar.sourceEncoding=UTF-8 \
					-Dsonar.sources=./src/main/java \
					-Dsonar.language=java \
                    -Dsonar.host.url=http://172.22.33.207:9000 
                """				
				}
				}
            }
        }
    }
}

1.1.2 Jenkins插件方式

使用SonarQube Scanner插件可以更方便地在Jenkins中集成SonarQube:

前置条件

  1. 在Jenkins中安装SonarQube Scanner插件

  1. Jenkins中配置SonarQube内容:
    • 进入Jenkins管理界面 > 系统配置 > SonarQube servers > Add SonarQube
    • 添加SonarQube服务器信息(URL、认证令牌等)

  1. 生成withSonarQubeEnv流水线语法

  1. SonarQube中配置webhook

需要在sonarqube的web页面配置webhook ,名称自定义,url规则如下:其中sonarqube-webhook是固定的, 无需密码

Details

jenkins 在安装好SonarQube Scanner插件 之后,会提供一个webhook 接口,http://jenkinsip:端口/sonarqube-webhook/ , 比如: http://172.22.33.201:8080/sonarqube-webhook/

Maven项目示例:

使用withSonarQubeEnv DSL引入在Jenkins中配置的sonar环境

groovy
pipeline {
    agent any
    stages{
        stage('checkout'){
            steps{
                script {
                checkout scmGit(branches: [[name: '*/main']], extensions: [], userRemoteConfigs: [[credentialsId: 'a11aa99b-7bba-48fd-bd01-46b68732578a', url: 'http://code.srebro.cn/opforge/maven-demo.git']])
                       }
                }
        }
        stage('maven构建'){
            steps{
                script {
                    sh """
					source /etc/profile > /dev/null 2>&1
                    mvn clean  package
                    """						
                       }
                }
        }		
        stage('sonarqube扫描'){
            steps{
				script{
				withSonarQubeEnv(credentialsId: '3ce7da72-90ae-4af9-a2e4-2b8eb83ec0df') {
                sh """
                    source /etc/profile > /dev/null 2>&1
                    sonar-scanner \
                    -Dsonar.token=${SONAR_AUTH_TOKEN} \
					-Dsonar.projectKey=my-project-2 \
					-Dsonar.projectName=my-project-2 \
					-Dsonar.sourceEncoding=UTF-8 \
					-Dsonar.sources=./src/main/java \
					-Dsonar.language=java \
                    -Dsonar.host.url=${SONAR_HOST_URL}
                """				
				}
				}
				
				script{
					def qg = waitForQualityGate()
						if (qg.status != 'OK') {
						error "未通过Sonarqube的代码质量阈检查,请及时修改!failure: ${qg.status} Sonar检测报告:http://172.22.33.207:9000/dashboard?id=my-project-2"
					   }
				}				
				
            }
        }
    }
	
    post {
        always {
            // 清理工作区
            cleanWs()
        }
    }
}

1.1.3 指定sonarqube 扫描在docker-slave节点上运行

Readme

在docker-slave节点上构建 需要使用到Docker-plugin 插件,docker-slave及使用方法不在此讨论,后续单独讨论

  • 编写Jenkins-slave Dockerfile文件

Readme

  • 基础镜像来源于jenkins/inbound-agent:latest-jdk17,此镜像自带jdk17,此镜像自带jdk17,官方Dockerfile: https://github.com/jenkinsci/docker-agent/blob/master/debian/Dockerfile
  • jenkins slave节点通过JNLP 的方式连接jenkins master, 将自动配置代理的namesecret,不需要对容器进行任何特殊配置。
  • 镜像中封装了常见的工具,可自行选择
yaml
# 基于Jenkins inbound-agent的定制镜像
FROM jenkins/inbound-agent:latest-jdk17

# 添加镜像信息
LABEL maintainer="srebro"
LABEL description="inbound-agent:latest-jdk17-debian for sonar-scanner-cli:7.1.0.4889"
LABEL version="1.0"

# 切换到root用户进行安装配置
USER root

# 设置环境变量
ENV TZ=Asia/Shanghai \
    LANG=C.UTF-8 \
    LANGUAGE=C.UTF-8 \
    LC_ALL=C.UTF-8 \
    SONAR_SCANNER_HOME=/opt/sonar-scanner \
    SONAR_SCANNER_VERSION=7.1.0.4889

# 配置系统设置和安装必要工具
RUN set -eux; \
    # 更换Debian源为腾讯云镜像
    sed -Ei "s/(deb|security).debian.org/mirrors.cloud.tencent.com/g" /etc/apt/sources.list.d/debian.sources && \
    # 配置时区
    ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && \
    echo $TZ > /etc/timezone && \
    # 更新包列表并安装必要工具
    apt-get update && \
    apt-get install -y \
        bash-completion \
        bc \
        cifs-utils \
        curl \
        dnsutils \
        g++ \
        gcc \
        git \
        git-lfs \
        htop \
        iftop \
        iotop \
        jq \
        lrzsz \
        nmap \
        netcat-openbsd \
        nethogs \
        net-tools \
        ntpdate \
        openssh-server \
        psmisc \
        sysstat \
        tar \
        telnet \
        tzdata \
        unzip \
        vim \
        wget && \
    # 安装SonarScanner CLI 7.1.0.4889
    mkdir -p ${SONAR_SCANNER_HOME} && \
    wget  https://cnb.cool/srebro/docker-images/-/releases/download/V1.0.0/sonar-scanner-cli-${SONAR_SCANNER_VERSION}-linux-x64.zip -P /tmp && \
    unzip /tmp/sonar-scanner-cli-${SONAR_SCANNER_VERSION}-linux-x64.zip -d /tmp && \
    mv /tmp/sonar-scanner-${SONAR_SCANNER_VERSION}-linux-x64/* ${SONAR_SCANNER_HOME}/ && \
    rm -rf /tmp/sonar-scanner-cli-${SONAR_SCANNER_VERSION}-linux-x64.zip /tmp/sonar-scanner-${SONAR_SCANNER_VERSION}-linux-x64/ && \
    ln -s ${SONAR_SCANNER_HOME}/bin/sonar-scanner /usr/bin/sonar-scanner && \
    # 清理apt缓存
    apt-get clean && \
    rm -rf /var/lib/apt/lists/*

# 配置sonar-scanner默认属性
RUN echo "# Default SonarQube Scanner settings" > ${SONAR_SCANNER_HOME}/conf/sonar-scanner.properties && \
    echo "sonar.sourceEncoding=UTF-8" >> ${SONAR_SCANNER_HOME}/conf/sonar-scanner.properties && \
    echo "sonar.verbose=false" >> ${SONAR_SCANNER_HOME}/conf/sonar-scanner.properties

# 切换回jenkins用户以提高安全性
USER jenkins

# 设置jenkins用户的SonarScanner环境变量
ENV PATH="${SONAR_SCANNER_HOME}/bin:${PATH}"
bash
docker build -t docker build -t docker.cnb.cool/srebro/docker-images/jenkins-slave:sonar-scanner-cli-7.1.0.4889 -f Dockerfile-sonar-scanner-cli-7.1.0.4889 .
  • Docker Agent templates

    • 进入Jenkins管理界面 > 系统配置 > Clouds
    • 选择已有的cloud云节点 > configure > Docker Agent templates > 进度条拖到最下面 > Add Docker Template

Readme

  • 配置jenkins workspace共享目录,方便在不同的stage 阶段,能共享workspace 目录 type=bind,source=/home/application/jenkins/workspace,target=/home/jenkins/workspace

  • 创建pipeline脚本

groovy
pipeline {
	agent { label 'sonar-scanner-cli-7.1.0.4889' }
    stages {

        stage('拉取代码') {
            steps {
			script {
                checkout scmGit(branches: [[name: '*/main']], extensions: [], userRemoteConfigs: [[credentialsId: 'a11aa99b-7bba-48fd-bd01-46b68732578a', url: 'http://code.srebro.cn/opforge/maven-demo.git']])
				}
            }
        }
        stage('sonarqube扫描'){
            steps{
				script{
				withSonarQubeEnv(credentialsId: '3ce7da72-90ae-4af9-a2e4-2b8eb83ec0df') {
                sh """
                    sonar-scanner \
                    -Dsonar.token=${SONAR_AUTH_TOKEN} \
					-Dsonar.projectKey=my-project-2 \
					-Dsonar.projectName=my-project-2 \
					-Dsonar.sourceEncoding=UTF-8 \
					-Dsonar.sources=./src/main/java \
					-Dsonar.language=java \
                    -Dsonar.host.url=${SONAR_HOST_URL}
                """				
				}
				}
				
				script{
					def qg = waitForQualityGate()
						if (qg.status != 'OK') {
						error "未通过Sonarqube的代码质量阈检查,请及时修改!failure: ${qg.status} Sonar检测报告:http://172.22.33.207:9000/dashboard?id=my-project-2"
					   }
				}				
				
            }
        }			
    }
}
  • 构建并查看结果

二、sonar日常使用实践

2.1 规则的禁用与启用

SonarQube提供了丰富的代码质量规则,但并非所有规则都适用于每个项目。可以根据项目需求禁用或启用特定规则:

全局规则管理

  1. 登录SonarQube管理员账号
  2. 进入"Rules"页面(顶部菜单)
  3. 使用过滤器找到需要修改的规则
  4. 点击规则名称进入详情页
  5. 点击右上角的"Activate"或"Deactivate"按钮

项目级规则管理

  1. 进入项目页面
  2. 点击"Project Settings"
  3. 选择"Quality Profiles"
  4. 为项目选择适合的质量配置文件或创建新的配置文件
  5. 在配置文件中启用或禁用特定规则

在代码中忽略特定规则

java
// 在Java代码中忽略特定规则
@SuppressWarnings("squid:S1234") // 忽略规则S1234
public void someMethod() {
    // 代码实现
}
javascript
// 在JavaScript代码中忽略特定规则
// eslint-disable-next-line sonarjs/no-unused-collection
const unusedArray = [];

2.2 代码质量阈的配置

质量阈(Quality Gate)是SonarQube中的一个重要概念,用于定义代码必须满足的质量标准。如果代码不满足这些标准,可以在CI/CD流水线中阻止代码合并或部署。

配置质量阈

  1. 登录SonarQube管理员账号
  2. 进入"Quality Gates"页面
  3. 创建新的质量阈或编辑现有质量阈
  4. 添加条件,例如:
    • 代码覆盖率 > 80%
    • 新增代码中的Bug数量 = 0
    • 技术债务比率 < 5%
    • 重复代码 < 3%
  5. 设置为默认质量阈或将其应用于特定项目

在项目中应用质量阈

  1. 进入项目页面
  2. 点击"Project Settings"
  3. 选择"Quality Gates"
  4. 选择要应用的质量阈

2.3 代码覆盖率统计

代码覆盖率是衡量测试质量的重要指标,SonarQube可以集成各种测试覆盖率工具的报告:

Java项目(JaCoCo)

xml
<!-- pom.xml -->
<plugin>
    <groupId>org.jacoco</groupId>
    <artifactId>jacoco-maven-plugin</artifactId>
    <version>0.8.7</version>
    <executions>
        <execution>
            <goals>
                <goal>prepare-agent</goal>
            </goals>
        </execution>
        <execution>
            <id>report</id>
            <phase>test</phase>
            <goals>
                <goal>report</goal>
            </goals>
        </execution>
    </executions>
</plugin>

在SonarQube分析时添加参数:

bash
mvn sonar:sonar -Dsonar.coverage.jacoco.xmlReportPaths=target/site/jacoco/jacoco.xml

JavaScript项目(Istanbul/NYC)

bash
# 生成覆盖率报告
npm test -- --coverage

# 在SonarQube分析时添加参数
sonar-scanner -Dsonar.javascript.lcov.reportPaths=coverage/lcov.info

Go项目

bash
# 生成覆盖率报告
go test -coverprofile=coverage.out ./...

# 在SonarQube分析时添加参数
sonar-scanner -Dsonar.go.coverage.reportPaths=coverage.out

2.4 多分支代码扫描

SonarQube企业版和开发者版支持多分支代码扫描,可以为不同的分支创建独立的分析结果。 社区版默认是不支持多分支代码扫描,需要安装插件并配置才能实现

SonarQube社区版本配置多分支扫描




👇 安装步骤

第一步:https://github.com/mc1arke/sonarqube-community-branch-plugin/releases 页面对应的插件下载到 extensions/plugins/目录。


第二步: 修改 sonarqube 的/opt/sonarqube/conf/sonar.properties 配置文件,注意⚠️${version} 需要替换成下载的版本号。

sonar.web.javaAdditionalOpts=-javaagent:./extensions/plugins/sonarqube-community-branch-plugin-${version}.jar=web

sonar.ce.javaAdditionalOpts=-javaagent:./extensions/plugins/sonarqube-community-branch-plugin-${version}.jar=ce


第三步:https://github.com/mc1arke/sonarqube-community-branch-plugin/releases 页面对应 sonarqube-webapp.zip 替换 sonarqube 中 /opt/sonarqube/web 的前端代码


第四步: 重启 sonarqube 服务


SonarQube25.4.0 社区版容器化部署方式

WARNING

为了配置文件持久化,可以先docker run 之后
使用docker cp sonarqube:/opt/sonarqube/conf/sonar.properties /home/application/sonarqube/sonarqube_conf

把配置文件拷贝到本地,再进行修改配置,挂载到容器中。

💡扫描时,除了必须要的参数以外,还需要增加 –Dsonar.branch.name= 参数💡

yaml
services:
  sonarqube:
    image: docker.cnb.cool/srebro/docker-images-chrom/sonarqube:25.4.0.105899-community
    #image: sonarqube:25.4.0-community
    hostname: sonarqube
    container_name: sonarqube
    depends_on:
      - db
    environment:
      SONAR_JDBC_URL: jdbc:postgresql://db:5432/sonar
      SONAR_JDBC_USERNAME: sonar
      SONAR_JDBC_PASSWORD: sonar
    volumes:
      - /home/application/sonarqube/sonarqube_web:/opt/sonarqube/web    #web前端持久化
      - /home/application/sonarqube/sonarqube_conf:/opt/sonarqube/conf  #配置文件持久化
      - /home/application/sonarqube/sonarqube_extensions:/opt/sonarqube/extensions  # 插件扩展持久化
      - /home/application/sonarqube/sonarqube_logs:/opt/sonarqube/logs  # 日志持久化
      - /home/application/sonarqube/sonarqube_data:/opt/sonarqube/data  # 数据持久化
    ports:
      - "9000:9000"
  db:
    image: docker.cnb.cool/srebro/docker-images-chrom/postgres:17
    #image: postgres:17
    hostname: postgresql
    container_name: postgresql
    environment:
      POSTGRES_USER: sonar
      POSTGRES_PASSWORD: sonar
      POSTGRES_DB: sonar
    volumes:
      - /home/application/sonarqube/pgdata:/var/lib/postgresql/data
    ports:
      - "5432:5432"
bash
sonar-scanner -Dsonar.projectKey=test -Dsonar.projectName=test -Dsonar.host.url=http://172.22.33.207:9000 -Dsonar.sourceEncoding=UTF-8 -Dsonar.verbose=false -Dsonar.token=sqa_454efabf263d0aeb867f0e8034c6407938f89d91 -Dsonar.sources=./src/main/java -Dsonar.language=java
bash
sonar-scanner -Dsonar.projectKey=test -Dsonar.projectName=test -Dsonar.host.url=http://172.22.33.207:9000 -Dsonar.sourceEncoding=UTF-8 -Dsonar.verbose=false -Dsonar.token=sqa_454efabf263d0aeb867f0e8034c6407938f89d91 -Dsonar.sources=./src/main/java -Dsonar.language=java -Dsonar.branch.name=dev


  • 多分支代码扫描效果

2.5 扫描结果关联commitid

将SonarQube扫描结果与Git提交关联,可以更好地追踪代码质量变化:

配置Git信息

bash
sonar-scanner \
  -Dsonar.projectKey=my-project \
  -Dsonar.sources=. \
  -Dsonar.scm.provider=git \
  -Dsonar.scm.revision=$(git rev-parse HEAD) \
  -Dsonar.scm.url=$(git config --get remote.origin.url)

在Jenkins Pipeline中使用

groovy
stage('SonarQube Analysis') {
    steps {
        withSonarQubeEnv('SonarQube') {
            sh """
                sonar-scanner \
                  -Dsonar.projectKey=my-project \
                  -Dsonar.sources=. \
                  -Dsonar.scm.provider=git \
                  -Dsonar.scm.revision=${env.GIT_COMMIT} \
                  -Dsonar.scm.url=${env.GIT_URL}
            """
        }
    }
}

2.6 控制流水线是否跳过sonarscan

使用环境变量控制

groovy
pipeline {
    agent any
	
    parameters {choice choices: ['false', 'true'], description: '是否跳过sonar扫描', name: 'skipSonar'}
	
    stages{
        stage('checkout'){
            steps{
                script {
                checkout scmGit(branches: [[name: '*/main']], extensions: [], userRemoteConfigs: [[credentialsId: 'a11aa99b-7bba-48fd-bd01-46b68732578a', url: 'http://code.srebro.cn/opforge/maven-demo.git']])
                       }
                }
        }
        stage('maven构建'){
            steps{
                script {
                    sh """
					source /etc/profile > /dev/null 2>&1
                    mvn clean  package
                    """						
                       }
                }
        }		
        stage('sonarqube扫描'){
            when {
                environment name: 'skipSonar', value: 'false'
            }		
            steps{
				script{
				withSonarQubeEnv(credentialsId: '3ce7da72-90ae-4af9-a2e4-2b8eb83ec0df') {
                sh """
                    source /etc/profile > /dev/null 2>&1
                    sonar-scanner \
                    -Dsonar.token=${SONAR_AUTH_TOKEN} \
					-Dsonar.projectKey=my-project-2 \
					-Dsonar.projectName=my-project-2 \
					-Dsonar.sourceEncoding=UTF-8 \
					-Dsonar.sources=./src/main/java \
					-Dsonar.language=java \
                    -Dsonar.host.url=${SONAR_HOST_URL}
                """				
				}
				}
				
				script{
					def qg = waitForQualityGate()
						if (qg.status != 'OK') {
						error "未通过Sonarqube的代码质量阈检查,请及时修改!failure: ${qg.status} Sonar检测报告:http://172.22.33.207:9000/dashboard?id=my-project-2"
					   }
				}
            }
        }
    }
	
    post {
        always {
            // 清理工作区
            cleanWs()
        }
    }
}

最近更新

采用 CC BY-NC-ND 4.0 协议,完整转载请注明来自 运维小弟