docker-compose를 이용한 SonarQube 도입기

Astin·2024년 7월 10일
0

어떤 도구를 사용해야 할까?

정적 분석 도구는 여러가지가 있는데 그중에서 어떤 것을 선택할 지 고민이었습니다.
가장 유명한 것은 sonarqube였는데 선택에 앞서서 명확한 근거를 가지고 채택해야 했습니다. 따라서 sonarqube와 각 정적 분석도구들의 특징들을 먼저 정리하고 비교했습니다.

SonarQube의 주요 특징

  1. 광범위한 언어 지원
  • Java, C#, JavaScript, TypeScript, Python, C/C++, Go, PHP 등 다양한 언어를 지원하여 여러 언어로 작성된 프로젝트도 일관되게 관리할 수 있습니다.
  1. 다양한 플러그인을 제공
  • GitHub, GitLab, Jenkins 등과의 통합이 용이합니다.
  1. 다양한 품질 게이트
  • 코드 커버리지, 복잡도, 중복 코드 등 여러 측면에서 품질 기준을 설정할 수 있습니다.
  1. 개발자 친화적인 인터페이스
  • 웹 기반 UI를 통해 코드 분석 결과를 쉽게 확인하고, 문제를 추적할 수 있습니다.
  1. 활발한 커뮤니티와 문서화
  • 활발한 커뮤니티와 풍부한 문서가 제공되어 문제 해결과 학습에 도움이 됩니다.

다른 정적 분석 도구와의 비교

  1. ESLint (JavaScript/TypeScript 전용)
  • 장점: JavaScript와 TypeScript를 위한 강력한 린팅 도구. 커스터마이징이 용이하고, 다양한 플러그인을 통해 기능을 확장할 수 있습니다.
  • 단점: JavaScript/TypeScript에 특화되어 있어 다른 언어 지원은 부족합니다.
  • 비교: 현재 진행하는 프로젝트는 java기반이고 추후 확장성을 고려해서 채택하지 않았습니다.
  1. FindBugs/SpotBugs (Java 전용):
  • 장점: Java 코드의 버그를 찾는 데 특화된 도구. 효율적이고 정확한 분석 제공합니다.
  • 단점: Java 전용이므로 다른 언어에는 적용 불가능합니다.
  • 비교: SonarQube에서 FindBugs/SpotBugs와 통합하여 사용할 수 있으므로 채택하지 않았습니다.
  1. PMD (Java, JavaScript, Apex 등):
  • 장점: 다양한 언어를 지원하며, 코드 스타일과 버그를 검출하는 데 유용합니다.
  • 단점: 사용자 인터페이스가 직관적이지 않습니다.
  • 비교: SonarQube는 PMD보다 더 직관적인 UI와 더 넓은 언어 지원을 제공하므로 채택하지 않았습니다.
  1. Checkmarx (보안 분석 도구):
  • 장점: 코드 보안 분석에 특화되어 있으며, 심층적인 보안 분석 기능 제공합니다.
  • 단점: 주로 보안 분석에 초점이 맞춰져 있어, 전체적인 코드 품질 관리에는 적합하지 않습니다.
  • 비교: SonarQube는 보안뿐만 아니라 코드 품질, 유지 보수성 등 여러 측면을 종합적으로 분석가능하므로 채택하지 않았습니다.

이러한 분석 결과를 바탕으로 소나큐브를 사용하기로 결정했습니다.

환경 선택

sonarqube를 사용하기 위해서는 설치가 필요합니다.
방법은 로컬에 설치하는 것과 docker를 이용하는 법 2가지가 있습니다.

sonarqube는 기본적으로 h2 db를 사용합니다. h2는 SonarQube를 처음 설치할 때 기본으로 설정되어 있어 추가적인 설정이 필요 없고 가벼운 메모리 내장형 데이터베이스로, 간단한 테스트에 적합합니다.
다만, H2는 다수의 사용자와 대량의 데이터 처리가 필요하고 고가용성이 필요한 프로덕션 환경에는 적합하지 않습니다. 또한 내장형 데이터베이스 특성상 시스템 장애 시 데이터 손실 가능성이 높습니다.

