sonarqube k8s drone helm

Sonarqube Code Quality Review 源碼檢測

羅詠茹 Vickey Luo 2021/11/30 13:03:28
3987

一、Sonarqube 簡介

* 支援超過25種程式語言[2]:Java、C/C++、C#、PHP、Flex、Groovy、JavaScript、Python、PL/SQL、COBOL等。(不過有些是商業軟體外掛程式)

* 可以在Android開發中使用

* 提供重複代碼、編碼標準、單元測試、代碼覆蓋率、代碼複雜度、潛在Bug、注釋和軟體設計報告[3][4]

* 提供了指標歷史記錄、計劃圖(「時間機器」)和微分檢視

* 提供了完全自動化的分析:與Maven、Ant、Gradle和持續整合工具(Atlassian Bamboo、Jenkins、Hudson等)[5][6][7]

* 與Eclipse開發環境整合

* 與JIRA、Mantis、LDAP、Fortify等外部工具集

* 支援擴充外掛程式[8][9]

* 利用SQALE計算技術債務[10]

* 支援Tomcat。不過計劃從SonarQube 4.1起終止對Tomcat的支援[11]。

 

> wikipediia

 

二、運作架構

 

SonarQube7.9及其以後版本將不再支持Mysql,所以這裡推薦設置PostgreSQL作為SonarQube的數據庫。

 

 

> https://programmer.group/k8s-install-sonarqube.html

 

三、工作原理

 

在典型的開發過程中:

1. 開發人員在IDE中開發和合併代碼(最好使用SonarLint在編輯器中接收即時反饋),然後將其代碼簽入ALM。

2. 組織的持續集成(CI)工具可以檢出,構建和運行單元測試,而集成的SonarQube掃描儀可以分析結果。

3. 掃描程序將結果發佈到SonarQube服務器,該服務器通過SonarQube界面,電子郵件,IDE內通知(通過SonarLint)以及對拉取或合併請求的修飾(使用Developer Edition及更高版本時)向開發人員提供反饋。

 

四、實際安裝

 

環境說明:

相較於安裝在本地端的方式掃描原始碼; 此次是在k8s cluster上安裝sonarqube,並透過Helm去管理k8s (以chart形式); drone server跑pipeline, sonarqube server 收集掃描資料.

補充: 未免關機後資料消失, 選擇將資料以pv,pvc形式連到nfs server;此篇文章是以/home/public路徑設為自己架設的nfs server ; 而由於此篇chart中定義pull下來的image 有client端可執行sonar-scanner, 所以不需要像本地端安裝一樣另外安裝client端.

 

> https://artifacthub.io/packages/helm/oteemo-charts/sonarqube

 

helm repo add oteemo-charts https://oteemo.github.io/charts

helm search repo sonarqube

helm pull oteemo-charts/sonarqube --untar

 

★ 將以下文件位置改成符合環境需要

* values.yaml: hosts, persistence, globalpath

* templates/deployment.yaml: container.volumeMounts

* charts/postgres/values.yaml: mountpath, subpath

 

1. 用判斷式決定要吃什麼資源,是sonarqube內建的pvc 還是要吃我們給他的資源

圖片說明

(在values.yaml檔案中,persistence欄位為true且existingClaim有檔案名稱, 意思是我們將資源定義在sonarqube-my-data-pvc中,請以此為資源依據,不需要理會sonarqube內建的pvc.)

將此次定義的pv,pvc寫在同一份文件分享如下;而因為postgresql也有自己的pv,pvc可定義,一樣寫在同一份即可.不需要再寫入sub chart.

 

2. subpath目的是為了清空目錄時, 不會誤刪到其他檔案, 所以需要subpath隔開, 上方install以後須確認目錄

★ 相較於subpath;資料未掛出為emptydir形式, 則chart中sonar與postgresql可能發生pvc衝突,i.e.兩邊都有定義pvc, 重複向pv索要資源因而衝突, 解方:其中一個pvc設置enable=false,ingress設置enable為true;emptydir最明顯的缺點是,由於資料並未掛出(此篇有,是透過subpath連到nfs server)資料庫若刪掉,所有資料全部消失.

