文章目录
后端项目单元测试
后端 java 项目的静态代码检测主要包括:单元测试和静态代码检查。
编写测试流水线
[root@gitclient apiserver]# vim .gitlab-ci.yml
stages:
- compile
unittest:
stage: compile
image: maven:3.8.5-openjdk-17
script:
- mvn verify -Dmaven.test.failure.ignore=true
- ls target/surefire-reports/*.xml
rules:
- if: $CI_COMMIT_BRANCH == "main"
artifacts:
when: always
reports:
junit:
- target/surefire-reports/TEST-*.xml
- target/failsafe-reports/TEST-*.xml
tags:
- study-runner
[root@gitclient apiserver]# git add .gitlab-ci.yml
[root@gitclient apiserver]# git commit -m "Test mvn verify"
[root@gitclient apiserver]# git push origin main
提示:maven 所使用的 jdk 版本需要和创建后端项目的 java 版本一致。
提交流水线后,等待测试完成。
查看测试结果。
SonarQube代码检测平台
SonarQube介绍
SonarQube 是一个开源平台,用于持续检查代码质量并执行自动化分析。它可以与 Jenkins 或 Gitlab 、 Actions 等集成,以便在 CI/CD 流程中自动进行代码审查。
其内核是设计思想是 “质量即代码”(Quality as Code) 理念。
SonarQube 将代码质量分解为三个正交维度:
- 可靠性(Reliability):预防运行时缺陷
- 安全性(Security):消除漏洞攻击面
- 可维护性(Maintainability):降低技术维护难度
SonarQube部署
本实验基于已有的 Kubernetes 集群和 Longhorn 持久化进行部署,保障 SonarQube 的健壮性和数据持久化。
部署 SonarQube 之前需要先部署 PostgreSQL 。
对于 Kubernetes 和 Longhorn 部署,不在此文章范围内,可自行参考: 附039.Kubernetes_v1.32.2生产环境高可用部署 。
- 确认当前环境
确认当前环境具备 StorageClass ,用于给 sonarqube 提供持久化数据。
root@master01:~# mkdir sonarqube
root@master01:~# cd sonarqube/
root@master01:~/sonarqube# kubectl get sc
NAME PROVISIONER RECLAIMPOLICY VOLUMEBINDINGMODE ALLOWVOLUMEEXPANSION AGE
longhorn (default) driver.longhorn.io Delete Immediate true 15d
longhorn-static driver.longhorn.io Delete Immediate true 15d
- 创建PostgreSQL PVC
root@master01:~/sonarqube# kubectl create namespace sonarqube
root@master01:~/sonarqube# cat <<EOF > postgresqlpvc.yaml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: postgresql-pvc
namespace: sonarqube
spec:
accessModes:
- ReadWriteOnce
storageClassName: longhorn
resources:
requests:
storage: 1Gi
EOF
root@master01:~/sonarqube# kubectl apply -f postgresqlpvc.yaml
- 部署PostgreSQL
部署 postgresql ,创建 SonarQube 所需的用户名、密码、数据库DB,使用已创建的 PVC 。
同时将 postpresql 对外暴露服务,以便于后续 SonarQube 调用。
root@master01:~/sonarqube# cat <<EOF > postgresqldeployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: postgres
namespace: sonarqube
spec:
replicas: 1
selector:
matchLabels:
app: postgres
template:
metadata:
labels:
app: postgres
spec:
containers:
- name: postgres
image: postgres:17.4
env:
- name: POSTGRES_USER
value: sonar
- name: POSTGRES_PASSWORD
value: sonar1234
- name: POSTGRES_DB
value: sonardb
- name: PGDATA
value: /var/lib/postgresql/data
ports:
- containerPort: 5432
volumeMounts:
- name: postgres-data
mountPath: /var/lib/postgresql
- mountPath: /etc/localtime
name: timeconfig
readOnly: true
volumes:
- name: postgres-data
persistentVolumeClaim:
claimName: postgresql-pvc
- name: timeconfig
hostPath:
path: /etc/localtime
---
apiVersion: v1
kind: Service
metadata:
name: postgres
namespace: sonarqube
spec:
type: NodePort
selector:
app: postgres
ports:
- protocol: TCP
port: 5432
targetPort: 5432
nodePort: 31003
EOF
root@master01:~/sonarqube# kubectl apply -f postgresqldeployment.yaml
确认 postgresql 部署情况:
root@master01:~/sonarqube# kubectl -n sonarqube get pods -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
postgres-cf69988db-4hr2l 1/1 Running 0 16s 10.10.30.97 worker02 <none> <none>
root@master01:~/sonarqube# kubectl -n sonarqube get svc -o wide
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE SELECTOR
postgres NodePort 10.20.2.220 <none> 5432:31003/TCP 19s app=postgres
root@master01:~/sonarqube# kubectl -n sonarqube get pvc -o wide
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS VOLUMEATTRIBUTESCLASS AGE VOLUMEMODE
postgresql-pvc Bound pvc-91fad7aa-b315-48bb-9e67-ff27125605ac 1Gi RWO longhorn <unset> 34s Filesystem
- 部署SonarQube pvc
sonarqube 主要目录均配置持久化存储。
root@master01:~/postgresql# vim sonarqubepvc.yaml
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: sonarqube-data-pvc
namespace: sonarqube
spec:
accessModes:
- ReadWriteOnce
storageClassName: longhorn
resources:
requests:
storage: 2Gi
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: sonarqube-extensions-pvc
namespace: sonarqube
spec:
accessModes:
- ReadWriteOnce
storageClassName: longhorn
resources:
requests:
storage: 500Mi
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: sonarqube-logs-pvc
namespace: sonarqube
spec:
accessModes:
- ReadWriteOnce
storageClassName: longhorn
resources:
requests:
storage: 200Mi
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: sonarqube-config-pvc
namespace: sonarqube
spec:
accessModes:
- ReadWriteOnce
storageClassName: longhorn
resources:
requests:
storage: 200Mi
root@master01:~/postgresql# kubectl apply -f sonarqubepvc.yaml
- 部署SonarQube
为了多个主要目录都做持久化,对 data 、extensions 、 conf 、logs均做单独 PVC 挂载。
若单独挂载,则需要对 Pod 内部的容器权限使用 initContainers 提前进行设置。
同时 sonarqube 在后续配置和 gitlab 集成的时候,本环境的 gitlab 采用了自签名证书,所以需要将 CA 添加到 sonarqube 中,否则无法通过证书校验。
kubectl -n sonarqube create configmap gitlab-ca \
--from-file=myCA.crt=/gitlab/config/certs/myCA.crt
root@master01:~/sonarqube# vim sonarqubedeployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: sonarqube
namespace: sonarqube
spec:
replicas: 1
selector:
matchLabels:
app: sonarqube
strategy:
type: Recreate
template:
metadata:
labels:
app: sonarqube
spec:
initContainers:
- name: volume-permission-fix
image: uhub.service.ucloud.cn/imxhy/xhyops:v2
command: ["sh", "-c", "chown -R 1000:1000 /opt/sonarqube && update-ca-certificates"]
volumeMounts:
- name: sonarqube-data
mountPath: /opt/sonarqube/data
- name: sonarqube-extensions
mountPath: /opt/sonarqube/extensions
- name: sonarqube-logs
mountPath: /opt/sonarqube/logs
- name: sonarqube-conf
mountPath: /opt/sonarqube/conf
- name: ca-cert
mountPath: /etc/ssl/certs/myCA.crt
subPath: myCA.crt
containers:
- name: sonarqube
image: sonarqube:9.9.8-community
env:
- name: SONAR_JDBC_URL
value: jdbc:postgresql://postgres:5432/sonardb
- name: SONAR_JDBC_USERNAME
value: sonar
- name: SONAR_JDBC_PASSWORD
value: sonar1234
- name: SONAR_WEB_JAVAOPTS
value: "-Xmx2048m -Xms1024m -Dsonar.web.context=/ -Dsonar.core.serverBaseURL=http://172.24.8.180:31004"
- name: SONAR_CE_JAVAOPTS
value: "-Xmx2048m"
- name: TZ
value: Asia/Shanghai
ports:
- containerPort: 9000
volumeMounts:
- name: sonarqube-data
mountPath: /opt/sonarqube/data
- name: sonarqube-extensions
mountPath: /opt/sonarqube/extensions
- name: sonarqube-logs
mountPath: /opt/sonarqube/logs
- name: sonarqube-conf
mountPath: /opt/sonarqube/conf
- mountPath: /etc/localtime
name: timeconfig
readOnly: true
volumes:
- name: sonarqube-data
persistentVolumeClaim:
claimName: sonarqube-data-pvc
- name: sonarqube-extensions
persistentVolumeClaim:
claimName: sonarqube-extensions-pvc
- name: sonarqube-logs
persistentVolumeClaim:
claimName: sonarqube-logs-pvc
- name: sonarqube-conf
persistentVolumeClaim:
claimName: sonarqube-config-pvc
- name: timeconfig
hostPath:
path: /etc/localtime
- name: ca-cert
configMap:
name: gitlab-ca
items:
- key: myCA.crt
path: myCA.crt
---
apiVersion: v1
kind: Service
metadata:
name: sonarqube
namespace: sonarqube
spec:
type: NodePort
selector:
app: sonarqube
ports:
- protocol: TCP
port: 9000
targetPort: 9000
nodePort: 31004
root@master01:~/sonarqube# kubectl apply -f sonarqubedeployment.yaml
- 确认部署情况
确认 postgresql 和 sonarqube 部署情况。
root@master01:~/sonarqube# kubectl -n sonarqube get pods -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
postgres-ccfdf8967-s6wz7 1/1 Running 0 116m 10.10.5.42 worker01 <none> <none>
sonarqube-6678c6fb95-d86mt 1/1 Running 0 100m 10.10.5.36 worker01 <none> <none>
root@master01:~/sonarqube# kubectl -n sonarqube get pvc -o wide
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS VOLUMEATTRIBUTESCLASS AGE VOLUMEMODE
postgresql-pvc Bound pvc-456bcb6d-3630-487d-8cab-1cb88677f5e4 1Gi RWO longhorn <unset> 123m Filesystem
sonarqube-config-pvc Bound pvc-54759240-43d8-4923-a953-4e919dcf5e30 200Mi RWO longhorn <unset> 114m Filesystem
sonarqube-data-pvc Bound pvc-f66670e0-fd71-49a7-ad4a-a9748e62156c 2Gi RWO longhorn <unset> 114m Filesystem
sonarqube-extensions-pvc Bound pvc-c1b5855f-e2d5-4fc5-b82f-cb43bdb25ad6 500Mi RWO longhorn <unset> 114m Filesystem
sonarqube-logs-pvc Bound pvc-bb085d54-4b64-4290-a731-198283efedf2 200Mi RWO longhorn <unset> 114m Filesystem
root@master01:~/sonarqube# kubectl -n sonarqube get svc -o wide
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE SELECTOR
postgres NodePort 10.20.2.220 <none> 5432:31003/TCP 77m app=postgres
sonarqube NodePort 10.20.245.54 <none> 9000:31004/TCP 2m29s app=sonarqube
浏览器通过端口访问: http://172.24.8.180:31004 。
- ingress暴露
当前环境已存在 ingress ,所以可以进一步采用 ingress 暴露 sonarqube 服务。
root@master01:~/sonarqube# ll certs/
total 12
drwxr-xr-x 2 root root 58 Apr 28 21:47 ./
drwxr-xr-x 3 root root 164 Apr 28 21:46 ../
-rw-r--r-- 1 root root 4482 Apr 28 21:47 sq.linuxsb.com.crt
-rw-r--r-- 1 root root 1704 Apr 28 21:47 sq.linuxsb.com.key
root@master01:~/sonarqube# mv certs/sq.linuxsb.com.crt certs/tls.crt
root@master01:~/sonarqube# mv certs/sq.linuxsb.com.key certs/tls.key
root@master01:~/sonarqube# kubectl -n sonarqube create secret tls tls-sonarqube-ingress \
--cert=/root/sonarqube/certs/tls.crt \
--key=/root/sonarqube/certs/tls.key
root@master01:~/sonarqube# vim sonarqubeingress.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: sonarqube-ingress
namespace: sonarqube
spec:
ingressClassName: "nginx"
rules:
- host: sq.linuxsb.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: sonarqube
port:
number: 9000
tls:
- hosts:
- sq.linuxsb.com
secretName: tls-sonarqube-ingress
[root@master01 manifests]# kubectl apply -f sonarqubeingress.yaml
浏览器访问: https://sq.linuxsb.com/ ,使用默认用户名和密码 admin/admin 登录 。
首次登录需要修改密码:
成功登录。
提示:更多详细部署参考: Kubernetes 集群中部署 SonarQube 服务 。
sonarqube 与 gitlab 集成
gitlab 创建 Token
SonarQube 搭建完成后,需要和 Gitlab 平台集成,从而实现 和 Gitlab 上 代码仓联动。
- 生成Token
在 gitlab 上创建一个 Token ,后续用于 sonarqube 认证。该 Token 需要有足够的权限,能访问 Gitlab 上的所有项目。
偏好设置 ——> 访问令牌 ——> 添加新令牌。
创建不限期的所有权限的 Token 。
复制创建的 Token , glpat-hGsgfY1YyB79-5otzKMq 。
配置 sonarqube 集成 gitlab
登录 http://sq.linuxsb.com , Administration ——> Configuration ——> DevOps Platform Integrations ——> GitLab 。
Create configuration:
Check configuration ,检查配置。
sonarqube 集成 gitlab 账号
sonarqube 可以集成 gitlab 的账号进行登录。
- 创建应用
gitlab 上创建应用,用户头像 ——> 偏好设置 ——> 应用,新建应用。
设置应用名称,以及固定 URI : [sonarqube域名]/oauth2/callback/gitlab 。
记录相关值,后续用于在 sonarqube 上配置。
确认创建完成。
- 配置sonarqube
使用 admin 用户登录,Administration ——> Configuration ——> Authentication ——> GitLab 。
enabled 开启,配置 gitlab url,应用ID 以及 secret,将 gitlab 所创建的相关信息复制。
GitLab认证通常允许用户注册,但没有提供允许的组列表,这是潜在的不安全,因此建议配置一个允许的组列表。
Administration ——> Configuration ——> General ,配置 Server base URL ,配置为 sonarqube 的主域名。
- 确认验证
退出登录,重新访问 https://sq.linuxsb.com ,确认可以出现 Login with GitLab 。
如果是自签名证书,可能出现告警,继续即可。
使用 gitlab 账号登录,如 root 用户,通常建议可以为 sonarqube 单独创建一个其他用户。
由于实验目的,直接使用 gitlab 的 root 用户来登录 sonarqube ,会提示高权限风险,进行授权即可。
确认能正常使用 gitlab 用户登录。
配置Java后端项目
配置后端Java项目静态代码检查
sonarqube平台配置成功后,可以使用该平台对后端 Java 项目进行静态代码检查。
输入已创建的Token: glpat-hGsgfY1YyB79-5otzKMq 。
选择当前gitlab平台中已有的 apiserver java项目,设置。
选择持续集成工具,Gitlab CI。
使用 GitLab CI 分析项目中选择 Maven ,复制,将内容写入后端项目的pom.xml文件,然后单击 continue 。
添加环境变量,按照指示在gitlab中添加相应环境变量。
创建 SONAR_TOEN 变量,新生成一个Token,sqp_0bbc8fe52839d45f84c4e5ca28222f07d5463133 。
复制对应的Token,后续用于在gitlab创建变量。
登录gitlab,创建一个 SONAR_TOKEN 的变量,变量值为所创建value。
创建 SONAR_HOST_URL 变量。
确认所创建的变量。
创建CI流程文件 .gitlab-ci.yml ,内容直接复制,然后根据需要修改即可。
sonarqube-check:
image: maven:3.6.3-jdk-11
variables:
SONAR_USER_HOME: "${CI_PROJECT_DIR}/.sonar" # Defines the location of the analysis task cache
GIT_DEPTH: "0" # Tells git to fetch all the branches of the project, required by the analysis task
cache:
key: "${CI_JOB_NAME}"
paths:
- .sonar/cache
script:
- mvn verify sonar:sonar -Dsonar.projectKey=mygroup_apiserver_AZZ8-s2f7mv8sZMUbW1V
allow_failure: true
only:
- main
创建流水线
根据 sonarqube 提示,在pom.xml中追加对应内容
[root@gitclient apiserver]# vi pom.xml
#……
<properties>
<java.version>17</java.version>
<sonar.qualitygate.wait>true</sonar.qualitygate.wait> #追加
</properties>
#……
根据sonarqube提示的内容创建流水线:
[root@gitclient apiserver]# vim .gitlab-ci.yml
stages:
- compile
sonarqube-check:
stage: compile
image: maven:3.8.5-openjdk-17
variables:
SONAR_USER_HOME: "${CI_PROJECT_DIR}/.sonar" # Defines the location of the analysis task cache
GIT_DEPTH: "0" # Tells git to fetch all the branches of the project, required by the analysis task
cache:
key: "${CI_JOB_NAME}"
paths:
- .sonar/cache
script:
- mvn verify sonar:sonar -Dsonar.projectKey=mygroup_apiserver_AZZ8-s2f7mv8sZMUbW1V
allow_failure: true
only:
- main
tags:
- study-runner
提示:由于JAVA后端采用的是 java 17,所以需要修改maven为兼容的版本。
人为制造不规范的代码:
[root@gitclient apiserver]# vim src/main/java/com/example/apiservice/controller/HelloWorld.java
package com.example.apiservice.controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/demo")
public class HelloWorld {
@RequestMapping("/hello")
public String hello() {
int a = 100/10; #随意新增不规范代码进行模拟
return "Hello World!";
}
}
提交流水线:
[root@gitclient apiserver]# git add .
[root@gitclient apiserver]# git commit -m "Test SonarQube Check"
[root@gitclient apiserver]# git push origin main
提交流水线后,待gitlab流水线跑完,sonarqube的代码检测会有几个检测结果。
查看问题,可以查看人为模拟的不规范代码被正确识别。