제가 진행하는 프로젝트는 퀀트 모의투자 사이트로 지금 당장에는 고가용성이 그렇게까지 필요하지는 않았습니다. 다만, 추후에 실제 투자에도 모의투자 기능을 사용할 수 있도록 기획상으로 정해놓았습니다. 따라서, 이 경우 고가용성이 중요하다고 판단되었습니다. 더불어, 이용자가 추후에 늘어날 수도 있기도 하고 postgres를 db로 설정하는데 드는 비용도 크지 않다고 판단하였기에 postgres를 쓰기로 결정했습니다.

또한, postgres db와 sonarqube 설정을 설치없이 동시에 처리할 수 있고 다른 사람들과 같은 환경을 제공할 수 있는 docker로 구현하기로 결정하였습니다.

docker compose 설정

따라서 여러개의 컨테이너(postgres, sonarqube) 가 필요하므로 docker compose 설정을 우선 해주었습니다.

services:
  sonarqube:
    image: sonarqube:community
    depends_on:
      - db
    environment:
      SONAR_JDBC_URL: jdbc:postgresql://db:5432/sonar
      SONAR_JDBC_USERNAME: sonar
      SONAR_JDBC_PASSWORD: sonar
    volumes:
      - sonarqube_data:/opt/sonarqube/data
      - sonarqube_extensions:/opt/sonarqube/extensions
      - sonarqube_logs:/opt/sonarqube/logs
    ports:
      - "9000:9000"
    ulimits:
      nproc: 131072
      nofile:
        soft: 8192
        hard: 131072
  db:
    image: postgres:16
    environment:
      POSTGRES_USER: sonar
      POSTGRES_PASSWORD: sonar
    volumes:
      - postgresql:/var/lib/postgresql
      - postgresql_data:/var/lib/postgresql/data

volumes:
  sonarqube_data:
  sonarqube_extensions:
  sonarqube_logs:
  postgresql:
  postgresql_data:

코드는 sonarqube 공식 사이트를 참고하였습니다.
하나씩 설명해보면

sonarqube:
    image: sonarqube:community
    depends_on:
      - db
    environment:
      SONAR_JDBC_URL: jdbc:postgresql://db:5432/sonar
      SONAR_JDBC_USERNAME: sonar
      SONAR_JDBC_PASSWORD: sonar
    volumes:
      - sonarqube_data:/opt/sonarqube/data
      - sonarqube_extensions:/opt/sonarqube/extensions
      - sonarqube_logs:/opt/sonarqube/logs
    ports:
      - "9000:9000"
    ulimits:
      nproc: 131072
      nofile:
        soft: 8192
        hard: 131072
  • image: 이미지를 사용하여 SonarQube의 커뮤니티 에디션을 실행합니다.
  • depends_on: SonarQube가 실행되기 전에 db 서비스가 실행되도록 합니다.
  • environment:
    • SONAR_JDBC_URL: PostgreSQL 데이터베이스에 연결하기 위한 JDBC URL입니다.
    • SONAR_JDBC_USERNAME: 데이터베이스 사용자 이름입니다.
    • SONAR_JDBC_PASSWORD: 데이터베이스 사용자 비밀번호입니다.
  • volumes:
    • sonarqube_data: SonarQube의 데이터 디렉토리를 위한 볼륨입니다.
    • sonarqube_extensions: SonarQube의 확장 디렉토리를 위한 볼륨입니다.
    • sonarqube_logs: SonarQube의 로그 디렉토리를 위한 볼륨입니다.
  • ports: 로컬 머신의 포트 9000을 컨테이너의 포트 9000에 매핑합니다.
  • ulimits : 컨테이너 내에서 리소스 사용을 제한하여 시스템의 안정성과 성능을 보장하는 데 도움이 됩니다.
    • nproc : 프로세스 수를 제한하여, 컨테이너 내에서 무한히 많은 프로세스가 생성되는 것을 방지합니다.
    • nofile : 파일 디스크립터 수를 제한하여, 파일 핸들 자원을 적절히 관리할 수 있게 합니다.
      • soft : 컨테이너 내에서 파일 디스크립터의 소프트 제한을 설정합니다. 이는 컨테이너 내 프로세스들이 동시에 열 수 있는 파일의 최대 수를 의미합니다.
      • hard : 컨테이너 내에서 파일 디스크립터의 하드 제한을 설정합니다. 이는 시스템 전체적으로 적용되는 최대 파일 디스크립터 수로, 소프트 제한을 넘는 상황에서 이 값을 초과할 수 없습니다.
 db:
    image: postgres:16
    environment:
      POSTGRES_USER: sonar
      POSTGRES_PASSWORD: sonar
    volumes:
      - postgresql:/var/lib/postgresql
      - postgresql_data:/var/lib/postgresql/data
  • image: postgres:16 이미지를 사용하여 PostgreSQL 데이터베이스의 16 버전을 실행합니다.
  • environment:
    • POSTGRES_USER: 데이터베이스 사용자 이름입니다.
    • POSTGRES_PASSWORD: 데이터베이스 사용자 비밀번호입니다.
  • volumes:
    • postgresql: PostgreSQL 데이터 파일을 저장하기 위한 볼륨입니다.
    • postgresql_data: PostgreSQL 데이터 디렉토리를 위한 볼륨입니다.
