本篇博客详细介绍 SonarQube 的部署和常用编程语言的应用,另外给出在 Kubernetes 中使用的方法。
SonarQube 是一个用于代码质量管理的开源平台,用于管理源代码的质量。同时 SonarQube 还对大量的持续集成工具提供了接口支持,可以很方便地在持续集成中使用 SonarQube。此外 SonarQube 的插件还可以对 Java 以外的其他编程语言提供支持,对国际化以及报告文档化也有良好的支持。
特性:
- 多语言的平台: 支持超过20种编程语言,包括Java、Python、C#、C/C++、JavaScript等常用语言
- 自定义规则: 用户可根据不同项目自定义Quality Profile以及Quality Gates
- 丰富的插件: SonarQube 拥有丰富的插件,从而拥有强大的可扩展性
- 持续集成: 通过对某项目的持续扫描,可以对该项目的代码质量做长期的把控,并且预防新增代码中的不严谨和冗余
- 质量门: 在扫描代码后可以通过对“质量门”的比对判定此次“构建”的结果是否通过,质量门可以由用户定义,由多维度判定是否通过
1
2
3
4
5
6
7
8
|
docker run -d \
--name postgresql \
--network=host \
-p 5423:5423 \
-e POSTGRES_PASSWORD=postgrs@2020 \
-e PGDATA=/var/lib/postgresql/data/pgdata \
-v /root/postgresql/data:/var/lib/postgresql/data \
postgres
|
创建 sonarqube 使用的数据库和用户
1
|
CREATE DATABASE sonarqube;
|
请注意,生产环境不要使用容器来运行 PostgreSQL,最好是创建 sonarqube 用户并赋予其相应权限,可以参考:https://docs.sonarqube.org/latest/setup/install-server/
1、创建存储卷
1
2
3
|
docker volume create --name sonarqube_data
docker volume create --name sonarqube_extensions
docker volume create --name sonarqube_logs
|
2、使用 PostgreSQL 启动 SonarQube 容器
1
2
3
4
5
6
7
8
9
10
|
docker run -d --name sonarqube \
--network=host \
-p 9000:9000 \
-e SONAR_JDBC_URL=jdbc:postgresql://10.20.0.18/sonarqube \
-e SONAR_JDBC_USERNAME=postgres \
-e SONAR_JDBC_PASSWORD=postgrs@2020 \
-v sonarqube_data:/opt/sonarqube/data \
-v sonarqube_extensions:/opt/sonarqube/extensions \
-v sonarqube_logs:/opt/sonarqube/logs \
sonarqube
|
SonarQube 默认用户名/密码为:admin/admin
创建一个外部使用的 Token:
1
|
Token 的生成方式:User > My Account > Security
|
scmp-sonarqube: 33f42c54b432a9124cb966f8b6dd06daeda6f12b
部署到 kubernetes 中的 yaml 文件为:
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
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
|
kind: Deployment
apiVersion: apps/v1
metadata:
name: sonarqube
namespace: sgt
labels:
app: sonarqube
spec:
replicas: 1
selector:
matchLabels:
app: sonarqube
template:
metadata:
labels:
app: sonarqube
spec:
volumes:
- name: sonarqube-data
persistentVolumeClaim:
claimName: sonarqube-data
- name: sonarqube-extensions
persistentVolumeClaim:
claimName: sonarqube-extensions
- name: sonarqube-logs
persistentVolumeClaim:
claimName: sonarqube-logs
containers:
- name: sonarqube
image: "sonarqube:latest"
env:
- name: SONAR_JDBC_URL
value: "jdbc:postgresql://10.20.0.18/sonarqube"
- name: SONAR_JDBC_USERNAME
value: "postgres"
- name: SONAR_JDBC_PASSWORD
value: "postgrs@2020"
resources:
limits:
cpu: "1"
memory: "2Gi"
requests:
cpu: "1"
memory: "2Gi"
ports:
- containerPort: 9000
name: sonarqube
volumeMounts:
- name: sonarqube-data
mountPath: /opt/sonarqube/data
subPath: data
- name: sonarqube-extensions
mountPath: /opt/sonarqube/extensions
subPath: extensions
- name: sonarqube-logs
mountPath: /opt/sonarqube/logs
subPath: logs
imagePullPolicy: IfNotPresent
livenessProbe:
httpGet:
path: /sessions/new
port: 9000
initialDelaySeconds: 600
imagePullSecrets:
- name: default-secret
securityContext:
fsGroup: 999
runAsUser: 999
terminationGracePeriodSeconds: 3600
---
apiVersion: v1
kind: Service
metadata:
labels:
app: sonarqube
name: sonarqube
namespace: sgt
spec:
type: ClusterIP
ports:
- name: sonarqube-port
port: 80
targetPort: 9000
selector:
app: sonarqube
|
注意,内存至少2Gi,initialDelaySeconds 设置为 300 或 600 或更大点,否则程序尚未启动,就被 kubernetes 健康检查杀死
必须使用三个 PVC 来分别挂载 data,extensions,logs目录,如果只使用一个 PVC,使用 subPath 来区别,ES 会报各种各样的错误。
还可以增加环境变量:SONARQUBE_WEB_JVM_OPTS,指定额外的 JVM 参数。虽然官网说 SONARQUBE_JDBC_USERNAME, SONARQUBE_JDBC_PASSWORD, and SONARQUBE_JDBC_URL 会在未来弃用,但在 SONAR_VERSION=8.2.0.32929 时,docker 容器启动时,使用的环境变量名称分别为:SONAR_JDBC_URL,SONAR_JDBC_USERNAME,SONAR_JDBC_PASSWORD;而使用 kubernetes 时,则需要使用带 QUBE 的环境变量名称,否则不会使用我们提供的 PostgreSQL,而是使用默认的 H2 数据库。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
2020-04-27T13:55:28.492308527Z Caused by: java.nio.file.AccessDeniedException: /opt/sonarqube/data/es6
2020-04-27T13:55:28.49231103Z at sun.nio.fs.UnixException.translateToIOException(Unknown Source) ~[?:?]
2020-04-27T13:55:28.49231393Z at sun.nio.fs.UnixException.rethrowAsIOException(Unknown Source) ~[?:?]
2020-04-27T13:55:28.492316385Z at sun.nio.fs.UnixException.rethrowAsIOException(Unknown Source) ~[?:?]
2020-04-27T13:55:28.492318764Z at sun.nio.fs.UnixFileSystemProvider.createDirectory(Unknown Source) ~[?:?]
2020-04-27T13:55:28.492321152Z at java.nio.file.Files.createDirectory(Unknown Source) ~[?:?]
2020-04-27T13:55:28.492323672Z at java.nio.file.Files.createAndCheckIsDirectory(Unknown Source) ~[?:?]
2020-04-27T13:55:28.492326051Z at java.nio.file.Files.createDirectories(Unknown Source) ~[?:?]
2020-04-27T13:55:28.492328386Z at org.elasticsearch.bootstrap.Security.ensureDirectoryExists(Security.java:413) ~[elasticsearch-6.8.0.jar:6.8.0]
2020-04-27T13:55:28.492330853Z at org.elasticsearch.bootstrap.FilePermissionUtils.addDirectoryPath(FilePermissionUtils.java:68) ~[elasticsearch-6.8.0.jar:6.8.0]
2020-04-27T13:55:28.492333438Z at org.elasticsearch.bootstrap.Security.addFilePermissions(Security.java:299) ~[elasticsearch-6.8.0.jar:6.8.0]
2020-04-27T13:55:28.492339522Z at org.elasticsearch.bootstrap.Security.createPermissions(Security.java:254) ~[elasticsearch-6.8.0.jar:6.8.0]
2020-04-27T13:55:28.492342118Z at org.elasticsearch.bootstrap.Security.configure(Security.java:123) ~[elasticsearch-6.8.0.jar:6.8.0]
2020-04-27T13:55:28.49234464Z at org.elasticsearch.bootstrap.Bootstrap.setup(Bootstrap.java:207) ~[elasticsearch-6.8.0.jar:6.8.0]
2020-04-27T13:55:28.492347213Z at org.elasticsearch.bootstrap.Bootstrap.init(Bootstrap.java:333) ~[elasticsearch-6.8.0.jar:6.8.0]
2020-04-27T13:55:28.492349763Z at org.elasticsearch.bootstrap.Elasticsearch.init(Elasticsearch.java:159) ~[elasticsearch-6.8.0.jar:6.8.0]
2020-04-27T13:55:28.492352443Z ... 6 more
2020-04-27T13:55:28.614501888Z 2020.04.27 13:55:28 WARN app[][o.s.a.p.AbstractManagedProcess] Process exited with exit value [es]: 1
2020-04-27T13:55:28.614954443Z 2020.04.27 13:55:28 INFO app[][o.s.a.SchedulerImpl] Process[es] is stopped
2020-04-27T13:55:28.615619259Z 2020.04.27 13:55:28 INFO app[][o.s.a.SchedulerImpl] SonarQube is stopped
|
解决:在 sonarqube pod 中加入如下内容:(注意是 Pod 级别的)
1
2
3
|
securityContext:
fsGroup: 999
runAsUser: 999
|
ES 不能以 root 用户启动,sonarqube 用户在容器内的 uid 和 gid 是:999:999
如果 ES 索引报错,可以删除 /opt/sonarqube/data 目录下 的 es6 目录,然后重启 SonarQube
参考资料:
https://blog.csdn.net/qq_40460909/article/details/102797137
https://gitlab.com/afireinside/kubernetes-sonarqube
注意,SonarQube 官方文档一直强调不能在多个 SonarQube 实例上应用相同的存储卷
1、在 ~/.m2/ 目录下创建 settings.xml 文件,内容如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
<settings>
<pluginGroups>
<pluginGroup>org.sonarsource.scanner.maven</pluginGroup>
</pluginGroups>
<profiles>
<profile>
<id>sonar</id>
<activation>
<activeByDefault>true</activeByDefault>
</activation>
<properties>
<!-- Optional URL to server. Default value is http://localhost:9000 -->
<sonar.host.url>http://10.20.13.120:9000</sonar.host.url>
</properties>
</profile>
</profiles>
</settings>
|
2、运行命令进行代码检查
1
2
|
maven clean install
maven sonar:sonar
|
结果报告:http://10.20.13.120:9000/dashboard?id=com.scmp:scmp-notify(组件 Key)
http://10.20.13.120:9000/api/ce/task?id=AXGmIOnNdU_TwfUPe-cA(任务 id)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
{
task: {
id: "AXGmIOnNdU_TwfUPe-cA",
type: "REPORT",
componentId: "AXGmDlrMdU_TwfUPe-bz",
componentKey: "com.scmp:scmp-notify",
componentName: "scmp-notify",
componentQualifier: "TRK",
analysisId: "AXGmIPDs509z86A1PbWI",
status: "SUCCESS",
submittedAt: "2020-04-23T08:21:47+0000",
startedAt: "2020-04-23T08:21:48+0000",
executedAt: "2020-04-23T08:21:50+0000",
executionTimeMs: 1413,
logs: false,
hasScannerContext: true,
organization: "default-organization",
warningCount: 0,
warnings: [ ]
}
}
|
1、在 ~/.gradle/gradle.properties 文件中加入如下内容:
1
2
3
4
5
|
# gradle.properties
systemProp.sonar.host.url=http://10.20.0.18:9000
#----- Token generated from an account with 'publish analysis' permission
systemProp.sonar.login=33f42c54b432a9124cb966f8b6dd06daeda6f12b
|
Token 的生成方式:User > My Account > Security
2、在项目的 build.gradle 目录中加入如下内容:
1
2
3
|
plugins {
id "org.sonarqube" version "2.7"
}
|
3、运行命令进行代码检查
1
|
gradle sonarqube -Dsonar.projectName=scmp-gradle(项目在 SonarQube 中的项目名称) -Dsonar.projectKey=scmp-gradle(在 SonarQube 中的访问地址标识)
|
也可以直接在 build.gradle 中加入如下内容:
1
2
3
4
5
6
7
|
sonarqube {
properties {
property "sonar.sourceEncoding", "UTF-8"
property "sonar.projectKey", "java-spring-boot-gradle" // 项目 ID,也即 SonarQube 中的项目路径
property "sonar.projectName", "java-spring-boot-gradle" // 项目在 SonarQube 中的名称
}
}
|
然后直接使用 gradle sonarqube
命令进行代码静态检测
1、安装 sonar-scanner
1)下载适合所有平台的安装包(实际使用时使用 Linux 系统下的安装包)
2)添加 bin 到环境变量中
1
2
|
export SONAR_SCANNER_HOME="/Users/xdhuxc/Applications/sonar-scanner-4.2.0"
export PATH="${PATH}:${SONAR_SCANNER_HOME}/bin/"
|
2、运行代码检测命令
1
2
3
4
5
|
sonar-scanner \
-Dsonar.projectKey=scmp-cicd \
-Dsonar.sources=. \
-Dsonar.host.url=http://10.20.0.18:9000 \
-Dsonar.login=33f42c54b432a9124cb966f8b6dd06daeda6f12b
|
在 SonarQube 中的访问地址为:http://10.20.0.18:9000/dashboard?id=scmp-cicd(为 sonar.projectKey 的值)
1、将社区版的 C/C++ 插件下载到 SONARQUBE_HOME/extensions/plugins/
目录下,下载地址为:
1
|
https://github.com/SonarOpenCommunity/sonar-cxx/releases
|
然后重启 SonarQube
2、从 SonarCloud
获取 build-wrapper
,原来的从 SonarQube
服务器获取的方式已经失效
1
2
3
4
5
|
wget https://sonarcloud.io/static/cpp/build-wrapper-linux-x86.zip(Linux 系统)
或
wget https://sonarcloud.io/static/cpp/build-wrapper-macosx-x86.zip(MacOS 系统)
或
wget https://sonarcloud.io/static/cpp/build-wrapper-win-x86.zip(Windows 系统)
|
3、将 build-wrapper-linux-x86
加入到 PATH
路径下,并重命名为:build-wrapper
4、执行如下命令进行代码检查:
1
2
3
4
5
6
7
8
|
build-wrapper --out-dir build_wrapper_output_directory make clean all(Linux 或 MacOS)
sonar-scanner \
-Dsonar.sourceEncoding=UTF-8 \
-Dsonar.projectKey=scmp-cxx-project \
-Dsonar.projectName=scmp-cxx-project \
-Dsonar.sources=. \
-Dsonar.host.url=http://10.20.0.18:9000 \
-Dsonar.login=33f42c54b432a9124cb966f8b6dd06daeda6f12b
|
此处具体项目的使用方式可参考:
https://github.com/SonarSource/sonar-scanning-examples/tree/master/sonarqube-scanner-build-wrapper-linux
参考资料:
https://github.com/SonarOpenCommunity/sonar-cxx
https://github.com/SonarOpenCommunity/sonar-cxx/wiki/Installation
https://github.com/SonarOpenCommunity/sonar-cxx/wiki
1、创建 token
2、下载对应平台的二进制文件
1
|
https://binaries.sonarsource.com/Distribution/sonar-scanner-cli/sonar-scanner-cli-4.2.0.1873.zip
|
解压后将 sonar-scanner-cli-4.2.0.1873/bin 目录加入到 PATH 环境变量中
3、运行测试命令
在项目目录下,运行如下命令:
1
2
3
4
5
|
sonar-scanner \
-Dsonar.projectKey=scmp-cicd \
-Dsonar.sources=. \
-Dsonar.host.url=http://10.20.0.18:9000 \
-Dsonar.login=33f42c54b432a9124cb966f8b6dd06daeda6f12b
|
在 SonarQube 中的访问地址为:http://10.20.0.18:9000/dashboard?id=scmp-cicd(为 sonar.projectKey 的值)
scmp-sonarqube: 33f42c54b432a9124cb966f8b6dd06daeda6f12b
常用参数含义:
- sonar.projectKey:项目唯一标识,将作为 dashboard URL 中的参数
- sonar.projectName:项目在 SonarQube 管理界面中的名称,最好取一个独特的唯一标识
- sonar.host.url:sonarqube 服务器的地址
- sonar.login:登录 sonarqube 服务器的 token,可在
User > My Account > Security
中创建,可以多个项目通用。
- sonar.sources:指定源代码目录
- sonar.sourceEncoding:源代码编码格式
- sonar.exclusions/sonar.test.exclusions:排除源代码目录下的目录和文件,支持通配符,可以使用分号分隔多个要排除的目录或文件模式
- sonar.inclusions/sonar.test.inclusions:只分析源代码目录中符合此通配符的文件和目录
- sonar.verbose=true:显示详细的 debug 日志信息
其他参数可参考:
https://docs.sonarqube.org/latest/analysis/analysis-parameters/
1、sonar.exclusions 的表达式在 zsh 模式下会出问题,返回:
1
|
zsh: no matches found: -Dsonar.exclusions=venv/**,templates/*.html
|
zsh 自作聪明地给解析这个表达式了,错误时直接返回,而不是把命令参数直接传递给 sonar-scanner,切换到 bash 下执行。
2、-Dsonar.exclusions=venv/**,templates/*.html
部分,可以使用逗号分隔多个目录或文件模式,但是逗号之间不能有空格,有空格时会报错:
1
2
3
4
5
6
7
8
9
|
ERROR: Unrecognized option: templates/cloudfront.html
INFO:
INFO: usage: sonar-scanner [options]
INFO:
INFO: Options:
INFO: -D,--define <arg> Define property
INFO: -h,--help Display help information
INFO: -v,--version Display version information
INFO: -X,--debug Produce execution debug output
|
3、目录排除写法:
1
2
|
venv/**(或者vendor/**):排除 venv 目录,位于 venv 目录下的文件均不进行扫描
**/*.html(或者**/*.css):排除所有目录下后缀为 .html 的文件
|
4、对于 golang,python 等项目的 vendor 或 venv 等目录,一定要使用 sonar.exclusions 参数排除掉该目录,否则 SonarQube 会将这些目录中的代码也进行分析。
1、SonarQube 可以配置 webhook,当项目分析完毕时,可以通过 POST 请求发送数据到 URL。详细使用方法参考:http://10.20.0.18:9000/admin/webhooks
2、SonarQube 从 v7.9 之后,不再支持 MySQL,仅支持 PostgreSQL,Oracle,Microsoft SQL Server,具体原因见:https://community.sonarsource.com/t/end-of-life-of-mysql-support/8667
1、sonarqube 报如下警告:
1
|
SCM provider autodetection failed. Please use "sonar.scm.provider" to define SCM of your project, or disable the SCM Sensor in the project settings.
|
增加 sonar.scm.disabled=true
配置,禁用 SCM。
SonarQube 官方常用编程语言静态代码检查示例:https://github.com/SonarSource/sonar-scanning-examples
https://www.zhang21.cn/2019/02/22/SonarQube/