SonarQube로 코드 품질 관리하기 (feat. gitlab, jenkins, docker)

2024. 10. 19. 15:16·Infra

sonarqube

1. SonarQube란?

SonarQube는 정적 분석 도구 중 하나로 20가지 이상의 언어와 프레임워크를 지원한다. 코드 품질을 관리하기

사람이 코드를 작성하거나, 코드 리뷰를 할 때 놓치기 쉬운 기본적인 실수들을 하지 않게 도와준다. PullRequest와 연동하면 분석 결과를 리뷰도 달아주기까지 한다.

 

1-1. 어떤 것을 우리에게 제공해주는가?

  • SonarQube를 활용하면 문제에 대한 분석을 자동화할 수 있다. 아래와 같은 지표를 한 눈에 파악할 수 있다.
  1. Code Smell: 변경 가능성, 모듈성, 이해가능성, 테스트 용의성, 재사용성 등을 분석
  2. 버그: 잠재적인 버그. 런타임에 예상되는 동작을 하지 않는 코드
  3. 취약점: 해커들에게 잠재적 약점이 될 수 있는 보안상 이슈. (ex. SQL Injection, XSS 공격)
  4. 코드 중복
  5. 복잡도: 순환 복잡도 측정, 코드 논리적 흐름 상 존재하는 인지 복잡도 측정
  6. 사이즈: 코드 라인 전체 라인 수 구문, 함수 클래스 파일, 디렉터리 주석 수, 코멘트 비율 등

 

1-2. 예시

분석 결과의 구체적인 예시는 아래와 같다.

  • 문자열 하드코딩
  • 불필요한 주석 (ex. 코드 자체를 주석 처리, Todo 주석)
  • 테스트에서 isEqualTo(0) 사용 (대신 isZero() 사용할 수 있다.)
  • 테스트에서 너무 많은 단언문을 남발

 

2. SonarQube 환경 구축하기

  • SonarQube 환경을 구축하기 위해 Docker Compsoe를 활용했다. (Docker와 Docker Compose 설치를 해야 아래 과정대로 따라올 수 있다.)
  • 소나큐브는 정적 분석 결과를 postgres DB를 사용해 저장하기 때문에 기본적으로 2개의 컨테이너(서버와 DB)가 필요하다.

2-1. Docker Compose

version: "3.9"

services:
    sonarqube:
        image: sonarqube:lts-community
        depends_on:
            - sonar_db
        environment:
            SONAR_JDBC_URL: jdbc:postgresql://sonar_db:5432/sonar
            SONAR_JDBC_USERNAME: username
            SONAR_JDBC_PASSWORD: password
        ports:
            - "9000:9000"
        volumes:
            - sonarqube_conf:/opt/sonarqube/conf
            - sonarqube_data:/opt/sonarqube/data
            - sonarqube_extensions:/opt/sonarqube/extensions
            - sonarqube_logs:/opt/sonarqube/logs
            - sonarqube_temp:/opt/sonarqube/temp

    sonar_db:
        image: postgres:13
        environment:
            POSTGRES_USER: username
            POSTGRES_PASSWORD: password
            POSTGRES_DB: sonar
        volumes:
            - sonar_db:/var/lib/postgresql
            - sonar_db_data:/var/lib/postgresql/data

volumes:
    sonarqube_conf:
    sonarqube_data:
    sonarqube_extensions:
    sonarqube_logs:
    sonarqube_temp:
    sonar_db:
    sonar_db_data:

 

2-2. SonarQube 실행하기

  • 위의 docker compose 스크립트를 실행하고 9090 포트에 접속해보자. 처음 홈페이지에 들어가면 로그인을 해야하는데 SonarQube의 기본 아이디, 비밀번호를 사용하면 된다.
  • 아이디: admin, 비밀번호: admin

SonarQube Login

  • 로그인을 하면 패스워드를 새롭게 설정할 수 있다.

Update SonarQube Password

  • 접속이 완료되면 아래와 같이 프로젝트 생성창이 뜬다.

SonarQube Homepage

 

 

 

