1. 配置通用账户
为了方便统一管理,我们在 gitlab 上注册一个通用账户:sonarqube,作为对接 SonarQube 的统一账户,同时还需要将 SonarQube 加到项目成员或项目所属项目组成员里面去,这样才能有权限 comment 和添加注释行。
1、启动 gitlab,创建 java 组,创建用户 sonarqube,并设置为管理员添加到 java 组里。
2、创建项目 springboot_cicd,指定项目组为 java,并添加人员为 sonarqube。
2. 配置ALM集成
1、使用 sonarqube 用户启动 gitlab,点击右上角的偏好设置,找到应用。
2、启动 sonarqube,设置 sonar 启动 url。
3、配置 ALM 集成,填写 gitlab 上显示的信息。
4、退出 sonarqube 重新进入,就会出现这个页面,使用 gitlab 授权访问 sonarqube,点击进去进行授权即可。
3. 注册Runner
3.1 注册Specific Runner
1、Specific Runner 指的是当期项目独有,可以在 springboot_cicd -> 设置 -> CI/CD -> Runner 中获取 url 和令牌进行注册。
2、注册 runner。
# root 用户下执行
gitlab-runner register -n \
--url http://192.168.58.12/ \
--registration-token rcEsUwBr8ovf4Gb5rjPx \
--tag-list shell \
--executor shell \
--description "shell执行器"
3.2 注册Share Runner
选中管理中心 -》Runner,可以看到共享 Runner 的 url 和 token,注册方法和上面一样。
3.3 使用Docker执行器
3.3.1 配置—-注册Runner禁用TLS
很多时候我们都是使用 docker 执行器而不是 shell 执行器,但使用 docker 执行器有一些注意点。
注册 runner 时,使用下面命令:
gitlab-runner register -n \
--url http://192.168.58.12/ \
--registration-token rcEsUwBr8ovf4Gb5rjPx \
--tag-list docker \
--executor docker \
--description "My Docker Runner" \
--docker-image "docker:19.03.12" \
--docker-volumes /var/run/docker.sock:/var/run/docker.sock
3.3.2 配置—-注册Runner启用TLS
注册 runner 时,使用下面命令:
gitlab-runner register -n \
--url http://192.168.58.12/ \
--registration-token rcEsUwBr8ovf4Gb5rjPx \
--executor docker \
--description "My Docker Runner" \
--docker-image "docker:19.03.12" \
--docker-privileged \
--docker-volumes "/certs/client"
4. 配置邮件
1、gitlab 中进行配置。
gitlab_rails['smtp_enable'] = true
gitlab_rails['smtp_address'] = "smtp.qq.com"
gitlab_rails['smtp_port'] = 465
gitlab_rails['smtp_user_name'] = "smtp username"
gitlab_rails['smtp_password'] = "smtp password"
gitlab_rails['smtp_domain'] = "qq.com"
gitlab_rails['smtp_authentication'] = "login"
gitlab_rails['smtp_enable_starttls_auto'] = true
gitlab_rails['smtp_tls'] = false
2、sonar-scanner 所在服务器需要安装 python-sonarqube-api 这个插件,执行命令:
# 在线下载
git clone https://github.com/shijl0925/python-sonarqube-api.git
cd python-sonarqube-api
python3 setup.py install
3、在 .gitlab.yml 文件中配置邮件发送。
mail-job2:
stage: mail
image: $HARBOR_HOST/library/python:3.7.9
script:
- echo "发送邮件"
- git clone https://github.com/shijl0925/python-sonarqube-api.git
- cd python-sonarqube-api
- python3 setup.py install
- cd ..
- python3 send_mail.py $CI_PROJECT_NAME $CI_COMMIT_REF_NAME $GITLAB_USER_EMAIL $SONARQUBE_URL $SONARQUBE_USERNAME $SONARQUBE_PASSWORD $SMTP_HOST $FROM_ADDR $FROM_ADDR_PASS
# - python3 /opt/sonar-scanner-4.6.2/sendmail_api.py $CI_PROJECT_NAME $CI_COMMIT_REF_NAME $GITLAB_USER_EMAIL
tags:
- docker_mail
only:
- master
5. 后端完整配置
5.1 环境变量
HARBOR_AUTH_CONFIG: '{"auths": {"localrepo.io:8443": {"auth": "YWRtaW46SGFyYm9yMTIzNDU"},"192.168.58.11:8443": {"auth": "YWRtaW46SGFyYm9yMTIzNDU"}}}'
HARBOR_USER: admin # harbor用户名
HARBOR_PASSWORD: Harbor12345 # harbor密码
HARBOR_HOST: localrepo.io:8443 # harbor服务器访问地址
SONARQUBE_URL:http://192.168.58.12:9000
CI_PROJECT_NAME:yygh-parent
CI_COMMIT_REF_NAME:master
GITLAB_USER_EMAIL:1076372957@qq.com
SONARQUBE_USERNAME:admin
SONARQUBE_PASSWORD:123
SMTP_HOST:smtp.qq.com
FROM_ADDR:2711845964@qq.com
FROM_ADDR_PASS:umnnlyikqlfjdegi
TEST_SERVER:192.168.58.12
5.2 pom.xml文件
<distributionManagement>
<repository>
<id>nexus-releases</id>
<name>corp nexus-releases</name>
<url>http://192.168.58.11:8081/repository/maven-releases/</url>
</repository>
<snapshotRepository>
<id>nexus-snapshots</id>
<name>corp nexus-snapshots</name>
<url>http://192.168.58.11:8081/repository/maven-snapshots/</url>
</snapshotRepository>
</distributionManagement>
<build>
<pluginManagement>
<plugins>
<plugin>
<groupId>org.sonarsource.scanner.maven</groupId>
<artifactId>sonar-maven-plugin</artifactId>
<version>3.7.0.1746</version>
</plugin>
</plugins>
</pluginManagement>
</build>
5.3 .gitlab-ci.yaml文件
image: $HARBOR_HOST/library/docker:19.03.12
cache:
paths:
- .m2/repository
stages:
- scan
- mail
- build
- unit-test
- deploy
scan-job:
stage: scan
image: $HARBOR_HOST/library/maven:3-jdk-8
script:
- echo "代码质量检查"
# - mvn clean verify sonar:sonar -Dsonar.host.url=$SONARQUBE_URL -Dsonar.login=c7cbb80d4b4fb119612d0e410e88b2046413db88 -Dsonar.java.binaries=./target/classes -Dsonar.sources=. -Dsonar.tests=. -Dsonar.test.inclusions=**/*Test*/** -Dsonar.exclusions=**/*Test*/** -Dsonar.language=java -Dsonar.sourceEncoding=UTF-8
- mvn sonar:sonar -Dsonar.projectKey=$CI_PROJECT_NAME -Dsonar.host.url=$SONARQUBE_URL -Dsonar.login=c7cbb80d4b4fb119612d0e410e88b2046413db88 -Dsonar.java.binaries=./
tags:
- docker_scan
only:
- master
mail-job2:
stage: mail
image: $HARBOR_HOST/library/python:3.7.9
script:
- echo "发送邮件"
- git clone https://github.com/shijl0925/python-sonarqube-api.git
- cd python-sonarqube-api
- python3 setup.py install
- cd ..
- python3 send_mail.py $CI_PROJECT_NAME $CI_COMMIT_REF_NAME $GITLAB_USER_EMAIL $SONARQUBE_URL $SONARQUBE_USERNAME $SONARQUBE_PASSWORD $SMTP_HOST $FROM_ADDR $FROM_ADDR_PASS
# - python3 /opt/sonar-scanner-4.6.2/sendmail_api.py $CI_PROJECT_NAME $CI_COMMIT_REF_NAME $GITLAB_USER_EMAIL
tags:
- docker_mail
only:
- master
build-job:
stage: build
image: $HARBOR_HOST/library/maven:3-jdk-8
script:
- echo "构建项目"
- mvn clean compile package -Dmaven.test.skip=true
artifacts:
paths:
- target/*.jar
expire_in: 10mins
tags:
- docker_build
only:
- master
unit-test-job:
stage: unit-test
script:
- echo '单元测试'
#- mvn test
tags:
- docker_unit-test
only:
- master
deploy-job:
stage: deploy
variables:
IMAGE_NAME: $HARBOR_HOST/test/$CI_PROJECT_NAME
before_script:
- export IMAGE_TAG=$(date "+%-y.%-m.%-d_%H%M%S")
script:
- echo "构建镜像"
- docker build -t $IMAGE_NAME:$IMAGE_TAG .
- echo "推送镜像"
- mkdir -p $HOME/.docker
- docker login --username $HARBOR_USER --password $HARBOR_PASSWORD $HARBOR_HOST
- docker push $IMAGE_NAME:$IMAGE_TAG
- echo "部署镜像"
- if [ $(docker ps -aq --filter name=$CI_PROJECT_NAME) ]; then docker rm -f $CI_PROJECT_NAME;fi
- docker pull $IMAGE_NAME:$IMAGE_TAG
- docker run -d -p 8081:8081 --name $CI_PROJECT_NAME $IMAGE_NAME:$IMAGE_TAG
tags:
- docker_deploy
only:
- master
5.4 Dockfile文件
FROM openjdk:8-jdk
COPY target/*.jar app.jar
EXPOSE 8081
ENTRYPOINT ["java","-jar","app.jar"]
5.5 send_mail文件
import sys
import smtplib
from email.mime.text import MIMEText
from email.mime.multipart import MIMEMultipart
from sonarqube import SonarQubeClient
def sendmail(subject, msg, toaddrs, fromaddr, password, smtpserver):
mail_msg = MIMEMultipart()
mail_msg['Subject'] = subject
mail_msg['From'] = fromaddr
mail_msg['To'] = ','.join(toaddrs)
mail_msg.attach(MIMEText(msg, 'html', 'utf-8'))
try:
s = smtplib.SMTP_SSL(smtpserver)
s.connect(smtpserver, 465) # 连接smtp
s.login(fromaddr, password) # 登录邮箱
s.sendmail(fromaddr, toaddrs, mail_msg.as_string()) # 发送邮件
s.quit()
print("send successful!")
except Exception as e:
print(e)
print("Failed to send ")
def getSonarqubeInfo(branch="master", component=None, url=None, username=None, password=None):
sonar = SonarQubeClient(sonarqube_url=url)
sonar.auth.authenticate_user(login=username, username=username, password=password)
component_data = sonar.measures.get_component_with_specified_measures(
component=component,
branch=branch,
fields="metrics,periods",
metricKeys="""
code_smells,bugs,coverage,duplicated_lines_density,ncloc,
security_rating,reliability_rating,vulnerabilities,comment_lines_density,
ncloc_language_distribution,alert_status,sqale_rating
"""
)
result_dict = {}
for info_dict in component_data["component"]["measures"]:
result_dict[info_dict["metric"]] = info_dict["value"]
print(result_dict)
return result_dict
def main():
subject = "Gitlab代码质量检测" # 邮件主题
project = sys.argv[1].strip() # 项目名称
branch = sys.argv[2] # 分支名称
user_email = sys.argv[3] # 收件人
sonar_url = sys.argv[4] # sonarQube URL
sonar_user = sys.argv[5] # sonarQube 用户
sonar_pass = sys.argv[6] # sonarQube 密码
smtp_host = sys.argv[7] # smtp主机
fromaddr = sys.argv[8] # 发件地址
password = sys.argv[9] # 邮箱密码
project_url = "{}/dashboard?id={}&branch={}".format(sonar_url, project, branch)
print("项目地址:"+project_url)
sonarqube_data = getSonarqubeInfo(branch=branch, component=project, url=sonar_url, username=sonar_user, password=sonar_pass)
html_text = """
<!DOCTYPE html>
<html lang="en">
<head>
<title></title>
<meta charset="utf-8">
</head>
<body>
<div class="page" style="margin-left: 30px">
<h3>您好:</h3>
<h3> 本次提交代码检查结果如下</h3>
<h3> 项目名称:{project} </h3>
<h3> 分支:{branch} </h3>
<h4>一、总体情况</h4>
<ul>
<li style="font-weight:bold;">
本次扫描代码行数: <span style="color:blue">{lines} </span>,
bugs: <span style="color:red">{bugs}</span>,
Vulnerabilities: <span style="color:red">{vulnerabilities}</span>,
Code Smells: <span style="color:red">{code_smells}</span>
</li>
<li style="font-weight:bold;margin-top: 10px;">
URL地址:
<a style="font-weight:bold;"
href={project_url}>{project_url}
</a>
</li>
</ul>
<h4>二、信息详情</h4>
<ul>
<li style="font-weight:bold;">
综合等级: {sqale_rating}
</li>
<li style="font-weight:bold;">
各语言扫描行数: {ncloc_language_distribution}
</li>
<li style="font-weight:bold;">
代码重复率: {duplicated_lines_density}
</li>
<li style="font-weight:bold;">
安全等级: {security_rating}
</li>
<li style="font-weight:bold;">
可靠等级: {reliability_rating}
</li>
<li style="font-weight:bold;">
注释行密度: {comment_lines_density}
</li>
</ul>
</div>
</body>
</html>
""".format(project_url=project_url,
user_mail=user_email,
project=project,
branch=branch,
lines=sonarqube_data["ncloc"],
bugs=sonarqube_data["bugs"],
vulnerabilities=sonarqube_data["vulnerabilities"],
code_smells=sonarqube_data["code_smells"],
ncloc_language_distribution=sonarqube_data["ncloc_language_distribution"],
duplicated_lines_density=sonarqube_data["duplicated_lines_density"],
reliability_rating=sonarqube_data["reliability_rating"],
security_rating=sonarqube_data["security_rating"],
comment_lines_density=sonarqube_data["comment_lines_density"],
sqale_rating=sonarqube_data["sqale_rating"]
)
toaddrs = [user_email,]
msg = html_text
#print(msg)
# 发送邮件
sendmail(subject, msg, toaddrs, fromaddr, password, smtp_host)
if __name__ == '__main__':
main()
5.6 maven的settings.xml
6. 前端完整配置
6.1 环境变量
这里后端用的自己写的 springboot 项目,前端用的 ruoyi 的前后端分离的前端项目。
CI_COMMIT_REF_NAME:master
CI_PROJECT_NAME:ruoyi_ui
NPM_REGISTRY_URL:https://registry.npm.taobao.org
6.2 .gitlab-ci.yaml文件
image: $HARBOR_HOST/library/docker:19.03.12
cache:
paths:
- node_modules
- dist/
stages:
- build
- deploy
# npm run build:stage,打包到预发布环境,构建打包成功之后,会在根目录生成dist文件夹,里面就是构建打包好的文件,通常是 ***.js 、***.css、index.html 等静态文件。
build-job:
stage: build
image: $HARBOR_HOST/library/node:10.16-slim
script:
- echo '安装依赖'
- npm cache clean --force
- npm install --registry=$NPM_REGISTRY_URL
- echo '开始构建'
- npm run build:stage
artifacts:
paths:
- dist/*
expire_in: 10mins
tags:
- docker
deploy-job:
stage: deploy
variables:
IMAGE_NAME: $HARBOR_HOST/ruoyi/$CI_PROJECT_NAME
before_script:
- export IMAGE_TAG=$(date "+%-y.%-m.%-d_%H%M%S")
script:
- echo "构建镜像"
- docker build -t $IMAGE_NAME:$IMAGE_TAG .
- echo "推送镜像"
- mkdir -p $HOME/.docker
- docker login --username $HARBOR_USER --password $HARBOR_PASSWORD $HARBOR_HOST
- docker push $IMAGE_NAME:$IMAGE_TAG
- echo "部署镜像"
- if [ $(docker ps -aq --filter name=$CI_PROJECT_NAME) ]; then docker rm -f $CI_PROJECT_NAME;fi
- docker pull $IMAGE_NAME:$IMAGE_TAG
- docker run -d -p 81:80 --name $CI_PROJECT_NAME $IMAGE_NAME:$IMAGE_TAG
tags:
- docker
only:
- master
6.3 Dockfile文件
FROM harbor02.io:8443/library/nginx:1.19.6-alpine RUN rm -f /etc/nginx/nginx.conf \ && rm -f /etc/nginx/conf.d/* \ && rm -f /usr/share/nginx/html/* ADD dist/ /usr/share/nginx/html/ # SSL证书 # 生成配置文件 COPY nginx.conf /etc/nginx/nginx.conf CMD nginx -g 'daemon off;'
6.4 nginx.conf文件
user nginx;
worker_processes auto;
error_log /var/log/nginx/error.log warn;
pid /var/run/nginx.pid;
events {
worker_connections 1024;
}
http {
include /etc/nginx/mime.types;
default_type application/octet-stream;
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';
access_log /var/log/nginx/access.log main;
sendfile on;
keepalive_timeout 65;
server {
listen 80;
listen [::]:80;
server_name _;
charset utf-8;
# 静态资源代理
location / {
root /usr/share/nginx/html;
index index.html;
}
# 将所有的以/开头的请求都转发给后台的服务器应用中去
location /stage-api/ {
rewrite ^/stage-api/(.*)$ /$1 break;
proxy_pass http://192.168.58.12:8081/;
}
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root /usr/share/nginx/html;
}
}
}