volumes:
  sonarqube_data:
  sonarqube_extensions:
  sonarqube_logs:
  postgresql:
  postgresql_data:
  • sonarqube_data: SonarQube의 데이터 디렉토리를 위한 볼륨입니다.
  • sonarqube_extensions: SonarQube의 확장 디렉토리를 위한 볼륨입니다.
  • sonarqube_logs: SonarQube의 로그 디렉토리를 위한 볼륨입니다.
  • postgresql: PostgreSQL 데이터 파일을 저장하기 위한 볼륨입니다.
  • postgresql_data: PostgreSQL 데이터 디렉토리를 위한 볼륨입니다.

주의사항 : db connection failed error

db-1 | 2024-06-17 05:02:28.160 UTC [1] FATAL: database files are incompatible with server
db-1 | 2024-06-17 05:02:28.160 UTC [1] DETAIL: The data directory was initialized by PostgreSQL version 16, which is not compatible with this version 12.19 (Debian 12.19-1.pgdg120+1).

처음에 저처럼 다른 블로그를 참고해서 docker compose를 구성했다면 위와 같은 에러가 발생할 수 있습니다.

이는 volume의 문제이므로 이미지만 지운다고 해결되지 않습니다.
이때는 볼룸까지 같이 지워줘야 하므로 docker-compose down -v 명령어를 사용해주면 됩니다. 혹은 위의 docker compose 파일의 postgres 버전을 동일하게 변경해주면 됩니다. 위의 에러를 예로 들자면 image부분에서 postgres:12.19 -> postgres:16으로 바꿔주면 됩니다.

주의사항 : wsl distro terminated abruptly

윈도우에서 공식문서대로 설정하고 실행할 경우 위와 같은 에러가 발생할 수 있습니다.
sonarqube가 내부적으로 elasticsearch를 사용하고있는데, elasticsearch가 요구하는 vm memory 용량을 늘려주지 않아 생긴 문제입니다.
즉, WSL2가 사용 가능한 시스템 리소스를 초과하여 발생한 문제이므로 리소스를 따로 제한해줄 필요가 있습니다.

저의 경우 docker compose 파일 구성에 리소스를 제한하는 ulimits를 설정하여 해결하였습니다.

ec2 설정

ec2 인스턴스와 보안그룹은 설정되어있다고 가정하고 진행하겠습니다.
만약 ec2 설정이 되어있지 않는다면 아래 블로그 글을 참고하면 좋을 것 같습니다.
https://velog.io/@jonghyun3668/SpringBoot-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-EC2-%EB%B0%B0%ED%8F%AC%ED%95%98%EA%B8%B0
위 블로그 글을 따라서 설정하면 되는데 보안그룹의 경우 sonarqube가 9000번을 사용하므로 인바운드에 9000번 포트 허용만 추가로 해주시면 됩니다.