> https://kubernetes.io/zh/docs/concepts/storage/volumes/#emptydir

↑↑↑此為values.yaml 

圖片說明

(helm chart對於空格/階層/欄位名稱非常嚴謹,若定義的欄位名稱像是下圖的templates/deployment.yaml中mountPath欄位前方沒有-,或者下方未給name,空行錯誤,或者p沒有大寫,都會讓helm讀不懂這份文件進而部署失敗;所以這邊提示sonarqube 8.2版本後是/opt/sonarqube路徑 那便不可更改.所以此處在下方以globalPath方式添加一層;並在deployment.yaml中也加上一層互相參照)

↓↓↓此為templates/deployment.yaml

(此處mountPath,subPath均定義name:sonarqube的掛載點;指涉相同掛載點名稱定義subpath位置如下)

 

* 這邊的data是postgre的 也需要收到sonarqube中

 

* (此畫面是截圖自charts/postgres/values.yaml)由於要避免相同目錄名稱data 資料寫入錯誤地方 所以subpath如此命名.

 

* 可用k9s確認pod有無running, 若沒有須除錯; 若顯示ready0/1,status卻為running:可能因為pod已起來,但程式端未響應.可以按l或d查看詳細log,並esc回到上一層.

 

* sonarqube server順利安裝後, 可以admin/admin登入

 

★ 會遇到問題: data權限不夠

因為postgresql 與sonarqube 都有相同資料夾data

所以當最一開始helm install, sonarqube pod尚未起來的時候,

postgresql 的data先起來了, sonarqube就會想要放資料進data才會報錯: 權限不足

 

* 看起來運行沒問題. 唯一的問題是sonarqube一跑起來, memory與cpu會飆高;測試helm uninstall , CPU與MEM立馬下降

 

3. sonarqube要設定ingress 還是開nodeport 可設定

* 此次設定為內部ip的ingress設定, 因目前也沒有大流量的問題.

* 也不要曝露在www.elite-erp.com.tw這個網域的ingress上

 

進階設定

★ 考量到erp環境之前曾經設定過annotation根目錄/直接轉到erp服務登入畫面,若此次sonarqube照上面如此設定, 則/目錄會依據ingress規則先轉到sonarqube,而不會進erp.(當然sonarqube也有自己的annotation;這邊是因為有優先順序的關係)

 

㊣以下截圖結果為URL IP:30080/sonarqube 可以這樣subpath方式導向到sonarqube登入畫面之設定方式.

 

若已設定完成以k9s檢視pod卻未順利部署; 可選擇uninstall 再重新部署. 可能是因為pv,pvc調整過.

---

 

* 測試前hosts.name為k8s,URL k8s:30080可以看到sonarqube server; 測試後,將hosts.name由k8s改成""空值; 因為新版的k8s name 無法識別IP; 這邊改空值可以URL IP:port順利看到畫面:

 

admin/admin 帳密登入

 

helm install 'name' -n 'namespaces' .

kubectl get po -n 'namespaces'

 

指令說明

helm部署管理k8s資源,以namespaces隔開,部署時指定namespaces到指定空間.再以指令檢視該空間所有pod以驗證是否部署成功.

若想在install以前確認helm charts有無任何錯誤需修正之處可以下指令檢視並除錯

#專門檢查格式錯誤

helm lint

#檢查格式以外錯誤

helm install 'name' -n 'namespaces' . --dry-run --debug

當然,還是有很小部分的機率是驗證指令過了,但仍無法順利部署=顯示錯誤訊息.

 

五、和 Drone-CI 整合流程

★ 照sonarqube server顯示,有區分為java,c#與其他程式語言. 分兩種掃描原碼方式:以下為plugin 執行指令

1. java,c# 

對於許多語言,您只需要源代碼即可進行分析。對於Java,您需要同時提供源代碼和編譯後的字節碼。我猜您不是項目所有者或貢獻者。您應該諮詢那些人,以了解如何編譯該項目。編譯完成後,您需要將二進製文件的位置傳遞給分析。