3. SonarQube 연동하기 (Jenkins, GitLab)

  • 아래 설정을 하기 전에 Jenkins에서 형상 관리 저장소(GitHub, GitLab 등) 설정이 되어 있다고 가정하고 진행한다. (만약 설정되어 있지 않다면 플러그인을 설치하고 필요한 Secret 설정을 해줘야 한다.)

3-1. SonarQube Token 발급하기

  • Jenkins에서 사용할 SonarQube의 토큰을 먼저 생성해야 한다.
  • 프로젝트를 생성하기 위해 Manually를 클릭해준다.

SonarQube Homepage

 

  • 프로젝트를 생성해준다.

Create a project

 

  • 그렇게 새로 생성한 프로젝트에는 아래와 같이 Locally 메뉴가 있을 것이다. 클릭하자.

Click the Locally button

  • 여기서 토큰 이름과 기한을 설정해 토큰을 생성하면 된다.

Analyze your project

  • Generate 버튼을 클릭하면 아래와 같이 토큰이 생성된다. 토큰을 메모장에 따로 저장해둔 다음 Contiune 버튼을 클릭하자.

Provide a token

  • SonarQube를 실행하기 위한 정보를 보여준다.
  • plugins는 Spring Project에 설정해줘야 한다.
    • 사실 여기에 함정이 있다. 뒤에 5. 연동 시 주의사항에서 plugins 버전 설정을 참고 바람.
  • 그리고 "and run the following command:" 에 작성된 명령어는 Jenkins 파이프라인에서 직접 실행할 명령어다.

Run analysis on your project

 

3-2. SonarQube Jenkins Webhook 설정하기

  • 최상단에 있는 메뉴인 Administration을 클릭하고 Configuration > Webhooks를 클릭하자.
  • 우측 상단에 있는 Create 버튼을 클릭한다.

SonarQube Webhook

  • URL은 Jenkins 서버의 endpoint()를 작성해주면 된다.
    • endpoint 형식: http://{jenkins_url}:{port}/sonarqube-webhook/

Create SonarQube Webhook

 

3-3. Jenkins 설정하기 - SecretKey

  • 본격적으로 SonarQube를 설정하기 전에 SecretKey를 설정해보도록 하자.
  • Jenkins 관리 > Credentials를 클릭한다. 

Jenkins Security Setting

  • Stores scoped to Jenkins의 System을 클릭한다

Store scoped to Jenkins

  • Global credentials를 클릭한다.

Global credentials

  • Add Credentials 버튼을 클릭한다.
  • 종류를 Secret text로 설정한 뒤, SonarQube에서 발급받은 토큰을 등록해준다.

New credentials

 

 

3-4. Jenkins 설정하기 - SonarQube Server

  • 그렇다면 이제 젠킨스 파이프라인을 만들어보자.
  • 먼저 SonarQube 플러그인이 설치되어 있어야 한다. SonarQube Scanner와 Sonar Quality Gates를 설치해주자.

Jenkins Plugins

  • 설치가 완료되었다면 Jenkins 관리 > System의 SonarQube servers 설정을 확인하자.
  • Server URL은 SonarQube의 서버 URL을 넣어주면 된다. 만약 Jenkins와 SonarQube가 같은 가상 머신 안에 있다면 Default 설정인 localhost:9000으로 두어도 상관없다.
  • Server authentication token에는 위에서 입력한 credentials의 토큰을 드롭다운으로 선택할 수 있다. sonarqube token을 선택해준다.

register SonarQube servers

  • SonarQube Scanner Server를 따로 설치하지 않았다면 Jenkins 내에서 Install automatically를 체크하여 자동으로 설치하도록 구성해야 한다. (Jenkins 관리 > Tools > SonarQube Scanner installations)

SonarQube Scanner installations

 

 

3-5. Jenkins 파이프라인 스크립트 작성