EC2 인스턴스를 설정하고 접속하면 우선 Docker와 Docker Compose를 설치해야합니다.

docker 설치(amazon)

sudo yum update -y # 패키지 업데이트 
sudo yum install docker -y # docker 설치 
sudo systemctl enable docker # 부팅시 docker 자동시작되도록 설정
sudo usermod -a -G docker ec2-user # 현재 사용자를 docker 그룹에 추가 

docker 설치(ubuntu)

sudo apt update # 패키지 업데이트 
sudo apt install docker.io -y  # docker 설치 
sudo systemctl start docker # docker 시작 
sudo systemctl enable docker  # 부팅시 docker 자동시작되도록 설정
sudo usermod -aG docker $USER # 현재 사용자를 docker 그룹에 추가

docker compose 설치

sudo curl -L "https://github.com/docker/compose/releases/download/$(curl -s https://api.github.com/repos/docker/compose/releases/latest | jq -r .tag_name)/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose
sudo chmod +x /usr/local/bin/docker-compose
  • sudo curl -L ~: 최신 버전의 Docker Compose 바이너리를 다운로드하여 /usr/local/bin/docker-compose 경로에 저장합니다.
    • sudo: 시스템 관리자 권한으로 명령을 실행합니다.
    • curl: URL에서 데이터를 다운로드하는 명령줄 도구입니다.
    • -L: 리다이렉션을 따라가도록 합니다. 다운로드할 URL이 리다이렉션될 경우, 최종 URL을 따라가도록 합니다.
    • -o : 다운로드한 파일을 해당 경로에 저장합니다.
  • sudo chmod +x /usr/local/bin/docker-compose: Docker Compose 바이너리에 실행 권한을 부여합니다.
    • sudo: 시스템 관리자 권한으로 명령을 실행합니다.
    • chmod: 파일 권한을 변경하는 명령입니다.
    • +x: 실행 권한을 추가합니다. 이 옵션은 파일을 실행 가능하게 만듭니다.
    • /usr/local/bin/docker-compose: 실행 권한을 부여할 대상 파일입니다.

이 명령어들을 실행하면 최신 버전의 Docker와 Docker Compose 바이너리를 다운로드하여 시스템에 설치하고, 해당 파일에 실행 권한을 부여하여 Docker 와 Docker Compose 명령을 사용할 수 있게 됩니다.

sudo nano docker-compose.yml

위에서 작성한 docker compose의 경우 nano editor를 사용해서 추가해주시면 됩니다(ctrl+s로 저장, ctrl+x로 나가기 하시면 됩니다)

sudo yum install nano -y # amazon
sudo apt install nano -y # ubuntu

만약 설치가 되어 있지 않다면 위의 명령어로 설치해주시면 됩니다.

docker-compose up -d

실행권한이 부여되었으므로 이제 docker compose 파일이 설치된 위치로 이동해서 위 명령어로 sonarqube를 실행하면 됩니다.

만약 메모리가 부족하다면?

sonarqube의 경우 기본 4G의 메모리가 필요하므로 프리티어를 사용할 경우 메모리가 부족해서 실행되지 않을 수 있다.
이때, 메모리 스왑을 이용하면 메모리 부족 문제를 해결 할 수 있다.

스왑 파일 생성:

스왑 파일을 생성하고 4GB로 설정합니다.

sudo fallocate -l 4G /swapfile
스왑 파일에 적절한 권한 설정

스왑 파일에 적절한 권한을 부여합니다.

sudo chmod 600 /swapfile
스왑 파일을 스왑 영역으로 설정
sudo mkswap /swapfile
스왑 파일 활성화
sudo swapon /swapfile
스왑 파일 활성화 확인
sudo swapon --show
재부팅 후에도 스왑이 유지되도록 설정

/etc/fstab 파일에 스왑 파일을 추가하여 시스템 재부팅 후에도 스왑이 유지되도록 설정합니다.

echo '/swapfile none swap sw 0 0' | sudo tee -a /etc/fstab

vm.max_map_count 에러 발생시