> 此處引用說明java與其他程式語言的不同處https://community.sonarsource.com/t/sonar-scanner-error-sonar-java-binaries-property/8602

* 以下為plugin掃描指令,由於java環境需要mvn, 所以融合在maven-build步驟,添加第58行.此處可調整成適合自己的環境及設置在sonar server的token

* 以erputils中的程式碼做測試,掃描結果畫面在最下方.

46   - name: maven-build
47     image: 10.20.30.144:8444/drone-plugin/maven:3.6.1-jdk-8
48     volumes:
49       - name: cache
50         # dependencies are saved in the .m2 directory
51         path: /root/.m2
52     commands:
53       - mvn --version
54       - mvn clean install
55       - mvn package -DskipTests=true -f pomWithTomcat.xml
56       - pwd
57       - ls -al ./target
58       - mvn sonar:sonar -Dsonar.projectKey=tursdaytest -Dsonar.host.url=http://192.168.131.3:30080 -Dsonar.login=fba18c9394cb8afe82c8919dc3580c6735d6586f
59     when:
60       branch:
61         include:
62           - release/stg
63           - release/pre-prod
64           - master****

 

2. 這次開發的語言是除了java,c#以外的其他程式語言

* 以vote中的程式碼做測試,掃描結果畫面在最下方.

47   - name: code-analysis
48     image: aosapps/drone-sonar-plugin
49     settings:
50       sonar_host:
51         from_secret: sonar_host
52       sonar_token:
53         from_secret: sonar_token
54     commands:
55       - sonar-scanner -Dsonar.projectKey=votetest -Dsonar.sources=. -Dsonar.host.url=http://192.168.131.3:30080 -Dsonar.login=7ca1400dc77bd12421ee7b1bcdd4f48ee37de422
56     when:
57       branch:
58         - production

 

---

URL serverIP:port

更多設定

* sonarqube server每一個分析當成一個project, 未設定一開始程式碼掃描結果是public;在drone sonar plugin可先設定成private,這樣就不用等第一次掃完,再進去UI修改(程式碼已洩漏)所以步驟應該在掃描分析步驟之前.

* 將明碼部分以ENV餵進去變成變數. 原本以settings形式,有冗餘前綴PLUGIN所以用ENV形式較完善.

 

#erputils有java

46   - name: maven-build
47     image: 10.20.30.144:8444/drone-plugin/maven:3.6.1-jdk-8
48     environment:
49       SONAR_HOST:
50         from_secret: sonar_host
51       SONAR_TOKEN:
52         from_secret: sonar_token
53     volumes:
54       - name: cache
55         # dependencies are saved in the .m2 directory
56         path: /root/.m2
57     commands:
58       - mvn --version
59       - mvn clean install
60       - mvn package -DskipTests=true -f pomWithTomcat.xml
61       - pwd
62       - ls -al ./target
63       - curl -X POST "http://$SONAR_TOKEN@$SONAR_HOST/api/projects/update_visibility?project=erputils&visibility=private"
64       - mvn sonar:sonar -Dsonar.projectKey=erputils -Dsonar.host.url=http://$SONAR_HOST -Dsonar.login=$SONAR_TOKEN
65     when:
66       branch:
67         include:
68           - release/stg
69           - release/pre-prod
70           - master

 