pipeline {
    agent any

    stages {
        stage('checkout') {
            steps {
                script {
                    SCM_VARS = git branch: '${gitlabSourceBranch}', credentialsId: '${CREDENTIAL}', url: '${GIT_URL}'
                    env.GIT_COMMIT = SCM_VARS.GIT_COMMIT
                }
            }
        }
        
        stage('Build') {
            steps {
                sh './gradlew clean build'
            }
        }
        
        stage('SonarQube analysis') {
            steps{
                withSonarQubeEnv('sonarqube'){
                    sh """
                    ./gradlew clean sonar \
                    -Dsonar.projectKey={SONAR_PROJECT} \
                    -Dsonar.host.url=${SONAR_URL} \ 
                    -Dsonar.login=${SONAR_TOKEN}
                    """
                }
            }
        }
        
        stage('SonarQube Quality Gate'){
            steps{
                timeout(time: 1, unit: 'MINUTES') {
                    script{
                        echo "Start"
                        def qg = waitForQualityGate()
                        echo "Status: ${qg.status}"
                        if(qg.status != 'OK') {
                            echo "NOT OK Status: ${qg.status}"
                            updateGitlabCommitStatus(name: "SonarQube Quality Gate", state: "failed")
                            error "Pipeline aborted due to quality gate failure: ${qg.status}"
                        } else{
                            echo "status: ${qg.status}"
                            updateGitlabCommitStatus(name: "SonarQube Quality Gate", state: "success")
                        }
                        echo "End"
                    }
                }
            }
        }
    }
}
  • "SonarQube Analysis" stage는 대상 SonarQube Server를 지정하여 분석을 진행한다.
    • withSonarQubeEnv는 Jenkins 관리 > System에 등록한 SonarQube Servers의 Name과 매핑된다.
  • "SonarQube Quality Gate" stage는 SonarQube Servers에서 분석 결과를 응답하기까지 대기한다.
    • 대기 시간을 지정하여 무한정 대기하는 상태를 방지할 수 있다. waitForQualityGate를 사용해 Server에서 분석을 완료하고 상태를 반환할때까지 파이프라인을 중단시키는 시간을 지정한다.

 

4. GitLab으로 리포팅하기

  • 이 기능은 Developer Edition 이상에서만 가능하다.
  • 아래 블로그 링크에서 특정 플러그인을 사용해 무료로 Github 리포팅 사용하는 방법을 알아볼 수 있다.
  • [Server] 소나큐브(SonarQube) 커뮤니티 무료 버전에서 PR 데코레이션(Pull Request Decoration) 설정 적용하기

 

4-1. GitLab OAuth2 로그인 애플리케이션 구성 및 SonarQube 등록

  • 아래 문서를 참고해 OAuth2 애플리케이션을 구성해주자
  • SonarQube Docs: GitLab
  • GitLab에서 애플리케이션을 만들면 Application ID와 Secret을 알려준다. 그 정보를 SonarQube에 입력해줘야 한다.
  • Administration > Configuration > General Setting > Authentication
  • GitLab 주소 및 Application ID와 Secret을 입력해준다.

SonarQube > Configuration > General Setting Page

 

4-2. SonarQube에 GitLab 연동하기

  • SonarQube Docs: Setting up the GitLab integration at the global level
  • GitLab에서 Access Token을 발급받는다. scope는 api와 read_api 두 가지를 선택하도록 하자.

Add a personal access token page

  • Administration > Configuration > General Settings > DevOps Platform Integrations
  • Create configuration 버튼을 클릭해 연동 정보를 입력해주자.

devops platform

  • API URL을 입력할 때 뒤에 /api/v4 라는 path를 붙여줘야 한다.
  • 도메인 주소는 현재 사용하고 있는 gitlab의 주소를 넣어주면 된다.

add gitlab api

 

4-3. GitLab Webhook 등록하기

  • 먼저 Jenkins의 파이프라인에서 Build when a change is pushed to GitLab~~ 설정을 해준다.

jenkins pipeline

  • 만약 merge request에 새로운 commit이 들어갔을 때도 파이프라인이 재실행되길 원한다면 설정을 아래와 같이 수정해주자.

jenkins pipeline setting

  • 고급 설정에서 Secret token의 Generate 버튼을 클릭하여 토큰을 만들어주자.

Jenkins Secret Token

  • GitLab의 Webhook 설정에서 Jenkins의 위 설정에서 나타난 URL 정보와 Secret Token을 입력해준다.

Webhooks

  • Trigger에서 Merge Request Event를 체크해준다.

Webhooks

 

 