SonarQube를 실행할 때 발생하는 vm.max_map_count 오류는 시스템의 가상 메모리 매핑 수 설정이 낮기 때문에 발생합니다. SonarQube는 Elasticsearch를 사용하며, Elasticsearch는 높은 vm.max_map_count 값을 필요로 합니다. 이를 해결하려면 vm.max_map_count 값을 262144 이상으로 설정해야 합니다.

현재 설정 확인

현재 vm.max_map_count 설정을 확인합니다.

sudo sysctl vm.max_map_count
설정 변경

설정을 일시적으로 변경하려면 다음 명령어를 실행합니다

sudo sysctl -w vm.max_map_count=262144
설정을 영구적으로 변경

시스템 재부팅 후에도 설정이 유지되도록 하려면 /etc/sysctl.conf 파일에 설정을 추가합니다

sudo nano /etc/sysctl.conf

파일의 끝에 다음 줄을 추가합니다

vm.max_map_count=262144

변경 사항을 적용하려면 다음 명령어를 실행합니다

sudo sysctl -p

github app 설정

이렇게 설정까지 마치시고 docker를 실행하신다면 http://<EC2_PUBLIC_IP>:9000에 sonarqube가 실행 될 것입니다.

처음 아이디 비밀번호는

id : admin
password : admin

이므로 이를 입력하고 접속해주시면 아래와 같은 화면을 보실 수 있습니다.

이제 프로젝트 연결이 필요한데 저는 여기서 github를 사용할 예정이므로
github app 설정이 필요했습니다.

github 앱의 경우 setting> developer setting > new github app 으로 들어가면 github app을 만들 수 있습니다.

홈페이지와 callback url의 경우 public ip로 설정하면 됩니다. 저의 경우 ec2에 올렸으므로 http://ec2-4-30-266-34.ap-northeast-2.compute.amazonaws.com:9000 으로 설정하였습니다. webhook의 경우 당장은 사용하지 않아 일단 비활성화 시켜놓겠습니다.

권한의 경우 기본적으로

  • Checks : Read & write 로
  • GitHub Enterprise 인 경우 : Repository metadata를
  • GitHub.com 인 경우(일반 계정인 경우) : Metadata 를 Read-only 로
  • Pull Requests : Read & write

으로 설정하면 됩니다.

만약 repo가 private 일 경우 repository permissions에서

  • Contents : Read-only 를 추가하면 되고

깃허브 oatuh로그인 설정을 할 경우 Account permission에서

  • Email addresses Read-only 를 추가하고

Organization permissions에서

  • Members Read-only
  • Projects Read-only

를 추가하면 됩니다.

깃허브 앱 설정을 하고 나면 프로젝트 setting>integration>githb app에서 방금 설정한 github app을 설치하면 됩니다.

sonar 설정하기

자 이제 github app 설정이 완료되었으니 sonar qube 설정을 하면 됩니다.

위의 github setup 버튼을 누르시거나 Administration > DevOps Platform Integrations > Github > CreateConfiguration 으로 들어가면 github 프로젝트와 연동을 하실 수 있습니다.

이름은 원하시는 대로 작명하시면 되고 api url의 경우 github enterprise를 사용하는 경우 위쪽, 아닌 경우 아래쪽 url을 적어주시면 됩니다.

gihub app id, client id, client secret, private key의 경우 속해있는 조직에서 setting> developer setting> github app setting > edit 으로 들어가시면 정보를 볼 수 있습니다.

client secret, private key의 경우 발급당시에만 볼 수 있으므로 따로 저장해두시는 걸 추천합니다.

이제 원하는 project를 선택하고 세팅을 선택하면 아래와 같은 화면을 볼 수 있습니다. (저의 경우 global setting으로 진행하였습니다.)

저의 경우 githb actions를 사용할 것이므로 이를 선택하면

위와 같은 페이지가 나오는데 맞춰서 설정해주시면 됩니다.

sonar.yaml

name: Build

on:
  push:
    branches:
      - main