#將step拆成兩個:更好的寫法 交接可以更清楚
102   - name: maven-build
103     image: 10.20.30.144:8444/drone-plugin/maven:3.6.1-jdk-8
104     volumes:
105       - name: cache
106         # dependencies are saved in the .m2 directory
107         path: /root/.m2
108     commands:
109       - mvn --version
110       - mvn clean install
111       - mvn package -DskipTests=true -f pomWithTomcat.xml
112       - pwd
113       - ls -al ./target
114     when:
115       branch:
116         include:
117           - release/stg
118           - release/pre-prod
119           - master
120
121   - name: sonar-scan
122     image: 10.20.30.144:8444/drone-plugin/maven:3.6.1-jdk-8
123     volumes:
124       - name: cache
125         # dependencies are saved in the .m2 directory
126         path: /root/.m2
127     environment:
128       SONAR_HOST:
129         from_secret: sonar_host
130       SONAR_TOKEN:
131         from_secret: sonar_token
132     commands:
133       - curl -X POST "http://$SONAR_TOKEN@$SONAR_HOST/api/projects/update_visibility?project=tperp\&visibility=private"
134       - mvn sonar:sonar -Dsonar.login=$SONAR_TOKEN -Dsonar.host.url=http://$SONAR_HOST -Dsonar.projectKey=$DRONE_REPO_NAME -Dsonar.projectVersion=$DRONE_BUILD_NUMBER
135     when:
136       branch:
137         - release/pre-prod

 

#testsonascannojava(用vote來掃描)

49     environment:
50       SONAR_HOST:
51         from_secret: sonar_host
52       SONAR_TOKEN:
53         from_secret: sonar_token
54     commands:
55       - curl -X POST "http://$SONAR_TOKEN@$SONAR_HOST/api/projects/update_visibility?project=votetest&visibility=private"
56       - sonar-scanner -Dsonar.projectKey=votetest -Dsonar.sources=. -Dsonar.host.url=http://$SONAR_HOST -Dsonar.login=$SONAR_TOKEN

> https://gist.github.com/mr-exz/a304acaedd3bac248cf51df70dde95e9

 

* 再優化: 明碼藏起來

---

 

* 選取右上角 新增專案(此處設定名稱,使用token選擇程式語言可看到插入plugin指令的提示, 目前不需要下載client端即可掃描) 或產token

 

 

* 將token 複製進.drone.yml文件 from_secret欄位 (非明碼傳輸較安全)

 

* 在drone server 的 settings建立secret name與value ; 若不方便建立, 可在pipeline 的.drone.yml寫明碼即可.

 

六、Demo

 

* 將sonarqube plugin 放入pipeline的文件 .drone.yml

* 設定觸發pipeline條件為push 或 merge (視需求)

* 一觸發會自動掃描原始碼, 並傳送結果到sonar server(可於web-ui介面看到)

 

 

* 以erputils測試(主要以java開發)

 

* 以vote測試(非java開發)

 

---

 

前述均為 maven 打包交由 sonarqube scan ; 在 eval 這幾包是經由 gradle 打包交給 sonarqube scan,指令也有所不同. 以下呈現 gradle 打包及掃瞄指令。

 

進階設定- gradle 打包

 

  - name: gradle-build
    image: 10.20.30.144:8444/drone-plugin/gradle:6.8
    volumes:
      - name: cache
        path: /home/gradle/.gradle
    commands:
      - gradle build
      - ls -al ./build/libs
    when:
      branch:
        include:
          ## stg branch
          - release/stg
          ## preprod branch
          - release/preprod
          ## prod branch
          - master

 

進階設定 - gradle 掃描

  - name: sonar-scan
    image: 10.20.30.144:8444/drone-plugin/gradle:6.8
    volumes:
      - name: cache
        # dependencies are saved in the .gradle directory
        path: /home/gradle/.gradle
    environment:
      SONAR_HOST:
        from_secret: sonar_host
      SONAR_TOKEN:
        from_secret: sonar_token
    commands:
      - gradle sonarqube -Dsonar.login=$SONAR_TOKEN -Dsonar.host.url=https://$SONAR_HOST -Dsonar.projectKey=$DRONE_REPO_NAME -Dsonar.projectVersion=$DRONE_BUILD_NUMBER
    when:
      branch:
        - release/preprod

 

只有 maven 有針對 java 特定寫法進行掃描, 另兩種打包方式 gradle, npm(node.js) 無特定寫法. 因為非java語言.

 