4-4. GitLab으로부터 Merge Request 정보 수신하기

  • 앞에서 설명했다시피 이 기능은 Developer Edition 이상에서만 가능하다.
  • 아래의 공식문서를 참고하면 Pull Request(GitLab에서는 Merge Request)를 분석할 수 있는 기능에 대해 설명되어 있다.
  • SonarQube Docs: Setting up the pull request analysis

 

  • 그렇다면 pull request 정보를 어디로부터 받아 넘겨줘야 할까? Jenkins의 GitLab 플러그인 문서를 확인해보면 여러가지 정보를 환경변수로 넣어준다는 사실을 알 수 있다.
  • Jenkins GitLab Plugin Docs
  • 실제로 Jenkins에서 printenv를 찍어보면 GitLab 정보가 환경변수에 저장되어 있는 것을 확인할 수 있다. 여기서 필요한 정보는 아래와 같이 세 가지다.
gitlabMergeRequestIid=28
gitlabTargetBranch=merge
gitlabBranch=feat/sonar
  • 위 내용을 pull request에 매칭해주자.
stage('SonarQube analysis') {
    steps{
        withSonarQubeEnv('lohasmeal-sonar'){
            sh """
            ./gradlew sonar 
            
            ... args ...
            
            -Dsonar.pullrequest.key=${gitlabMergeRequestIid} \
            -Dsonar.pullrequest.branch=${gitlabSourceBranch} \
            -Dsonar.pullrequest.base=${gitlabTargetBranch}
            """
        }
    }
}

 

 

 

5. 연동 시 주의사항

5-1. SonarQube Scanner 버전 문제

  • gradlew의 sonar 명령어를 사용할 때 아래와 같은 에러와 함께 명령어가 실패하는 경우가 있다.
* What went wrong:
'org.gradle.api.provider.Provider org.gradle.api.reporting.Report.getOutputLocation()'
  • 아래 질문의 답변에 의하면 SonarQube 버전을 5.x.x으로 설정하면 문제를 해결할 수 있다고 한다.
  • SonarQube QnA
  • SonarQube Docs: SonarScanner for Gradle
  • Spring Application의 SonarQube 플러그인 버전을 업그레이드했다.
plugins {
    id("org.sonarqube") version("5.1.0.4882")
}

 

 

5-2. Please provide compiled classes of your project with sonar.java.binaries property

  • sonar.java.binaries를 설정해주지 않으면 아래와 같은 에러가 발생할 수 있다.
* What went wrong:
Execution failed for task ':sonar'.
> Your project contains .java files, please provide compiled classes with sonar.java.binaries property, or exclude them from the analysis with sonar.exclusions property.
  • gradlew sonar를 실행할 때 Spring Application에서 SonarQube의 property를 설정해줄 수 있다. sonar.java.binaries 옵션을 추가해주도록 하자.
  • build.gradle.kts 코드 예시
 sonarqube {
    properties {
        property("sonar.host.url", "http://${SONARQUBE_URL}:9000")
        property("sonar.login", "{SONARQUBE_TOKEN}")
        property("sonar.sources", "src")
        property("sonar.language", "java")
        property("sonar.sourceEncoding", "UTF-8")
        property("sonar.coverage.jacoco.xmlReportPaths", "${layout.buildDirectory.get()}/reports/jacoco/test/jacocoTestReport.xml")
        property("sonar.java.binaries", "${layout.buildDirectory.get()}/classes")
        property("sonar.test.inclusions", "**/*Test.java")
        property("sonar.exclusions", "**/test/**, **/Q*.java, **/*Doc*.java, **/resources/**")
    }
}

 

 

6. SonarQube 정적 분석 결과 확인하기

  • 아래와 같이 Overview에서 SonarQube의 실행결과를 확인할 수 있다.

overview report

  • 이슈에서는 Code Smell과 Bug를 확인해볼 수 있다.

SonarQube Issues

 

 

7. 결론

회사에서 SonarQube 서버를 구성하고 사용한 지 딱 한 달이 되었다. SonarQube를 사용하고 나서 엄청난 변화가 있진 않았지만, 그냥 지나칠 수도 있는 실수에 대해 피드백 바로 받을 수 있기 때문에 코드 품질이 나빠지는 것을 방지할 수 있었다. 코드의 상태가 조금씩 좋아지는 중이다.

 

