一、前言:为何需要 SonarQube?

在现代软件开发中,代码质量是项目成功与否的关键因素之一。一个充满技术债、Bug 和安全漏洞的项目,不仅难以维护,更会随着时间的推移变得愈发脆弱。虽然优秀的程序员会努力编写高质量的代码,但完全依赖人的自觉性和经验是不可靠的。

SonarQube 是一个业界领先的开源平台,用于持续检查代码质量。它通过静态代码分析,可以检测出代码中的 Bug、漏洞 (Vulnerabilities) 和坏味道 (Code Smells),并提供详细的报告和改进建议。将 SonarQube 集成到 CI/CD 流水线中,可以建立起自动化的代码质量门禁,确保不合规的代码无法进入生产环境,从而持续提升团队的整体代码质量。

本指南将详细介绍如何使用 Docker 部署 SonarQube,并将其与 GitLab CI、Maven 等工具链集成。

二、使用 Docker 安装 SonarQube

官方推荐使用 Docker 进行部署,方便快捷。

1. 版本选择

SonarQube 提供多个版本,主要区别在于功能和支持的语言:

  • Community Edition (社区版): 免费,功能基础,但足以满足大多数项目的需求。
  • Developer Edition (开发者版): 收费,增加了对分支分析、Pull Request 装饰等高级功能的支持。
  • Enterprise Edition (企业版): 功能最全,适用于大型企业。

注意:版本兼容性

  • SonarQube v8.9 支持 Node.js <= 15.x 和 JDK 8。
  • SonarQube v10+ 支持 Node.js 14.17+ 和 JDK 11 或 17。

2. 使用 Docker Compose 部署

官方提供了详细的 docker-compose.yml 示例:SonarSource Docker Examples

从 7.9 版本开始,SonarQube 内置了 H2 数据库用于评估,但生产环境强烈建议使用外部数据库(如 PostgreSQL),因为 H2 不支持扩展和升级。

以下是一个使用内置数据库的 docker-compose.yml 示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
version: "3"

services:
sonarqube:
image: sonarqube:10.0.0-community
container_name: sonarqube
ports:
- "9000:9000"
volumes:
- ./sonarqube_conf:/opt/sonarqube/conf
- ./sonarqube_data:/opt/sonarqube/data
- ./sonarqube_extensions:/opt/sonarqube/extensions
- ./sonarqube_logs:/opt/sonarqube/logs
environment:
# JVM 内存配置,根据服务器资源调整
- SONAR_ES_JAVA_OPTS=-Xmx512m -Xms512m

3. 常见启动问题

  • 权限问题: java.lang.IllegalStateException: Unable to access 'path.data'

    • 原因: Docker 容器内的用户没有权限写入挂载到宿主机的目录。
    • 解决: 给宿主机上的挂载目录赋予 777 权限:sudo chmod -R 777 ./sonarqube_*
  • Elasticsearch 限制: max file descriptors [4096] for elasticsearch process is too low

    • 原因: SonarQube 内置的 Elasticsearch 需要更高的系统资源限制。
    • 解决: 在宿主机上执行 sudo sysctl -w vm.max_map_count=262144

4. 访问和初始化

启动容器后,通过 http://<your-ip>:9000 访问 SonarQube 界面。

  • 默认管理员账号: admin
  • 默认密码: admin

首次登录后会强制要求修改密码。

5. 安装插件

SonarQube 拥有丰富的插件市场。以安装中文语言包为例:

  1. 登录后进入 Administration -> Marketplace
  2. 在搜索框中输入 Chinese,找到 Chinese Pack
  3. 点击 Install,等待安装完成。
  4. 根据提示重启 SonarQube 服务器。

三、使用 SonarScanner 分析代码

SonarScanner 是一个命令行的代码扫描工具,用于执行分析并将结果上传到 SonarQube 服务器。

1. 安装 SonarScanner

1
2
3
4
5
6
7
8
# 下载并解压
cd /usr/local/src/
curl -L https://binaries.sonarsource.com/Distribution/sonar-scanner-cli/sonar-scanner-cli-4.8.0.2856-linux.zip -o sonar-scanner.zip
unzip sonar-scanner.zip
mv sonar-scanner-* sonar-scanner

# 添加到系统路径 (可选,但推荐)
ln -s /usr/local/src/sonar-scanner/bin/sonar-scanner /usr/local/bin/sonar-scanner

2. 生成认证令牌 (Token)

为了安全,不应直接使用管理员账号密码。应为 CI/CD 创建专用的用户和令牌。

  1. 登录 SonarQube,进入 My Account -> Security
  2. 生成一个新的 Token,并妥善保管。这个 Token 只会显示一次

3. 执行扫描

在项目根目录下执行 sonar-scanner 命令。分析所需的参数可以通过命令行 -D 参数或配置文件 sonar-project.properties 提供。

1
2
3
4
5
sonar-scanner \
-Dsonar.projectKey=my-awesome-project \
-Dsonar.sources=. \
-Dsonar.host.url=http://localhost:9000 \
-Dsonar.token=your_generated_token
  • sonar.projectKey: 项目的唯一标识符。
  • sonar.sources: 要分析的源代码目录。
  • sonar.host.url: SonarQube 服务器地址。
  • sonar.token: 上一步生成的认证令牌。