jobs:
  build:
    name: Build and analyze
    runs-on: ubuntu-latest
    
    steps:
      - uses: actions/checkout@v4
        with:
          fetch-depth: 0  # Shallow clones should be disabled for a better relevancy of analysis
      - name: Set up JDK 17
        uses: actions/setup-java@v1
        with:
          java-version: 17
      - name: Cache SonarQube packages
        uses: actions/cache@v1
        with:
          path: ~/.sonar/cache
          key: ${{ runner.os }}-sonar
          restore-keys: ${{ runner.os }}-sonar
      - name: Build and analyze
        env:
          SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
          SONAR_HOST_URL: ${{ secrets.SONAR_HOST_URL }}
        run: ./gradlew build sonar --info

custom.yaml

name: Backend CI

on:
  push:
    branches:
      - develop
      - main
      - feature/ci
  pull_request:
    branches:
      - develop
      - main

jobs:
  build:
    runs-on: ubuntu-latest

    steps:
      - name: Checkout
        uses: actions/checkout@v3
        with:
          fetch-depth: 0

      - name: Set up JDK 17
        uses: actions/setup-java@v3
        with:
          java-version: '17'
          distribution: 'temurin'

      - name: Grant execute permission for gradlew
        run: chmod +x gradlew

      - name: Build with Gradle
        run: ./gradlew build

  sonarqube:
    needs: build
    runs-on: ubuntu-latest

    steps:
      - uses: actions/checkout@v4
        with:
          fetch-depth: 0

      - name: Set up JDK 17
        uses: actions/setup-java@v1
        with:
          java-version: 17

      - name: Cache SonarQube packages
        uses: actions/cache@v1
        with:
          path: ~/.sonar/cache
          key: ${{ runner.os }}-sonar
          restore-keys: ${{ runner.os }}-sonar

      - name: Cache Gradle packages
        uses: actions/cache@v1
        with:
          path: ~/.gradle/caches
          key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle') }}
          restore-keys: ${{ runner.os }}-gradle

      - name: Grant execute permission for gradlew
        run: chmod +x ./gradlew

      - name: Build and analyze
        env:
          SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
          SONAR_HOST_URL: ${{ secrets.SONAR_HOST_URL }}
          SONAR_PROJECT_KEY: ${{ secrets.SONAR_PROJECT_KEY }}
          SONAR_PROJECT_NAME: ${{ secrets.SONAR_PROJECT_NAME }}
        run: ./gradlew build sonar --info


sonar.yaml은 공식, custom.yaml은 제 코드입니다. 저의 경우 build가 성공했을 시에만 실행하고 싶어서 needs: build 라는 조건을 설정해 주었고 build.gradle에 project key와 name을 설정하는 대신 더 안전하고 관리하기 용이한 github secrets variable을 활용했습니다.
(github secrets의 경우 setting> secrets and variables> actions 에서 설정가능합니다)

sonar token 발급 방법

우측 위의 프로필 선택> security 로 들어가면 위와 같은 화면이 나옵니다.
이름, 타입(용도에 맞게 선택, project>global>user로 갈수록 권한이 많습니다), 기한을 선택하고 생성하면 문자열 토큰 값이 생성됩니다.

이후 프로젝트 설정> secrets and variables > actions 로 들어가서 secret 변수를 생성하면 됩니다.

저의 경우 SONAR_TOKEN으로 이름을 설정하여 토큰 값을 저장하였습니다.

소감

이렇게 설정을 마치고 나면 github action을 통해 ci가 실행될때 sonar qube가 실행되게 됩니다. sonar qube와 docker-compose 관련된 자료가 적고 오래되어서 구현하는데 꽤 많은 시간이 소요되었지만 sonar qube를 통해 코드의 품질을 측정할 수 있게 되어 유지, 향상이 수월해 질 수 있었습니다. 또한, docker-compose를 통해 환경을 일관적으로 가져갈 수 있게 되어 확장성 또한 확보할 수 있었습니다. 추후에도 이러한 코드 품질을 높일 수 있는 도구가 있다면 소개해보겠습니다. 감사합니다.

참고

https://docs.sonarsource.com/sonarqube/latest/

https://dlwnsdud205.tistory.com/350

https://docs.sonarsource.com/sonarqube/9.9/devops-platform-integration/github-integration/

0개의 댓글

관련 채용 정보