SonarQube를 더 잘 활용하기 위해 아래와 같은 방식을 고려해볼 수 있다.

  • SonarLint IDE 플러그인 설치하여 SonarQube와 연동하기
  • SonarQube에서 Custom Ruleset 설정하고 SonarLint로 설정 가져오기

 

SonarQube를 사용하는 것은 인프라 리소스를 꽤나 사용해야 하기 때문에 사용하기 망설여질 수 있다. 만약 리소스가 걱정이라면 SonarLint라는 플러그인만 설치해 사용하는 것만으로도 코드의 품질을 높일 수 있다.

 

참고자료

  • SonarQube Docs: GitLab Integration
  • SonarQube with Docker compose: complete tutorial
  • [Setting | DevOps] jenkins, gitlab, sonarqube 연동설정
  • SonarQube 정적분석 및 Jenkins CI/CD 통합
  • SonarQube + Jenkins + GitLab
  • 코드분석 도구 적용기 - 3편, SonarQube 적용하기
  • 소나큐브 이용 코드 정적분석 자동화
  • 소나큐브 커뮤니티 버전에서 Pull Request Decoration 설정 적용하기

 

 

'Infra' 카테고리의 다른 글
  • 나만의 Swagger UI 서버, 쿠버네티스에서 운영하기
gakko
gakko
좌충우돌 개발기
  • gakko
    MYVELOP 마이벨롭
    gakko
  • 전체
    오늘
    어제
    • 분류 전체보기 (203)
      • Spring (23)
        • Spring (10)
        • Spring Boot (7)
        • Spring Security (1)
        • Hibernate (4)
      • Test (3)
      • 끄적끄적 (6)
      • 활동 (35)
        • 부스트캠프 (23)
        • 동아리 (3)
        • 컨퍼런스 (3)
        • 글또 (5)
        • 오픈소스 컨트리뷰션 (1)
      • 디자인패턴 (0)
      • Git & GitHub (22)
        • Git (13)
        • Github Actions (1)
        • 오류해결 (5)
        • 기타(마크다운 등) (3)
      • 리눅스 (6)
        • 기초 (6)
        • 리눅스 서버 구축하기 (0)
      • Infra (2)
        • Docker (1)
        • Elastic Search (0)
        • Jenkins (1)
        • AWS (1)
      • MySQL (7)
        • 기초 (6)
        • Real MySQL (1)
      • 후기 (3)
        • Udemy 리뷰 (3)
      • CS (26)
        • 웹 기본지식 (0)
        • 자료구조 (13)
        • 운영체제 OS (12)
        • 데이터베이스 (1)
        • 시스템 프로그래밍 (0)
        • 기타 (0)
      • Tools (1)
        • 이클립스 (1)
        • IntelliJ (0)
      • 프로젝트 (1)
        • 모여모여(부스트캠프) (1)
      • JAVA (32)
        • Maven (6)
        • 오류해결 (11)
        • 자바 클래스&메소드 (1)
        • JSP & Servlet (12)
      • Javascript (5)
        • 기초 (3)
        • React (2)
      • Python (28)
        • 파이썬 함수 (9)
        • 알고리즘 문제풀이 (16)
        • 데이터 사이언스 (2)
        • 웹 크롤링 (1)
      • 단순정보전달글 저장소 (0)
  • 블로그 메뉴

    • 홈
    • 태그
    • 방명록
  • 링크

    • 우진님
  • 공지사항

  • 인기 글

  • 태그

    jsp
    MySQL
    부스트캠프
    자바스크립트
    Git
    java
    스프링
    Spring
    GitHub
    파이썬
    스프링부트
    오류해결
    Python
    웹개발
    부스트캠프 7기
    운영체제
    os
    부스트캠프 멤버십
    알고리즘
    자바
  • 최근 댓글

  • hELLO· Designed By정상우.v4.10.0
gakko
SonarQube로 코드 품질 관리하기 (feat. gitlab, jenkins, docker)
상단으로

티스토리툴바