四、与 CI/CD 集成

1. 集成 GitLab CI (JavaScript/TypeScript 项目)

对于前端项目,通常需要先运行 eslint 生成报告,再由 SonarScanner 读取。

.gitlab-ci.yml 示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
stages:
- check

sonarqube-check:
stage: check
image: node:16 # 使用包含 Node.js 的镜像
before_script:
# 安装 sonar-scanner
- curl -L https://binaries.sonarsource.com/Distribution/sonar-scanner-cli/sonar-scanner-cli-4.8.0.2856-linux.zip -o sonar-scanner.zip && unzip sonar-scanner.zip
- export PATH=$PATH:$(pwd)/sonar-scanner-4.8.0.2856-linux/bin
# 安装依赖并生成 eslint 报告
- npm install
- npm run lint -- -f json -o eslint_report.json || true
script:
- sonar-scanner
-Dsonar.projectKey=${CI_PROJECT_NAME}
-Dsonar.sources=.
-Dsonar.host.url=${SONAR_HOST_URL} # 在 GitLab CI/CD 变量中配置
-Dsonar.token=${SONAR_TOKEN} # 在 GitLab CI/CD 变量中配置
-Dsonar.eslint.reportPaths=eslint_report.json
only:
- master
- develop

2. 集成 Maven (Java 项目)

对于 Java 项目,使用 sonar-maven-plugin 是最方便的方式。

pom.xml 配置:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
<properties>
<!-- SonarQube 服务器地址和 Token,建议通过环境变量传入 -->
<sonar.host.url>${env.SONAR_HOST_URL}</sonar.host.url>
<sonar.login>${env.SONAR_TOKEN}</sonar.login>
<sonar.projectKey>${project.artifactId}</sonar.projectKey>
<!-- JaCoCo 用于生成测试覆盖率报告 -->
<sonar.coverage.jacoco.xmlReportPaths>${project.build.directory}/site/jacoco/jacoco.xml</sonar.coverage.jacoco.xmlReportPaths>
</properties>

<build>
<plugins>
<plugin>
<groupId>org.sonarsource.scanner.maven</groupId>
<artifactId>sonar-maven-plugin</artifactId>
<version>3.9.1.2184</version>
</plugin>
<plugin>
<groupId>org.jacoco</groupId>
<artifactId>jacoco-maven-plugin</artifactId>
<version>0.8.10</version>
<executions>
<execution>
<id>prepare-agent</id>
<goals><goal>prepare-agent</goal></goals>
</execution>
<execution>
<id>report</id>
<goals><goal>report</goal></goals>
</execution>
</executions>
</plugin>
</plugins>
</build>

在 CI/CD 中执行:

1
2
# -DskipTests 会跳过单元测试,但依然会生成覆盖率报告
mvn clean verify sonar:sonar -DskipTests

五、社区版实现多分支扫描

官方的社区版不支持多分支分析,但社区提供了解决方案。

方案一:使用 sonarqube-community-branch-plugin 插件 (推荐)

这是一个非常流行的开源插件,可以为社区版解锁分支分析功能。

  1. 下载插件: 从 GitHub Releases 下载与你的 SonarQube 版本兼容的 .jar 文件。
  2. 安装插件: 将 .jar 文件放入 SonarQube 的 extensions/plugins 目录。
  3. 配置启动参数: 修改 docker-compose.yml,为 SonarQube 添加 Java Agent 环境变量。
    1
    2
    3
    environment:
    - SONAR_WEB_JAVAADDITIONALOPTS=-javaagent:/opt/sonarqube/extensions/plugins/sonarqube-community-branch-plugin.jar=web
    - SONAR_CE_JAVAADDITIONALOPTS=-javaagent:/opt/sonarqube/extensions/plugins/sonarqube-community-branch-plugin.jar=ce
  4. 重启 SonarQube
  5. 在扫描时指定分支名:
    1
    2
    3
    sonar-scanner -Dsonar.branch.name=${CI_COMMIT_REF_NAME} # GitLab CI
    # 或
    mvn sonar:sonar -Dsonar.branch.name=${GIT_BRANCH} # Jenkins

方案二:动态修改 sonar.projectKey

这是一种“曲线救国”的方法。通过为每个分支生成一个唯一的 projectKey,让 SonarQube 将每个分支视为一个独立的项目。

1
2
3
4
5
# 在 CI/CD 脚本中
# 例如,使用 "项目名-分支名" 作为 projectKey
PROJECT_KEY="${CI_PROJECT_NAME}-${CI_COMMIT_REF_NAME}"

sonar-scanner -Dsonar.projectKey=${PROJECT_KEY} ...

缺点: 这种方式会使 SonarQube 实例中的项目数量激增,管理不便,且无法在同一个项目视图下对比不同分支的质量。

总结

将 SonarQube 集成到开发流程中,是保障和提升代码质量的有效手段。通过自动化分析和质量门禁,它可以帮助团队及早发现问题,培养良好的编码习惯,最终交付更健壮、更易于维护的软件产品。