補充說明:maven build

 

  - name: maven-build
    image: 10.20.30.144:8444/drone-plugin/maven:3.6.1-jdk-8
    volumes:
      - name: cache
        # dependencies are saved in the .m2 directory
        path: /root/.m2
    commands:
      - mvn --version
      - mvn clean install
      - mvn package -DskipTests=true -f pomWithTomcat.xml
      - pwd
      - ls -al ./target
    when:
      branch:
        include:
          - release/stg
          - release/pre-prod
          - master

 

補充說明 - npm build & scan

 

  - name: npm-build-prod
    image: 10.20.30.144:8444/drone-plugin/node:12-slim
    volumes:
      - name: cache
        path: /drone/src/node_modules
    commands:
      - npm install
      - npm run build
    when:
      branch:
        ## prod branch
        - master
  - name: sonar-scan
    image: 10.20.30.144:8444/drone-plugin/drone-sonar-plugin
    volumes:
      - name: cache
        path: /drone/src/node_modules
    environment:
      SONAR_HOST:
        from_secret: sonar_host
      SONAR_TOKEN:
        from_secret: sonar_token
    commands:
      - sonar-scanner -Dsonar.projectKey=$DRONE_REPO_NAME -Dsonar.sources=. -Dsonar.host.url=https://$SONAR_HOST -Dsonar.login=$SONAR_TOKEN
    when:
      branch:
        - preprod

 

七、常遇到的狀況, 可解決方法

 

1. 因為有定義pv,pvc,故無法以helm upgrade 直接重新部署, 需要helm uninstall, 再install. 同時可以kubectl get pv or pvc檢視資源是否已經uninstall, 若無,可以kubectl delete pv or pvc協助.

3. 在掛載遠方資料夾/home/public; es要給足權限. /home/public是我的nfs server;也可更改成各位nfs server的路徑.若自己架設nfs server, 可參考

> http://linux.vbird.org/linux_server/0330nfs.php

 

八、Reference

 

* 延伸資料

> 1. https://www.youtube.com/watch?app=desktop&v=vE39Fg8pvZg

> 2. https://www.youtube.com/watch?v=31igoWxauEQ

> 3. https://programmer.group/k8s-install-sonarqube.html

 

* 此篇以helm install chart管理並部署k8s資源,下方官方文章是以kubectl apply -f的方式部署, 但無法受到helm管理,將來部署或解安裝,kubectl apply的資源如同孤兒.

> https://kubernetes.io/docs/tasks/configure-pod-container/configure-persistent-volume-storage/

 

* 異常java.lang.RuntimeException:無法以root身份運行elasticsearch

    * SonarQube將啟動Elasticsearch進程,並且運行SonarQube本身的同一帳戶將用於Elasticsearch進程。由於Elasticsearch不能以形式運行root,這意味著SonarQube也不能以形式運行。您必須選擇root運行SonarQube的其他非帳戶,最好是專用於此目的的帳戶。

> 我們以helm chart部署, 使用者預設即sonarqube 非root.

> https://docs.sonarqube.org/latest/setup/install-server/

 

* Running Ssonarqube As Service

> https://docs.sonarqube.org/latest/setup/operate-server/

 

九、切權限

### A.權限規劃

1. admin

2. erpuser 負責創建sonar_token

3. 很多一般user: 只能看到他們自己的project , 可以對這個project有權限做事.

 

> ★ 以下為vote-user與voteapi-user 能看到的project截圖

> ★ 因為開發端設置目錄名稱的關係, 所以project name 由sonarqube自行抓取建立, project name相同, 但可於project右方顯示的主要開發語言區隔: javascript(前端) java(後端); 點選進入project 可於右側點選欄位看到project key,亦可以看到是vote、voteapi

 

login : admin

login : vote-user

login : voteapi-user

 

### B.權限設置順序

使用時機: 有新的repo出現要掃源碼時

 

1. administration > security > create permission templates (設定好此project專屬的權限)

2. 首頁右上角 create project (此處prject key與上方設置一致)

3. 點選進入單一project > 右上選取project settings > permissions > 右上apply permission template (選取剛剛1.建立的templates)

 

羅詠茹 Vickey Luo