3.2.3.5 Pipeline 簡單案例
#範例: 指令碼式 node { stage('Get code') { echo '獲取程式碼' //git clone } stage('Build') { echo '構建專案程式碼' } stage('Test') { echo '測試專案功能' } stage('Deploy') { echo '部署專案' } } #範例: 宣告式 pipeline { agent any stages { stage('獲取程式碼') { steps { echo '獲取程式碼' } } stage('構建程式碼') { steps { echo '構建專案程式碼' } } stage('程式碼測試') { steps { echo '測試專案功能' } } stage('專案部署') { steps { echo '部署專案' } } } }
安裝 Pipeline 外掛 (不裝也能創流水線任務, 執行任務時可能會報錯 )
Pipeline Stage View 外掛 (執行任務,右側直接顯示執行過程,階段檢視)
#jenkins建立流水線任務 #流水線下有流水線語法,點進去,可以自動生成語句,左側可調出環境變數 #流水線輸入程式碼 pipeline { agent any #應用在任意主機上 stages { stage('獲取程式碼') { #構建任務階段 steps { echo '獲取程式碼' } } stage('構建程式碼') { steps { echo '構建專案程式碼' } } stage('程式碼測試') { steps { echo '測試專案功能' } } stage('專案部署') { steps { echo '部署專案' } } } } #點儲存,構建任務 #可以點選左側 從指定階段重新執行,可以選擇階段開始,不用從頭開始執行,方便除錯 #左側 開啟Blue Ocean,可以看到每個階段狀態,也可選階段執行,如果沒有先點上方整體重新執行 #某個構建任務下,左側回放,相當於臨時修改程式碼除錯,程式碼不儲存
3.2.7.1 案例:基本語法
steps 內部的命令,每一條單獨的命令都在當前任務的工作目錄下執行。
pipeline { environment { APP = "testapp" } agent any stages { stage ('cmd test') { steps { echo '命令測試' sh 'pwd' sh 'rm -rf *' sh 'mkdir testdir1' sh 'cd testdir1 && pwd && mkdir testdir2 && cd testdir2 && pwd' sh 'pwd && tree' #pwd還是在初始目錄下,上面的命令不影響這行 sh 'echo $WORKSPACE' sh 'echo $JOB_NAME' sh 'mkdir $WORKSPACE/$JOB_NAME' sh 'touch $WORKSPACE/$JOB_NAME/${APP}.log' sh 'pwd && tree' } } } }
Jenkins環境變數可分為內建變數和使用者自定義變數兩類
引用全域性環境變數格式有三種:
#注:sh裡不屬於pipeline語法,不支援這些變數定義 ${env.<ENV_VAR_NAME>} $env.<ENV_VAR_NAME> $<ENV_VAR_NAME> ${ENV_VAR_NAME} #注意:變數引用有時要加雙引號引起來,如:"${env.<ENV_VAR_NAME>}"
範例:宣告和使用變數
pipeline { agent any environment { NAME = "wangxiaochun" } stages { stage('declare var') { steps { script { #把who|wc -l 賦值給LOGIN變數 env.LOGIN=sh(returnStdout: true, script: "who|wc -l") } } } stage('get var') { steps { echo "NAME=${NAME}" echo "LOGIN=${LOGIN}" } } } }
範例:spring-boot-helloworld 完整案例(推薦)
#說明:環境準備 #提前在部署目標伺服器上建立目錄 mkdir -p /data/appdir #提前在Manage Jenkins-tools-Maven安裝中建立maven-3.6.3 pipeline { agent any //修改git地址和jenkins用的憑據,APP_PATH部署的路徑(目標機器建目錄) environment { REPO="git@gitlab.wang.org:example1/spring-boot-helloworld.git" CREDENTIAL="gitlab-root-private-key" APP="spring-boot-helloworld" APP_PATH="/data/appdir" } tools { //jenkins系統管理/全域性工具配置的Maven(可jenkins安裝,也可配自己安裝的) maven 'maven-3.6.3' } stages { stage('code clone') { steps { git branch: 'main', credentialsId: "${CREDENTIAL}" ,url: "${REPO}" } } stage('Build') { steps { //sh 'mvn clean package -Dmaven.test.skip=true' sh 'mvn -B -DskipTests clean package' } } stage('Test') { steps { sh 'mvn test' } } stage("停止spring boot服務"){ steps { sh 'ssh root@10.0.0.153 "killall -0 java && killall -9 java|| true"' sh 'ssh root@10.0.0.154 "killall -0 java && killall -9 java || true"' } } stage("程式碼複製"){ steps { sh "scp target/${APP}-*-SNAPSHOT.jar root@10.0.0.153:${APP_PATH}" sh "scp target/${APP}-*-SNAPSHOT.jar root@10.0.0.154:${APP_PATH}" } } stage("啟動spring boot服務"){ steps { sh 'ssh root@10.0.0.153 "nohup java -jar ${APP_PATH}/${APP}-*.jar --server.port=8888 &>/dev/null & "' sh 'ssh root@10.0.0.154 "nohup java -jar ${APP_PATH}/${APP}-*.jar --server.port=8888 &>/dev/null & "' } } } }
3.2.7.3 案例:使用憑據 Credential
1.Username with password #使用者名稱和密碼使用變數名可自行指定,Jenkins都會透過credentialsID從指定的憑證提取出來使用者名稱和密碼並賦值給指定對應的變數 withCredentials([usernamePassword(credentialsID: '<ID>', usernameVariable: '<variable to hold username>', passwordVariable: '<variable to hold password>')]) 2.SSH金鑰 withCredentials(sshUserPrivateKey(credentialsId: '<credentials-id>’, keyFileVariable: 'MYKEYFILE' passphraseVariable: 'PASSPHRASE’, usernameVariable: USERNAME')]) { // some block }
範例:構建和推送Docker映象
#確保docker信任harbor,docker info檢視Insecure Registries是否信任harbor網址 pipeline { agent any tools { maven 'maven-3.6.3' } environment { codeRepo="http://gitlab.wang.org/example1/spring-boot-helloworld.git" credential="gitlab-root-password" harborServer='harbor.wang.org' projectName='spring-boot-helloworld' imageUrl="${harborServer}/example/${projectName}" imageTag="${BUILD_ID}" harborUserName="admin" harborPassword="123456" } stages { stage('Source') { steps { git branch: 'main', credentialsId: "${credential}", url: "${codeRepo}" } } stage('Build') { steps { sh 'mvn -B -DskipTests clean package' } } stage('Test') { steps { //注意:不要修改hello()函式,否則會導致下面失敗 sh 'mvn test' } } stage('Build Docker Image') { steps { sh 'docker build . -t "${imageUrl}:${imageTag}"' } } stage('Push Docker Image') { steps { sh "echo ${harborPassword} | docker login -u ${harborUserName} --password-stdin ${harborServer}" //sh "docker login -u ${harborUserName} -p ${harborPassword} ${harborServer}" sh "docker push ${imageUrl}:${imageTag}" } } stage('Run Docker ') { steps { //sh 'ssh root@10.0.0.153 "docker rm -f ${projectName} ; docker run --name ${projectName} -p 80:8888 -d ${imageUrl}:${imageTag}"' //sh 'ssh root@10.0.0.154 "docker rm -f ${projectName} ; docker run --name ${projectName} -p 80:8888 -d ${imageUrl}:${imageTag}"' sh "docker -H 10.0.0.153 rm -f ${projectName} ; docker -H 10.0.0.153 run --name ${projectName} -p 80:8888 -d ${imageUrl}:${imageTag}" sh "docker -H 10.0.0.154 rm -f ${projectName} ; docker -H 10.0.0.154 run --name ${projectName} -p 80:8888 -d ${imageUrl}:${imageTag}" } } } }
範例:基於憑據實現構建和推送Docker映象
pipeline { agent any tools { maven 'maven-3.6.3' } environment { codeRepo="http://gitlab.wang.org/example1/spring-boot-helloworld.git" credential="gitlab-root-password" harborServer='harbor.wang.org' projectName='spring-boot-helloworld' imageUrl="${harborServer}/example/${projectName}" imageTag="${BUILD_ID}" //harborUserName="admin" //harborPassword="123456" } stages { stage('Source') { steps { git branch: 'main', credentialsId: "${credential}", url: "${codeRepo}" } } stage('Build') { steps { sh 'mvn -B -DskipTests clean package' } } stage('Test') { steps { //注意:不要修改hello()函式,否則會導致下面失敗 sh 'mvn test' } } stage('Build Docker Image') { steps { sh 'docker build . -t "${imageUrl}:${imageTag}"' } } stage('Push Docker Image') { steps { withCredentials([usernamePassword(credentialsId: 'harbor-xiaoming-password', \ passwordVariable: 'harborPassword', usernameVariable: 'harborUserName')]) { sh "echo ${harborPassword} | docker login -u ${env.harborUserName} --password-stdin ${harborServer}" //sh "docker login -u ${env.harborUserName} -p ${harborPassword} ${harborServer}" sh "docker push ${imageUrl}:${imageTag}" echo "username=${env.harborUserName}" echo "password=${harborPassword}" //列印不出密碼 } } } stage('Run Docker ') { steps { //sh 'ssh root@10.0.0.153 "docker rm -f ${projectName} ; docker run --name ${projectName} -p 80:8888 -d ${imageUrl}:${imageTag}"' //sh 'ssh root@10.0.0.154 "docker rm -f ${projectName} ; docker run --name ${projectName} -p 80:8888 -d ${imageUrl}:${imageTag}"' sh "docker -H 10.0.0.153 rm -f ${projectName} ; docker -H 10.0.0.153 run --name ${projectName} -p 80:8888 -d ${imageUrl}:${imageTag}" sh "docker -H 10.0.0.154 rm -f ${projectName} ; docker -H 10.0.0.154 run --name ${projectName} -p 80:8888 -d ${imageUrl}:${imageTag}" } } } }
Jenkinsfile放在gitlab專案中, 優點: 可以跟著版本走, 有些公司是開發寫的
#把pipeline程式碼寫入Jenkinsfile檔案中,把該檔案放入gitlab專案中 #jenkins流水線專案,流水線定義選pipeline script from SCM #SCM選git,輸入倉庫,憑據,分支 #指令碼路徑 Jenkinsfile (這裡沒寫路徑代表專案根目錄下)
相當於自由風格中的引數化構建
注意:第一個執行PipleLine沒有Build with Parameters的提示,會用預設值, 第二次執行才會出現
pipeline { agent any parameters { string(name: 'PERSON', defaultValue: 'Mr Jenkins', description: 'Who should I say hello to?') text(name: 'BIOGRAPHY', defaultValue: '', description: 'Enter some information about the person') booleanParam(name: 'TOGGLE', defaultValue: true, description: 'Toggle this value') choice(name: 'CHOICE', choices: ['One', 'Two', 'Three'], description: 'Pick something') password(name: 'PASSWORD', defaultValue: 'SECRET', description: 'Enter a password') } stages { stage('Example') { steps { echo "Hello ${params.PERSON}" echo "Biography: ${params.BIOGRAPHY}" echo "Toggle: ${params.TOGGLE}" echo "Choice: ${params.CHOICE}" echo "Password: ${params.PASSWORD}" } } } }
input 指令支援中斷當前任務,以待確認和取消
pipeline { agent any stages { stage('Example') { steps { script { def userInput = input(submitterParameter: "approver", id: "approve", message: "Provide your approval to proceed", parameters: [string(defaultValue: "approved", description: 'Please provide the message why you are approving', name: 'remarks')]) echo "Remarks:${userInput['remarks']}" echo "It was ${userInput.approver} who approved this job" } } } } }
對於pipeline來說,使用if語句或者try語句,或者when來進行條件的流程控制,這兩種方式效果相似
when是用在stage段中的指令,用於限定當前stage的執行條件
pipeline { agent any parameters { booleanParam(name:'pushImage', defaultValue: 'true', description: 'Push Image to Harbor?') } tools { maven 'maven-3.8.6' } environment { codeRepo="http://gitlab.wang.org/root/spring-boot-helloWorld.git" harborServer='harbor.wang.org' projectName='spring-boot-helloworld' imageUrl="${harborServer}/example/${projectName}" imageTag='latest' } stages { stage('Source') { steps { git branch: 'main', credentialsId: 'gitlab-root-credential', url: "${codeRepo}" } } stage('Build') { steps { sh 'mvn -B -DskipTests clean package' } } stage('Test') { steps { sh 'mvn test' } } stage('Build Docker Image') { steps { sh 'docker image build . -t "${imageUrl}:${imageTag}"' } } stage('Push Docker Image') { agent any when { expression { params.pushImage } // expression { "${params.pushImage}" == 'true' } //beforeAgent: true } steps { // input(message: 'continue?') withCredentials([usernamePassword(credentialsId: 'harbor-user-credential', passwordVariable: 'harborPassword', usernameVariable: 'harborUserName')]) { sh "echo ${harborPassword} | docker login -u ${env.harborUserName} --password-stdin ${harborServer}" sh "docker image push ${imageUrl}:${imageTag}" } } } } }
宣告性Parallel的程式碼塊中的可以巢狀多個stage,從而讓多個stage任務並行執行。
jenkins左側 開啟blue ocean才能看到效果
pipeline { agent any stages { stage( 'Deploy') { parallel { stage('deploy_proxy'){ steps { echo "部署反向代理服務" } } stage('deploy app1') { steps { echo "部署deploy_app1應用" } } stage ('deploy app2'){ stages { stage ('delete container') { steps { echo "刪除舊容器" } } stage('start container') { steps { echo "啟動新容器" } } } } } } } }
3.2.7.8 案例:觸發器
#注意:第一次需要手動執行,後續才能生效 pipeline { agent any tools { maven 'maven-3.8.6' } triggers { gitlab(triggerOnPush: true, acceptMergeRequestOnSuccess: true, //合併請求 //triggerOnMergeRequest: true, branchFilterType: 'All', //所有分支 secretToken: '62dad2cd1d9ae62686ada8dc4cdOae66') } parameters { booleanParam(name: "PUSH", defaultValue: true) environment { GitRepo="http://gitlab.wang.org/ops/spring-boot-helloWorld.git HarborServer='harbor.wang.org' ImageUrl="example/spring-boot-helloworld ImageTag="latest" stages { stage('Source'){ steps { git branch: 'main', url: "${GitRepo}" } } ....... } }
範例:透過 Gitlab 外掛實現
#第一次構建後,再次進入會顯示對應的配置 pipeline { agent any parameters { booleanParam(name:'pushImage', defaultValue: 'true', description: 'Push Image to Harbor?') } tools { maven 'maven-3.8.6' } triggers { gitlab(triggerOnPush: true, acceptMergeRequestOnSuccess: true, triggerOnMergeRequest: true, branchFilterType: 'All', addVoteOnMergeRequest: true, secretToken: '62dad2cd1d9ae62686ada8dc4cdOae66') } environment { codeRepo="http://gitlab.wang.org/devops/spring-boot-helloWorld.git" harborServer='harbor.wang.org' projectName='spring-boot-helloworld' imageUrl="${harborServer}/example/${projectName}" imageTag="${BUILD_ID}" } stages { stage('Source') { steps { git branch: 'main', credentialsId: 'gitlab-root-credential', url: "${codeRepo}" } } stage('Build') { steps { sh 'mvn -B -DskipTests clean package' } } stage('Test') { steps { sh 'mvn test' } } stage('Build Docker Image') { steps { sh 'docker image build . -t "${imageUrl}:${imageTag}"' } } stage('Push Docker Image') { agent any when { expression { params.pushImage == 'true' } beforeAgent: true } steps { // input(message: 'continue?') withCredentials([usernamePassword(credentialsId: 'harbor-user-credential', passwordVariable: 'harborUserPassword', usernameVariable: 'harborUserName')]) { sh "echo ${harborUserPassword} | docker login -u ${env.harborUserName} --password-stdin ${harborServer}" sh "docker image push ${imageUrl}:${imageTag}" } } } } }
流水線也提供了構建後的動作,常可用於訊息通知
#在stages同級別的位置,新增一個post配置段 post { always { echo '任執行結束後執行此操作' } } #例如 ... post { always { //成功失敗都執行 mail to: 'root@wangxiaochun.com', subject: "Status of pipeline: ${currentBuild.fullDisplayName}", body: "${env.BUILD_URL} has result ${currentBuild.result}" } } } #範例:綜合案例,成功發郵件,失敗發企業微信 pipeline { agent any parameters { booleanParam(name:'pushImage', defaultValue: 'true', description: 'Push Image to Harbor?') } tools { maven 'maven-3.6.3' } stages { stage('Source') { steps { echo "source" } } stage('Build') { steps { echo "Build" } } stage('Build Docker Image') { steps { echo "Build Docker image" } } stage('Push Docker Image') { agent any when { expression { "${params.pushImage}" == 'true' } } steps { echo "Push" } } stage('Run Docker ') { steps { echo "run docker" } } } post { success { mail to: 'root@wangxiaochun.com', subject: "Status of pipeline: ${currentBuild.fullDisplayName}", body: "${env.BUILD_URL} has result ${currentBuild.result}" } failure{ qyWechatNotification failNotify: true, webhookUrl: 'https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=1a4e1e74-e7d6-4376-99f6-048b01a7b7fc' } } }
4.1.3 架構和整合
4.1.3.1 SonarQube 架構
1.SonarQube Server 包括三個主要部分 Web Server: UI 介面 Search Server :為UI提供搜尋功能,基於 ElasticSearch 實現 Compute Engine Server:處理程式碼分析報告,並將之儲存到 SonarQube Database 2.SonarQube Database: 負責儲存 SonarQube 的配置,以及專案的質量快照等 #下面2個屬於客戶端 3.SonarQube Plugin: 開發語言工具idea上可以安裝該外掛,傳送資料給 SonarQube Server 4.Code analysis Scanners: 程式碼掃描器,是SonarQube Server的客戶端, 將程式碼掃描後得出報告提交給 SonarQube Server
SonarQube 分為: 社群版,開發版,企業版和資料中心版
其中只有社群版是開源免費的
#SonarQube 分兩種版本: LTS 和非 LTS 版 #官方LTS版本說明 https://www.sonarqube.org/downloads/lts/ #各種版本下載 https://www.sonarsource.com/products/sonarqube/downloads/historical-downloads/ https://www.sonarqube.org/downloads/ #LTS 版有如下版本 9.9 8.9 7.9 6.7 5.6 4.5 3.7
4.2.1 硬體要求
#硬體需求 1.小型應用至少需要2GB的RAM (最好起步4個G) 2.磁碟空間取決於SonarQube分析的程式碼量 3.必須安裝在讀寫效能較好的磁碟, 儲存資料的目錄中包含ElasticSearch的索引,伺服器啟動並執行時,將會在該索引上進行大是I/O操作 4.不支援32位作業系統
[root@SonarQube-Server ~]#vim /etc/sysctl.conf vm.max_map_count=262144 #此項必須修改,否則無法啟動 fs.file-max=65536 #此項可不改,預設值滿足要求 #此檔案可不改,可選 [root@SonarQube-Server ~]# vim /etc/security/limits.conf sonarqube - nofile 65536 sonarqube - nproc 4096 #如果以systemd 執行SonarQube,需要在service檔案配置 [servcie] ..... LimitNOFILE=65536 LimitNPROC=4096 ......
注意:SonarQube 7.9 不再支援MySQL,可以選擇安裝 PostgreSQL
4.2.4 Java 環境依賴說明
SonarQube 7.9 以上版本不再支援 java 11, 客戶端還支援java 11
#使用普通賬戶啟動sonarqube,因為sonarqube內建了ES,所以不允許能root啟動 #Ubuntu使用useradd建立使用者時預設使用/bin/sh,並且不建立家目錄 [root@SonarQube-Server ~]#useradd -s /bin/bash -m sonarqube
4.3.1.1 安裝和配置 PostgreSQL 資料庫
4.3.2 下載 SonarQube 和修改配置檔案
4.3.3 啟動 SonarQube
4.3.4 建立 service 檔案
範例: 建立 service 檔案
#先停止sonarqube [root@SonarQube-Server ~]#su - sonarqube sonarqube@SonarQube-Server:~$ /usr/local/sonarqube/bin/linux-x86-64/sonar.sh stop sonarqube@SonarQube-Server:~$ /usr/local/sonarqube/bin/linux-x86-64/sonar.sh status SonarQube is not running. sonarqube@SonarQube-Server:~$exit #建立service檔案 [root@SonarQube-Server ~]#vim /etc/systemd/system/sonarqube.service [Unit] Description=SonarQube service After=syslog.target network.target [Service] Type=simple User=sonarqube Group=sonarqube PermissionsStartOnly=true ExecStart=/usr/bin/nohup /usr/bin/java -Xms32m -Xmx32m -Djava.net.preferIPv4Stack=true -jar /usr/local/sonarqube/lib/sonar-application-8.9.2.46101.jar #ExecStart=/usr/bin/nohup /usr/bin/java -Xms32m -Xmx32m -Djava.net.preferIPv4Stack=true -jar /usr/local/sonarqube/lib/sonar-application-7.9.6.jar StandardOutput=syslog LimitNOFILE=65536 LimitNPROC=4096 TimeoutStartSec=5 Restart=always [Install] WantedBy=multi-user.target [root@SonarQube-Server ~]#systemctl daemon-reload [root@SonarQube-Server ~]#systemctl enable --now sonarqube.service [root@SonarQube-Server ~]#systemctl status sonarqube.service
下面為指令碼實現整個安裝過程
#!/bin/bash #SONARQUBE從9.9版本以後要求安裝JDK17 #支援線上和離線安裝,線上下載可能很慢,建議離線安裝 SONARQUBE_VER="9.9.4.87374" #SONARQUBE_VER="9.9.3.79811" #SONARQUBE_VER="9.9.2.77730" #SONARQUBE_VER="9.9.1.69595" #SONARQUBE_VER="9.9.0.65466" #SONARQUBE_VER="8.9.10.61524" #SONARQUBE_VER="8.9.9.56886" #SONARQUBE_VER="8.9.2.46101" #SONARQUBE_VER="7.9.2" SONARQUBE_URL="https://binaries.sonarsource.com/Distribution/sonarqube/sonarqube-${SONARQUBE_VER}.zip" SONAR_USER=sonarqube SONAR_USER_PASSWORD=123456 WORK_DIR=`pwd` HOST=`hostname -I|awk '{print $1}'` GREEN="echo -e \E[32;1m" END="\E[0m" . /etc/os-release color () { RES_COL=60 MOVE_TO_COL="echo -en \\033[${RES_COL}G" SETCOLOR_SUCCESS="echo -en \\033[1;32m" SETCOLOR_FAILURE="echo -en \\033[1;31m" SETCOLOR_WARNING="echo -en \\033[1;33m" SETCOLOR_NORMAL="echo -en \E[0m" echo -n "$1" && $MOVE_TO_COL echo -n "[" if [ $2 = "success" -o $2 = "0" ] ;then ${SETCOLOR_SUCCESS} echo -n $" OK " elif [ $2 = "failure" -o $2 = "1" ] ;then ${SETCOLOR_FAILURE} echo -n $"FAILED" else ${SETCOLOR_WARNING} echo -n $"WARNING" fi ${SETCOLOR_NORMAL} echo -n "]" echo } install_jdk() { java -version &>/dev/null && { color "JDK 已安裝!" 1 ; return; } if command -v yum &>/dev/null ; then yum -y install java-1.8.0-openjdk-devel || { color "安裝JDK失敗!" 1; exit 1; } elif command -v apt &>/dev/null ; then apt update apt install openjdk-17-jdk -y || { color "安裝JDK失敗!" 1; exit 1; } #apt install openjdk-11-jdk -y || { color "安裝JDK失敗!" 1; exit 1; } #apt install openjdk-8-jdk -y || { color "安裝JDK失敗!" 1; exit 1; } else color "不支援當前作業系統!" 1 exit 1 fi java -version && { color "安裝 JDK 完成!" 0 ; } || { color "安裝JDK失敗!" 1; exit 1; } } system_prepare () { useradd -s /bin/bash -m sonarqube cat >> /etc/sysctl.conf <<EOF vm.max_map_count=524288 fs.file-max=131072 EOF sysctl -p cat >> /etc/security/limits.conf <<EOF sonarqube - nofile 131072 sonarqube - nproc 8192 EOF } install_postgresql(){ if [ $ID = "centos" -o $ID = "rocky" ];then if [ $VERSION_ID -eq 7 ];then rpm -i http://download.postgresql.org/pub/repos/yum/reporpms/EL-7-x86_64/pgdg-redhat-repo-latest.noarch.rpm yum -y install postgresql12-server postgresql12 postgresql12-libs postgresql-12-setup --initdb else #rpm -i http://download.postgresql.org/pub/repos/yum/reporpms/EL-8-x86_64/pgdg-redhat-repo-latest.noarch.rpm color "不支援此作業系統!" 1 exit fi systemctl enable postgresql.service systemctl start postgresql.service else apt update apt -y install postgresql fi if [ $? -eq 0 ];then color "安裝postgresql完成!" 0 else color "安裝postgresql失敗!" 1 exit fi } config_postgresql () { if [ $ID = "centos" -o $ID = "rocky" ];then sed -i.bak "/listen_addresses/a listen_addresses = '*'" /var/lib/pgsql/data/postgresql.conf cat >> /var/lib/pgsql/data/pg_hba.conf <<EOF host all all 0.0.0.0/0 md5 EOF else sed -i.bak "/listen_addresses/c listen_addresses = '*'" /etc/postgresql/1*/main/postgresql.conf cat >> /etc/postgresql/*/main/pg_hba.conf <<EOF host all all 0.0.0.0/0 md5 EOF fi systemctl restart postgresql su - postgres -c "psql -U postgres <<EOF CREATE USER $SONAR_USER WITH ENCRYPTED PASSWORD '$SONAR_USER_PASSWORD'; CREATE DATABASE sonarqube ; GRANT ALL PRIVILEGES ON DATABASE sonarqube TO $SONAR_USER; EOF" } install_sonarqube() { cd $WORK_DIR if [ -f sonarqube-${SONARQUBE_VER}.zip ] ;then mv sonarqube-${SONARQUBE_VER}.zip /usr/local/src else wget -P /usr/local/src ${SONARQUBE_URL} || { color "下載失敗!" 1 ;exit ; } fi cd /usr/local/src unzip ${SONARQUBE_URL##*/} ln -s /usr/local/src/sonarqube-${SONARQUBE_VER} /usr/local/sonarqube chown -R sonarqube.sonarqube /usr/local/sonarqube/ cat > /lib/systemd/system/sonarqube.service <<EOF [Unit] Description=SonarQube service After=syslog.target network.target [Service] Type=simple User=sonarqube Group=sonarqube PermissionsStartOnly=true ExecStart=/usr/bin/nohup /usr/bin/java -Xms32m -Xmx32m -Djava.net.preferIPv4Stack=true -jar /usr/local/sonarqube/lib/sonar-application-${SONARQUBE_VER}.jar StandardOutput=syslog LimitNOFILE=65536 LimitNPROC=8192 TimeoutStartSec=5 Restart=always [Install] WantedBy=multi-user.target EOF cat >> /usr/local/sonarqube/conf/sonar.properties <<EOF sonar.jdbc.username=$SONAR_USER sonar.jdbc.password=$SONAR_USER_PASSWORD sonar.jdbc.url=jdbc:postgresql://localhost/sonarqube EOF } start_sonarqube() { systemctl enable --now sonarqube.service systemctl is-active sonarqube if [ $? -eq 0 ];then echo color "sonarqube 安裝完成!" 0 echo "-------------------------------------------------------------------" echo -e "訪問連結: \c" ${GREEN}"http://$HOST:9000/"${END} echo -e "使用者和密碼: \c" ${GREEN}"admin/admin"${END} else color "sonarqube 安裝失敗!" 1 exit fi } install_jdk system_prepare install_postgresql config_postgresql install_sonarqube start_sonarqube
用瀏覽器訪問地址: http:10.0.0.155:9000 新版預設必須登入,不支援匿名訪問 預設使用者名稱和密碼都是 admin #第一次要修改密碼 改成 123456
4.4 管理 SonarQube 伺服器
4.4.1 安裝中文支援
#上方Administration下Marketplace可以安裝外掛,需要先同意風險才能安裝 #漢化外掛,搜chinese,安裝Chinese Pack #安裝完後,點 Restart Server #外掛在這個路徑下(可以複製給其他機器) [root@SonarQube-Server ~]#ll /usr/local/sonarqube/extensions/plugins/
#上方許可權下使用者(全域性許可權可以看到對應許可權) #建立使用者 xiaoming/123456 #建立完,右側點選令牌,輸入名稱,過期時間,生成,獲得令牌(自己儲存下來,視窗一次性的) squ_8f7075d74b1f707610bc935a4b9cbf9884af769e
8.9.X 新版預設取消了匿名使用者訪問,可以在上方配置下許可權 關閉Force user authentication
4.5 部署程式碼掃描器 sonar-scanner
下載地址: https://docs.sonarqube.org/latest/analyzing-source-code/scanners/sonarscanner/ #在jenkins機器上安裝 sonar-scanner [root@jenkins-ubuntu ~]#cd /usr/local/src #新版 [root@jenkins-ubuntu src]#wget https://binaries.sonarsource.com/Distribution/sonar-scanner-cli/sonar-scanner-cli-4.6.2.2472-linux.zip [root@jenkins-ubuntu src]#unzip sonar-scanner-cli-4.6.2.2472-linux.zip [root@jenkins-ubuntu src]#ln -s /usr/local/src/sonar-scanner-4.6.2.2472-linux/ /usr/local/sonar-scanner #建立軟連結方便執行 [root@jenkins-ubuntu ~]#ln -s /usr/local/sonar-scanner/bin/sonar-scanner /usr/local/bin/ [root@jenkins-ubuntu ~]#ldd /usr/local/sonar-scanner/bin/sonar-scanner 不是動態可執行檔案 #配置sonar-scanner連線sonarqube伺服器 [root@jenkins-ubuntu ~]#vim /usr/local/sonar-scanner/conf/sonar-scanner.properties #指向sonarqube伺服器的地址和埠 sonar.host.url=http://sonarqube.wang.org:9000 sonar.sourceEncoding=UTF-8 #如果登入sonarqube server需要驗證,還要加下面兩行sonarqube的預先建立的使用者資訊 #sonar.login=admin #sonar.password=123456 #sonar.login=xiaoming #密碼方式未來會淘汰 #sonar.password=123456 #建議使用Token方式 sonar.login=squ_8f7075d74b1f707610bc935a4b9cbf9884af769e
#專案目錄下要有 sonar-project.properties檔案 [root@jenkins python-sonar-runner]#ls README.md sonar-project.properties src validation.txt #sonar-project.properties下要有這兩項,projectKey是id,projectName是sonar出報告的專案名 [root@jenkins python-sonar-runner]#cat sonar-project.properties sonar.projectKey=org.sonarqube:python-simple-sonar-scanner sonar.projectName=Python :: Simple Project : SonarQube Scanner #專案原始碼編譯生成的二進位制檔案路徑,從sonar-scanner-4.1.2起,僅java專案要求sonar.java.binaries引數 sonar.java.binaries=. #路徑寫.可以在子目錄下,如寫./target編譯前沒該檔案掃描會報錯 ... #把程式碼提交給SonarQube伺服器,自動尋找sonar-project.properties檔案 [root@jenkins python-sonar-runner]#sonar-scanner #範例:無需配置檔案sonar-project.properties,掃描專案 [root@jenkins spring-boot-helloWorld]#sonar-scanner -Dsonar.projectName=myapp -Dsonar.projectKey=myapp -Dsonar.java.binaries=./ #無需配置sonar-scanner.properties中的token,掃描專案 [root@jenkins spring-boot-helloWorld]#sonar-scanner -Dsonar.login=<token> #SonarQube網站專案能看到提交的結果
4.8.1 安裝 GitLab 和準備專案
4.8.2 安裝 Harbor 並配置 Jenkins 連線 Harbor
4.8.3 安裝 Jenkins 並安裝相關外掛
4.8.4 在 Jenkins 建立憑據連線 Harbor
4.8.5 在 Jenkins 安裝 Maven 工具
4.8.6 在 Jenkins 上配置 Maven 環境
4.8.7 在 Jenkins 建立連線 GitLab 的憑據
4.8.8 在 Jenkins 上修改 Docker 的 socket 檔案許可權
4.8.9 安裝 SonarQube 並建立使用者和令牌
4.8.10 在 SonarQube 新增 Jenkins 的回撥介面
jenkins需要知道SonarQube的報告質量如何
#sonarqube上方配置下,左側配置下webhook,點選建立 #名稱隨便起,這裡寫 jenkins #URL(jenkins固定地址,保證後面域名可以解析) http://jenkins.wang.org:8080/sonarqube-webhook #密碼防止攻擊使用,隨意輸入,下面透過命令生成密碼 [root@ubuntu2204 ~]#openssl rand -base64 21 PiTVO6epEqIUlvU1DBoK1bVS3bkh #點選建立
4.8.12 在 Jenkins 建立訪問 Sonarqube的令牌憑據
用前面小節生成的Sonarqube中使用者令牌,在Jenkins 建立憑據
#jenkins左側系統管理,憑據管理,建立一個憑據 #型別(光一個key) Secret text #Secret輸入上面生成的使用者令牌 squ_8f7075d74b1f707610bc935a4b9cbf9884af769e #ID/描述寫 sonarqube-xiaoming-secret
#jenkins左側系統管理,系統配置 #SonarQube servers下,點選Add SonarQube #Name(未來呼叫的就是這個名字)寫 sonarqube #Server URL 寫 http://10.0.0.155:9000 #Server authentication token 選上面的憑據 sonarqube-xiaoming-secret #儲存
上面已經手動安裝了, 這裡不再進行安裝
如果未安裝,也可以在jenkins系統管理/全域性工具配置中SonarQube Scanner安裝
4.8.15 準備微信機器人
4.8.16 建立任務使用 Pipeline Script
#jenkins新建pipeline任務 pipeline { agent any tools { maven 'maven-3.6.3' } environment { codeRepo="http://gitlab.wang.org/example1/spring-boot-helloworld.git" harborServer='harbor.wang.org' credential='gitlab-root-password' projectName='spring-boot-helloworld' imageUrl="${harborServer}/example/${projectName}" imageTag="${BUILD_ID}" } stages { stage('Source') { steps { git branch: 'main', credentialsId: "${credential}", url: "${codeRepo}" } } stage("SonarQube Analysis") { steps { //這個名字要寫在jenkins上配SonarQube 伺服器名字 withSonarQubeEnv('sonarqube') { //java這個寫法也支援向sonar伺服器傳送程式碼掃描 sh 'mvn sonar:sonar -Dsonar.java.binaries=target/' } } } stage("Quality Gate") { steps { timeout(time: 30, unit: 'MINUTES') { //質量閾透過往下執行,失敗就不往下執行 waitForQualityGate abortPipeline: true } } } stage('Build') { steps { sh 'mvn -B -DskipTests clean package' } } stage('Test') { steps { sh 'mvn test' } } stage('Build Docker Image') { steps { sh 'docker image build . -t "${imageUrl}:${imageTag}"' // input(message: '映象已經構建完成,是否要推送?') } } stage('Push Docker Image') { steps { withCredentials([usernamePassword(credentialsId: 'harbor-xiaoming-password', passwordVariable: 'harborPassword', usernameVariable: 'harborUserName')]) { sh "docker login -u ${env.harborUserName} -p ${env.harborPassword} ${harborServer}" sh "docker image push ${imageUrl}:${imageTag}" } } } stage('Run Docker') { steps { //sh 'ssh root@10.0.0.202 "docker rm -f ${projectName} && docker run --name ${projectName} -p 80:8888 -d ${imageUrl}:${imageTag}"' //sh 'ssh root@10.0.0.203 "docker rm -f ${projectName} && docker run --name ${projectName} -p 80:8888 -d ${imageUrl}:${imageTag}"' sh "docker -H 10.0.0.153:2375 rm -f ${projectName} && docker -H 10.0.0.153:2375 run --name ${projectName} -p 80:8888 -d ${imageUrl}:${imageTag}" sh "docker -H 10.0.0.154:2375 rm -f ${projectName} && docker -H 10.0.0.154:2375 run --name ${projectName} -p 80:8888 -d ${imageUrl}:${imageTag}" } } } post { success { mail to: 'root@wangxiaochun.com', subject: "Status of pipeline: ${currentBuild.fullDisplayName}", body: "${env.BUILD_URL} has result ${currentBuild.result}" } failure{ qyWechatNotification failNotify: true, webhookUrl: 'https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=938039da-1123-4862-b47b-f0396020f45b' } } }