SonarQube 简介
所谓sonarqube 就是代码质量扫描工具。
官网:
https://www.sonarsource.com/sonarqube/
在个人开发学习中用处不大, 我草, 我的代码质量这么高需要这玩意?
但是在公司项目中, 这个可是必须的, 你永远都不知道你的队友写了什么进git 仓库
SonarScanner 简介
至于SanarScanner 就是1个本地工具, 用于把你的代码push 到SonarQube server 用于分析
参考:
https://docs.sonarsource.com/sonarqube/latest/analyzing-source-code/scanners/sonarscanner/
搭建本地 SonarQube 方案
由于SonarQube 企业版是收费的, GCP上也没有SonarQube 的托管服务。
所以建议还是自己基于SonarQube 社区版搭建1个服务
准备1个有docker 环境的linux server
这个例子是1个WSL2 server
gateman@DESKTOP-UIU9RFJ:~$ neofetch
.-/+oossssoo+/-. gateman@DESKTOP-UIU9RFJ
`:+ssssssssssssssssss+:` -----------------------
-+ssssssssssssssssssyyssss+- OS: Ubuntu 20.04.2 LTS on Windows 10 x86_64
.ossssssssssssssssssdMMMNysssso. Kernel: 5.10.16.3-microsoft-standard-WSL2
/ssssssssssshdmmNNmmyNMMMMhssssss/ Uptime: 24 days, 11 hours, 47 mins
+ssssssssshmydMMMMMMMNddddyssssssss+ Packages: 807 (dpkg)
/sssssssshNMMMyhhyyyyhmNMMMNhssssssss/ Shell: bash 5.0.17
.ssssssssdMMMNhsssssssssshNMMMdssssssss. Terminal: /dev/pts/3
+sssshhhyNMMNyssssssssssssyNMMMysssssss+ CPU: Intel i5-7260U (4) @ 2.207GHz
ossyNMMMNyMMhsssssssssssssshmmmhssssssso Memory: 5290MiB / 12689MiB
ossyNMMMNyMMhsssssssssssssshmmmhssssssso
+sssshhhyNMMNyssssssssssssyNMMMysssssss+
.ssssssssdMMMNhsssssssssshNMMMdssssssss.
/sssssssshNMMMyhhyyyyhdNMMMNhssssssss/
+sssssssssdmydMMMMMMMMddddyssssssss+
/ssssssssssshdmNNNNmyNMMMMhssssss/
.ossssssssssssssssssdMMMNysssso.
-+sssssssssssssssssyyyssss+-
`:+ssssssssssssssssss+:`
.-/+oossssoo+/-.
gateman@DESKTOP-UIU9RFJ:~$ docker --version
Docker version 24.0.5, build 24.0.5-0ubuntu1~20.04.1
准备1个Dockerfile
FROM sonarqube:10.6-community
ENTRYPOINT ["/opt/sonarqube/docker/entrypoint.sh"]
其实就是基于官网(docker-hub) 镜像构建1个
构建镜像
git pull && docker build -t gateman/sonarqube:1.0.0 .
运行镜像
docker run -p 9000:9000 gateman/sonarqube:1.0.0
把端口9000暴露出来就行
登陆
打开主机的页面
http://10.0.1.223:9000
第一次输入 admin:admin 就好, 然后马上会让你修改admin 帐号的密码
创建另1个用户
admin 帐号用于配置, 通常我们需要另1个账户分析代码
生成sonar token
切换到刚才创建账户登陆
生成1个Global Analysis Token 专门用于代码上传
安装SonarScanner
好了, 到了这一步服务端基本已经配置好
接下来要安装本地安装CLI 上传代码工具 SonarScanne
下载地址:
https://binaries.sonarsource.com/Distribution/sonar-scanner-cli/sonar-scanner-cli-6.1.0.4477-linux-x64.zip?_gl=1xwtft4_gcl_auMTM3NDUxMzczNS4xNzI1NjgyMjI1_gaMTQzNzQ0MDQ1Ni4xNzI1NjgyMjI1_ga_9JZ0GZ5TC6*MTcyNTY4MjIyNC4xLjEuMTcyNTY5MTEzMy42MC4wLjA.
解压
gateman@MoreFine-S500:~/Downloads/sonar$ ls
sonar-scanner-cli-6.1.0.4477-linux-x64.zip
gateman@MoreFine-S500:~/Downloads/sonar$ unzip sonar-scanner-cli-6.1.0.4477-linux-x64.zip
gateman@MoreFine-S500:~/devtools$ mv ~/Downloads/sonar/sonar-scanner-6.1.0.4477-linux-x64/ .
enable 执行文件
vi ~/.bashrc
...
# sonar-scanner
export PATH="/home/gateman/devtools/sonar-scanner-6.1.0.4477-linux-x64/bin:$PATH"
...
利用 SonarScanner 出发扫描
sonar-scanner -Dsonar.host.url=http://10.0.1.223:9000 -Dsonar.projectKey=my:python-common -Dsonar.sources=/home/gateman/projects/python/python_common -Dsonar.login=sqa_acba1803e527317f966735b2ae664fab8b77753f -Dsonar.exclusions=**/venv/**
具体的参数选项:
参数 | Desc |
---|---|
-Dsonar.projectKey=project_key | 指定项目在 SonarQube 中的唯一标识。 |
-Dsonar.projectName=project_name | 指定项目在 SonarQube 中显示的名称。 |
-Dsonar.sources=source_directory | 指定要分析的源代码目录。 |
-Dsonar.host.url=sonarqube_server_url | 指定 SonarQube 服务器的 URL 地址。 |
-Dsonar.login=authentication_token | 指定用于连接 SonarQube 服务器的认证令牌。 |
-Dsonar.language=language | 指定要分析的编程语言。 |
-Dsonar.sourceEncoding=utf-8 | 指定源代码的编码格式。 |
-Dsonar.projectVersion=project_version | 指定项目的版本号。 |
-Dsonar.branch.name=branch_name | 指定分析的分支名称。 |
-Dsonar.exclusions=pattern | 指定要排除的文件或目录模式。 |
默认 SonarQube会自动判断你的代码语言
但是最好用 -Dsonar.exclusions 排除掉那些库的代码(例如 venv of Python)
查看report
这时需要等服务器处理几分钟, 就可以从UI 上查看代码分析报告了
搭建GCP SonarQube 方案
为什么要搭在云上呢
本地方案有几个痛点
- sonarqube 使用频率偏少
- sonarqube 扫描时很耗cpu 和内存资源
- 我大部分CICD 设在GCP上, 无法连接家庭内网的服务器
首先还是准备一台有docker 环境的云主机
gateman@MoreFine-S500:~$ gcloud compute instances list --filter="status=RUNNING"
WARNING: This command is using service account impersonation. All API calls will be executed as [terraform@jason-hsbc.iam.gserviceaccount.com].
NAME ZONE MACHINE_TYPE PREEMPTIBLE INTERNAL_IP EXTERNAL_IP STATUS
tf-vpc0-subnet0-main-server europe-west2-c n2d-standard-4 true 192.168.0.35 34.39.2.90 RUNNING
tf-vpc0-subnet0-mysql0 europe-west2-c e2-standard-2 true 192.168.0.42 RUNNING
tf-vpc0-subnet0-vm0 europe-west2-c n2-highmem-4 true 192.168.0.51 RUNNING
_,met$$$$$gg. root@tf-vpc0-subnet0-vm0
,g$$$$$$$$$$$$$$$P. ------------------------
,g$$P" """Y$$.". OS: Debian GNU/Linux 11 (bullseye) x86_64
,$$P' `$$$. Host: Google Compute Engine
',$$P ,ggs. `$$b: Kernel: 5.10.0-32-cloud-amd64
`d$$' ,$P"' . $$$ Uptime: 9 hours, 43 mins
$$P d$' , $$P Packages: 505 (dpkg)
$$: $$. - ,d$$' Shell: bash 5.1.4
$$; Y$b._ _,d$P' CPU: Intel Xeon (4) @ 2.800GHz
Y$$. `.`"Y$$$$P"' Memory: 2979MiB / 32112MiB
`$$b "-.__
`Y$$
`Y$$.
`$$b.
`Y$$b.
`"Y$b._
`"""
配置terraform 添加1个cloud build trigger
添加1个trigger
# referring https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/cloudbuild_trigger
resource "google_cloudbuild_trigger" "sonarqube-service-gce-trigger" {
name = "sonarqube-service-gce-trigger" # could not contains underscore
location = var.region_id
# when use github then should use trigger_template
github {
name = "sonarqube-server"
owner = "nvd11"
push {
branch = "main"
invert_regex = false # means trigger on branch
}
}
substitutions = {
_VM_HOST = "tf-vpc0-subnet0-vm0"
_APP_ENV = "prod"
}
filename = "cloudbuild-gce.yaml"
# projects/jason-hsbc/serviceAccounts/terraform@jason-hsbc.iam.gserviceaccount.com
service_account = data.google_service_account.cloudbuild_sa.id
}
编写cloudbuild-gce.yaml 以部署sonarqube 到某台GCE (google compute engine)
cloudbuild-gce.yaml
steps:
# https://cloud.google.com/build/docs/configuring-builds/substitute-variable-values
- id: build docker image
name: 'gcr.io/cloud-builders/docker'
args: ['build', '-t', 'europe-west2-docker.pkg.dev/$PROJECT_ID/my-docker-repo/${_APP_NAME}:${_APP_TAG}', '.']
- id: upload docker image to GAR
name: 'gcr.io/cloud-builders/docker'
args: [ 'push', 'europe-west2-docker.pkg.dev/$PROJECT_ID/my-docker-repo/${_APP_NAME}:${_APP_TAG}']
- id: check and start VM if not running
name: 'gcr.io/cloud-builders/gcloud'
entrypoint: bash
args:
- '-c'
- |
set -x
VM_STATUS=$(gcloud compute instances describe ${_VM_HOST} --zone=europe-west2-c --format='value(status)' 2>/dev/null || true)
if [ "$$VM_STATUS" != "RUNNING" ]; then
echo "VM ${_VM_HOST} was not running. Starting now..."
gcloud compute instances start ${_VM_HOST} --zone=europe-west2-c
else
echo "VM ${_VM_HOST} is already running."
fi
# to prepare ssh private key file
- id: deploy image to GCE
name: 'gcr.io/cloud-builders/gcloud'
entrypoint: bash
args:
- '-c'
- |
whoami
gcloud auth list
set -x
mkdir -p /root/.ssh
gcloud secrets versions access latest --secret=gateman-private-ssh-key > /root/.ssh/id_rsa
gcloud secrets versions access latest --secret=gateman-public-ssh-key > /root/.ssh/id_rsa.pub
chmod 600 /root/.ssh/id_rsa
chmod 600 /root/.ssh/id_rsa.pub
gcloud compute ssh gateman@${_VM_HOST} --zone=europe-west2-c --quiet --ssh-key-file=/root/.ssh/id_rsa -- "whoami"
gcloud compute ssh gateman@${_VM_HOST} --zone=europe-west2-c --quiet --ssh-key-file=/root/.ssh/id_rsa -- "sudo docker container prune -f; sudo docker ps -a"
gcloud compute ssh gateman@${_VM_HOST} --zone=europe-west2-c --quiet --ssh-key-file=/root/.ssh/id_rsa -- "sudo docker stop ${_APP_NAME} && sudo docker rm ${_APP_NAME}"
gcloud compute ssh gateman@${_VM_HOST} --zone=europe-west2-c --quiet --ssh-key-file=/root/.ssh/id_rsa -- "sudo docker pull europe-west2-docker.pkg.dev/$PROJECT_ID/my-docker-repo/${_APP_NAME}:${_APP_TAG}"
gcloud compute ssh gateman@${_VM_HOST} --zone=europe-west2-c --quiet --ssh-key-file=/root/.ssh/id_rsa -- "sudo docker run -d -p ${_PORT}:${_CONTAINER_PORT} -e APP_ENVIRONMENT=${_APP_ENV} --name ${_APP_NAME} europe-west2-docker.pkg.dev/$PROJECT_ID/my-docker-repo/${_APP_NAME}:${_APP_TAG}"
echo ok
# https://stackoverflow.com/questions/68779751/error-publishing-source-code-from-cloud-build-to-a-bucket-using-triggers
logsBucket: gs://jason-hsbc_cloudbuild/logs/
options: # https://cloud.google.com/cloud-build/docs/build-config#options
logging: GCS_ONLY # or CLOUD_LOGGING_ONLY https://cloud.google.com/cloud-build/docs/build-config#logging
# to define
substitutions:
_APP_NAME: sonarqube-server
_APP_TAG: latest
_VM_HOST: "tf-vpc0-subnet0-vm0"
_PORT: "9000"
_CONTAINER_PORT: "9000"
检查cloudbuild job, 确保部署成功
在有外网ip的主机上设置nginx 代理
因为安装 sonarqube 的主机 tf-vpc0-subnet0-vm0 并没有外网ip, 家里并不能访问
所以我们需要在有外网ip的主机上设个nginx 代理, 代理到 tf-vpc0-subnet0-vm0:9000
暴力配置:
upstream sonarqube-server {
server tf-vpc0-subnet0-vm0:9000;
}
server {
listen 9000;
server_name www.jp-gvms.cloud;
location / {
proxy_pass http://sonarqube-server;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
}
这时可以打开 sonarqube 页面了, 相信你们也可以
http://www.jp-gcp-vms.cloud:9000/
登陆,修改admin 密码, 创建用户, 生成token
接下来这几步上面已经介绍过, 不再重复
把token 放入google 的secret manager
sqa_2b62011d73270438f1d37ae0cf6acff4d23ea990
为springboot cloud-order service 创建1个cloudbuild trigger 专门用于sonarqube 扫描
好了, 到了上面一步, 其实sonarqube server 配置已经做好
接下来就是使用
首先创建1个cloudbuild job for 需要扫描的service
resource "google_cloudbuild_trigger" "demo_cloud_order-sonarqube-trigger" {
name = "demo-cloud-order-sonarqube-trigger" # could not contains underscore
location = var.region_id
# when use github then should use trigger_template
github {
name = "demo_cloud_order"
owner = "nvd11"
push {
branch = ".*"
# for all branch
invert_regex = false # means trigger on branch
}
}
filename = "cloudbuild-sonarqube.yaml"
# projects/jason-hsbc/serviceAccounts/terraform@jason-hsbc.iam.gserviceaccount.com
service_account = data.google_service_account.cloudbuild_sa.id
}
为springboot cloud-order service 创建1个cloudbuild-sonarqube yaml 用于定义具体出发sonar 扫描步骤
steps:
# to prepare ssh private key file
# as the default user of sonarsource/sonar-scanner-cli is scanner-cli, it could not create any subfolder in /workspace
# that's why we need to change the permission to 777
- id: prepare ssh private key file
name: 'ubuntu'
entrypoint: bash
args:
- '-c'
- |
set -x
pwd
chmod -R 777 .
- id: trigger sonarqube scanning
name: 'sonarsource/sonar-scanner-cli'
entrypoint: '/bin/sh'
args:
- '-c'
- |
set -x
whoami
pwd
ls -l
sonar-scanner -Dsonar.projectKey=my:${_APP_NAME} -Dsonar.host.url=${_SONARQUBE_HOST} -Dsonar.token=$$SONAR_TOKEN -Dsonar.projectKey=my:${_APP_NAME} -Dsonar.sources=. -Dsonar.java.binaries=./test_scripts
secretEnv:
- 'SONAR_TOKEN'
logsBucket: gs://jason-hsbc_cloudbuild/logs/
options: # https://cloud.google.com/cloud-build/docs/build-config#options
logging: GCS_ONLY # or CLOUD_LOGGING_ONLY https://cloud.google.com/cloud-build/docs/build-config#logging
# to define
availableSecrets:
secretManager:
- versionName: projects/$PROJECT_ID/secrets/sonarqube-token-gateman/versions/latest
env: 'SONAR_TOKEN'
substitutions:
_SONARQUBE_HOST: http://www.jp-gcp-vms.cloud:9000
_APP_NAME: cloud-order
检查log