IT/PaaS

쿠버네티스 시작하기(10) - SonarQube & Jacoco 연동하기

twofootdog 2020. 1. 13. 11:42

지금까지 쿠버네티스 클러스터에 간단한 CI/CD 파이프라인을 만들어보았다. 이제 기본적인 파이프라인은 생성해보았으니 파이프라인을 좀 더 업그레이드 해보도록 하자.

우리가 구축한 CI/CD 파이프라인에 추가적으로 필요한 기능은 바로 테스트 자동화다. 이번 장에서는 테스트 자동화를 위해 CI/CD 파이프라인에 SonarQube와 Jacoco를 적용할 것이다.

 

1. 테스트 자동화란?

보통 JAVA로 개발된 소스를 테스트할 때는 JUnit을 활용해 테스트 코드를 작성하고 해당 코드를 실행해서 개발한 소스가 원하는대로 정상 동작하는지를 테스트한다. 테스트 자동화란 이 테스트 코드를 자동으로 실행시키는 것으로, 보통 CI/CD 파이프라인 내 STEP에 추가시켜 파이프라인 실행 중에 배포 환경에서 개발한 소스가 정상 동작하는지 테스트를 진행하는 것이다. 테스트 자동화는 수동 테스터의 개입 없이 반복적인 작업 및 회귀 테스트를 실행할 수 있다.

 

2. SonarQube & Jacoco란?

SonarQube란 코드정적분석을 통해 코드의 품질을 올려주는 오픈소스 플랫폼이다. 소스 정적 분석을 통해 Bug(소스 버그)Code Smells(유지보수가 어려운 코드), Security Vulnerability(보안에 취약한 코드) 등을 확인할 수 있다. 

Jacoco란 Java Code Coverage의 약자이며, Code Coverage란 소프트웨어를 개발한 후 테스트할 때 얼마나 많은 부분의 테스트가 되었는지에 대한 수치이다. 만약 SonarQube만 사용하게 되면 아무리 JUnit Test를 CI/CD 파이프라인 프로세스에 넣어도 Code Coverage가 측정되지 않는다. 하지만 SonarQube에 Jacoco를 결합하여 사용하게 되면 Code Coverage까지 측정되므로 둘은 함께 사용하는 것이 좋다. 

Code Coverage는 국제적으로 통용되는 수치는 없으나 100%는 현실적으로 불가능하고, 80%정도를 목표치로 잡는다(보통 60%로 잡고 중요 모듈은 80%이상으로 잡는 것이 좋다).

 

3. SonarQube 구성요소

SonarQube는 4개의 컴포넌트로 구성되어 있다.

3-1. SonarQube Server

3가지 메인 프로세스를 구동시킨다.

  • Web Server : 사용자들에게 분석결과를 보여주고, SonarQube 설정페이지를 보여줌
  • Search Server : ElasticSearch 서버를 사용해서 사용자들에게 검색기능을 제공
  • Compute Engine Server : 정적분석결과를 생성하고 이를 SonarQube Database에 저장

 

3-2. SonarQube Database

SonarQube의 기본 설정들과(보안, Plugin정보 등) 프로젝트 분석 스냅샷들을 저장한다. 설치 시 기본적으로 H2 DB를 포함하며, 설정을 통해 다양한 DB사용이 가능하다(그런데 확인해보니 주로 PostgreSQL을 사용하는 것 같다)

 

3-3. SonarQube Plugin

내부에서 사용가능한 다양한 기능(분석 프로그램언어, 권한, 관리, SCM 등)을 플러그인 형태로 설치 가능하다.

 

3-4. SonarQube Scanner

프로젝트 정적분석을 수행하는 툴로 다양한 형식으로 제공되며 CI Server와 연계하여 사용 가능하다.

 

 

4. SonarQube 정적분석 프로세스

SonarQube의 구성요소들을 활용해 소스 정적분석을 하는 방법은 아래와 같다.

  1. IDE에서 코드를 작성하고 Sonar Lint 등을 통해 로컬 코드 분석을 실행한다.
  2. 분석을 통해 수정된 코드를 SCM으로 Push 한다. 
  3. CI서버에서 트리거된 빌드 수행 시 SonarQube Scanner를 실행한다.
  4. SonarQube Scanner는 생성한 분석 리포트의 처리를 위해 SonarQube Server로 전송한다.
  5. SonarQube Server는 분석 리포트 결과를 처리하여 DB에 저장하고 결과를 웹서버를 통해 제공한다.
  6. 개발자는 웹페이지에서 분석 결과를 확인하고 코드를 개선한다.
  7. 관리자는 보고서를 받는다. SonarQube에서 데이터를 추출하기 위해 API를 활용하거나, SonarQube Server 모니터링을 위해 JMX를 사용할 수 있다.

 

 

5. 쿠버네티스 클러스터에 SonarQube 적용하기

자 그럼 이전 포스트에서 만든 쿠버네티스 클러스터 기반 CI/CD 파이프라인에 Sonarqube를 적용해보자.

지난번에 CI/CD 파이프라인에 대한 그림을 보여준적이 있었는데, 이번에 구축이 되는 부분을 표시하면 아래와 같다(빨간색 표시).

우선 필자는 정적분석을 진행할 개발 프로젝트를 아래와 같은 환경에서 구성하였다. 때문에 필자와 같은 환경이면 그대로 따라하면 되고, 그렇지 않다면 조금 더 검색을 하면서 진행하면 된다.

  • language : java
  • framework : springboot
  • 빌드툴 : gradle
  • ci/cd : jenkins 

 

5-1. SonarQube Secret 생성

첫번째로 SonarQube Database에 접근 시 사용할 Password로 활용하기 위해 쿠버네티스 Secret을 만들어보자. 

[root@k8s-master sonar]# echo -n 'mediumpostgres' | base64
bWVkaXVtcG9zdGdyZXM=

위 명령어로 나온 문장을 sonar-secret.yaml파일에 붙여넣자.

sonar-secret.yaml

apiVersion: v1
kind: Secret
metadata:
  name: postgres-pwd
type: Opaque
data:
  password: bWVkaXVtcG9zdGdyZXM=

 

5-2. SonarQube Database 생성

다음으로 SonarQube Database 용 yaml파일을 작성해보자. Database 생성 시에는 pv, pvc, deployment, service 총 4개가 필요하다.

soanr-pv-postgres.yaml : 

kind: PersistentVolume
apiVersion: v1
metadata:
  name: pv0001
  labels:
    type: local
spec:
  capacity:
    storage: 10Gi
  accessModes:
    - ReadWriteOnce
  hostPath:
    path: /data/postgresql/

sonar-pvc-postgres.yaml : 

kind: PersistentVolumeClaim
apiVersion: v1
metadata:
  name: claim-postgres
spec:
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 8Gi

sonar-postgres-deployment.yaml : 

#apiVersion: extensions/v1beta1
apiVersion: apps/v1
kind: Deployment
metadata:
  name: sonar-postgres
spec:
  replicas: 1
  template:
    metadata:
      name: sonar-postgres
      labels:
        name: sonar-postgres
    spec:
      containers:
        - image: postgres:latest
          name: sonar-postgres
          env:
            - name: POSTGRES_PASSWORD
              valueFrom:
                secretKeyRef:
                  name: postgres-pwd
                  key: password
            - name: POSTGRES_USER
              value: sonar
          ports:
            - containerPort: 5432
              name: postgresport
          volumeMounts:
            # This name must match the volumes.name below.
            - name: data-disk
              mountPath: /var/lib/postgresql/data
      volumes:
        - name: data-disk
          persistentVolumeClaim:
            claimName: claim-postgres

sonar-postgres-service.yaml : 

apiVersion: v1
kind: Service
metadata:
  labels:
    name: sonar-postgres
  name: sonar-postgres
spec:
  ports:
    - port: 5432
  selector:
    name: sonar-postgres

 

5-3. SonarQube Server 생성

다음으로 SonarQube Server를 생성해보자. Server를 생성할 때는 deployment와 service가 필요하다.

sonarqube-deployment.yaml : 

#apiVersion: extensions/v1beta1
apiVersion: apps/v1
kind: Deployment
metadata:
  name: sonarqube
spec:
  replicas: 1
  template:
    metadata:
      name: sonarqube
      labels:
        name: sonarqube
    spec:
      containers:
        - image: sonarqube:latest
          args:
            - -Dsonar.web.context=/sonar
          name: sonarqube
          env:
            - name: SONARQUBE_JDBC_PASSWORD
              valueFrom:
                secretKeyRef:
                  name: postgres-pwd
                  key: password
            - name: SONARQUBE_JDBC_URL
              value: jdbc:postgresql://sonar-postgres:5432/sonar
          ports:
            - containerPort: 9000
              name: sonarqube

sonarqube-service.yaml

apiVersion: v1
kind: Service
metadata:
  labels:
    name: sonar
  name: sonar
spec:
  type: NodePort
  ports:
    - port: 80
      targetPort: 9000
      nodePort: 30002
      name: sonarport
  selector:
    name: sonarqube

 

5-4. SonarQube 구동

이제 생성한 yaml파일을 쿠버네티스 클러스터에 적용하여 SonarQube를 구동시켜보자.

# kubectl apply -f sonar-secret.yaml
# kubectl apply -f sonar-pv-postgres.yaml
# kubectl apply -f sonar-pvc-postgres.yaml
# kubectl apply -f sonar-postgres-deployment.yaml
# kubectl apply -f sonar-postgres-service.yaml
# kubectl apply -f sonarqube-deployment.yaml
# kubectl apply -f sonarqube-service.yaml

 

Pod가 정상 기동되는 것을 확인할 수 있다.

 

SonarQube Service의 포트번호를 확인하고(아래 그림에선 30002)

 

웹페이지에서 http://[서버IP]:[포트번호]/sonar 로 접속해보자(예를들면 서버IP가 66.42.43.41인 경우 http://66.42.43.41:30002/sonar/로 접속). SonarQube 웹페이지가 뜨면 성공이다. 

 

5-5. SonarQube 프로젝트 생성

이제 코드정적분석 진행을 위한 신규 프로젝트를 생성하자. 하는 방법은 SonarQube에서 신규 프로젝트를 생성한 다음 해당 프로젝트와 Jenkins의 파이프라인과 연동시켜주면 된다.

우선 SonarQube에 로그인을 한다. Login 아이디/패스워드는 기본으로 admin/admin으로 설정되어 있다. 로그인 한 후 Create new project 버튼을 클릭한다.

 

Project KeyDisplay Name을 입력한다. 필자는 그냥 프로젝트 이름으로 심플하게 작성했다. 

 

다음으로 token을 생성한다. 해당 token은 CI/CD 파이프라인과 연동 시 필요하다. 기억해두도록 하자.

 

다음으로 languagebuild technology를 선택하면 아래처럼 SonarQube를 적용하는 방법이 나온다. 참고하도록 하자(아래처럼 적용할 수도 있지만 필자는 조금 다른 방식으로 진행해볼 것이다).

 

다시 홈으로 돌아오면 아래처럼 프로젝트가 생성된 것을 확인할 수 있다. 이제 위에서 보여진 command들을 활용하여 CI/CD 파이프라인과 SonarQube를 연동해보도록 하자.

 

 

5-6. 빌드툴(gradle)에 SonarQube 코드 정적분석 설정 추가

다음으로 빌드툴에 SonarQube 설정을 추가해보자. 필자는 빌드툴로 gradle을 사용하기 때문에 build.gradle 파일에 아래 코드를 추가하였다. 우선 plugins에 id "org.sonarqube" version "2.7" 을 추가했고, sonarqube properties를 추가하고 projetName과 projectKey에 5-5에서 입력했던 값을 넣고, host.url은 SonarQube url을, login에는 5-5에서 생성했던 token값을 넣었다. 아래와 같이 작성하게 되면 gradle task로 sonarqube를 사용할 수 있다(예를들면 gradle build 처럼 gradle sonarqube 명령어로 코드 정적분석을 진행할 수 있다)

build.gradle:

plugins {
	id 'org.springframework.boot' version '2.1.6.RELEASE'
	id 'java'
	id "org.sonarqube" version "2.7"	// 추가. sonarqube plugin
}

/* sonarqube 설정 추가 */
sonarqube {
	properties {
		property "sonar.projectName", "auth"
		property "sonar.projectKey", "auth"
		property "sonar.host.url", "http://66.42.43.41:30002/sonar"
		property "sonar.login", "851b8cf151af0e972c57debba6af5a4af648fa2f"
	}
}

 

5-7. CI/CD 파이프라인에 SonarQube 코드정적분석 STEP 적용

다음으로 CI/CD 파이프라인에 SonarQube 코드정적분석 STEP을 추가해보자. 방법은 파이프라인이 명시되어 있는 프로젝트의 Jenkinsfile에 코드정적분석 STEP만 추가해주면 된다(gradle sonarqube 명령어만 추가). 밑에서는 gradle -x teset build 명령어에 sonarqube 옵션값만 추가했다. sonarqube 옵션값에 의해 build.gradle에 추가한 sonarqube properties값을 참조하여 SonarQube 서버로 코드정적분석을 요청하는 것이다. 

Jenkinsfile :

/* pipeline 변수 설정 */
def DOCKER_IMAGE_NAME = "twofootdog/project-repo"           // 생성하는 Docker image 이름
def DOCKER_IMAGE_TAGS = "batch-visualizer-auth"  // 생성하는 Docker image 태그
def DOCKER_CONTAINER_NAME = "auth-container"    // 생성하는 Docker Container 이름
def NAMESPACE = "ns-project"
def SLACK_CHANNEL = "auth"
def DATE = new Date();

def notifyStarted(slack_channel) {
    slackSend (channel: "${slack_channel}", color: '#FFFF00', message: "STARTED: Job '${env.JOB_NAME} [${env.BUILD_NUMBER}]' (${env.BUILD_URL})")
}
def notifySuccessful(slack_channel) {
    slackSend (channel: "${slack_channel}", color: '#00FF00', message: "SUCCESSFUL: Job '${env.JOB_NAME} [${env.BUILD_NUMBER}]' (${env.BUILD_URL})")
}
def notifyFailed(slack_channel) {
  slackSend (channel: "${slack_channel}", color: '#FF0000', message: "FAILED: Job '${env.JOB_NAME} [${env.BUILD_NUMBER}]' (${env.BUILD_URL})")
}
  
podTemplate(label: 'builder',
            containers: [
                containerTemplate(name: 'gradle', image: 'gradle:5.6-jdk8', command: 'cat', ttyEnabled: true),
                containerTemplate(name: 'docker', image: 'docker', command: 'cat', ttyEnabled: true),
                containerTemplate(name: 'kubectl', image: 'lachlanevenson/k8s-kubectl:v1.15.3', command: 'cat', ttyEnabled: true)
            ],
            volumes: [
                hostPathVolume(mountPath: '/home/gradle/.gradle', hostPath: '/home/admin/k8s/jenkins/.gradle'),
                hostPathVolume(mountPath: '/var/run/docker.sock', hostPath: '/var/run/docker.sock'),
                //hostPathVolume(mountPath: '/usr/bin/docker', hostPath: '/usr/bin/docker')
            ]) {
    node('builder') {
        try {
            stage('Start') {
                /* Slack 메시지 전송 */
                notifyStarted(SLACK_CHANNEL)
            }
            stage('Checkout') {
                 checkout scm   // gitlab으로부터 소스 다운
            }
            stage('Build') {
                container('gradle') {
                    /* 도커 이미지를 활용하여 gradle 빌드를 수행하여 ./build/libs에 jar파일 생성. 
                    build 수행 후에는 sonarqube를 활용하여 소스정적분석을 실행한다 */
                    sh "gradle -x test build sonarqube"
                }
            }
            stage('Docker build') {
                container('docker') {
                    withCredentials([usernamePassword(
                        credentialsId: 'docker_hub_auth',
                        usernameVariable: 'USERNAME',
                        passwordVariable: 'PASSWORD')]) {
                            /* ./build/libs 생성된 jar파일을 도커파일을 활용하여 도커 빌드를 수행한다 */
                            sh "docker build -t ${DOCKER_IMAGE_NAME}:${DOCKER_IMAGE_TAGS} ."
                            sh "docker login -u ${USERNAME} -p ${PASSWORD}"
                            sh "docker push ${DOCKER_IMAGE_NAME}:${DOCKER_IMAGE_TAGS}"
                    }
                }
            }
            stage('Run kubectl') {
                container('kubectl') {
                    withCredentials([usernamePassword(
                        credentialsId: 'docker_hub_auth',
                        usernameVariable: 'USERNAME',
                        passwordVariable: 'PASSWORD')]) {
                            /* namespace 존재여부 확인. 미존재시 namespace 생성 */
                            sh "kubectl get ns ${NAMESPACE}|| kubectl create ns ${NAMESPACE}"

                            /* secret 존재여부 확인. 미존재시 secret 생성성 */
                            sh """
                                kubectl get secret my-secret -n ${NAMESPACE} || \
                                kubectl create secret docker-registry my-secret \
                                --docker-server=https://index.docker.io/v1/ \
                                --docker-username=${USERNAME} \
                                --docker-password=${PASSWORD} \
                                --docker-email=ekfrl2815@gmail.com \
                                -n ${NAMESPACE}
                            """
                            /* k8s-deployment.yaml 의 env값을 수정해준다(DATE로). 배포시 수정을 해주지 않으면 변경된 내용이 정상 배포되지 않는다. */
                            sh "echo ${DATE}"
                            sh "sed -i.bak 's#DATE_STRING#${DATE}#' ./k8s/k8s-deployment.yaml"

                            /* yaml파일로 배포를 수행한다 */
                            sh "kubectl apply -f ./k8s/k8s-deployment.yaml -n ${NAMESPACE}"
                            sh "kubectl apply -f ./k8s/k8s-service.yaml -n ${NAMESPACE}"

                    }
                }
            }
            notifySuccessful(SLACK_CHANNEL)
        } catch(e) {
        /* 배포 실패 시 */
            currentBuild.result = "FAILED"
            notifyFailed(SLACK_CHANNEL)
        }
    }
}

 

 

 

 

5-8. SonarQube TEST

SonarQube 적용이 완료되었으니 테스트를 진행해보자. 로컬PC의 소스를 수정한 후 git repository로 소스를 push하면 된다. 

우선 Jenkins 로그를 보면 sonarqube Task가 정상적으로 실행되는 것을 확인할 수 있다.

 

다음으로 SonarQube 웹페이지를 확인해보면 프로젝트 분석 결과가 생성된 것을 확인할 수 있다.

 

6. SonarQube에 Jacoco 추가하기

위 포스트 내용을 수행하게 되면 SonarQube는 정상적으로 동작하는 것을 확인할 수 있다. 그런데 Coverage는 0.0%로 나오게 된다. 왜냐하면 CI/CD 파이프라인에 JUnit TEST Step을 추가하지 않았기 때문이다. Jacoco를 연동하고 Code Coverage를 측정하는 방법은 간단하다. 우선 프로젝트에 Test코드(예 JUnit 테스트 코드)를 작성한 후, build.gradle파일(maven이면 pom.xml)에 id 'jacoco' 라고 명시해주고, JenkinsfileTest stage 추가 후 gradle clean test 명령어만 작성해주면 된다.

 

build.gradle : 

plugins {
	id 'org.springframework.boot' version '2.1.6.RELEASE'
	id 'java'
	id "org.sonarqube" version "2.7"	// sonarqube plugin
	id 'jacoco' // sonarqube coverage 를 위함
}

/* sonarqube 설정 추가 */
sonarqube {
	properties {
		property "sonar.projectName", "auth"
		property "sonar.projectKey", "auth"
		property "sonar.host.url", "http://66.42.43.41:30002/sonar"
		property "sonar.login", "851b8cf151af0e972c57debba6af5a4af648fa2f"
	}
}

 

Jenkinsfile : 

/* pipeline 변수 설정 */
def DOCKER_IMAGE_NAME = "twofootdog/project-repo"           // 생성하는 Docker image 이름
def DOCKER_IMAGE_TAGS = "batch-visualizer-auth"  // 생성하는 Docker image 태그
def DOCKER_CONTAINER_NAME = "auth-container"    // 생성하는 Docker Container 이름
def NAMESPACE = "ns-project"
def SLACK_CHANNEL = "auth"
def DATE = new Date();

def notifyStarted(slack_channel) {
    slackSend (channel: "${slack_channel}", color: '#FFFF00', message: "STARTED: Job '${env.JOB_NAME} [${env.BUILD_NUMBER}]' (${env.BUILD_URL})")
}
def notifySuccessful(slack_channel) {
    slackSend (channel: "${slack_channel}", color: '#00FF00', message: "SUCCESSFUL: Job '${env.JOB_NAME} [${env.BUILD_NUMBER}]' (${env.BUILD_URL})")
}
def notifyFailed(slack_channel) {
  slackSend (channel: "${slack_channel}", color: '#FF0000', message: "FAILED: Job '${env.JOB_NAME} [${env.BUILD_NUMBER}]' (${env.BUILD_URL})")
}

podTemplate(label: 'builder',
            containers: [
                containerTemplate(name: 'gradle', image: 'gradle:5.6-jdk8', command: 'cat', ttyEnabled: true),
                containerTemplate(name: 'docker', image: 'docker', command: 'cat', ttyEnabled: true),
                containerTemplate(name: 'kubectl', image: 'lachlanevenson/k8s-kubectl:v1.15.3', command: 'cat', ttyEnabled: true)
            ],
            volumes: [
                hostPathVolume(mountPath: '/home/gradle/.gradle', hostPath: '/home/admin/k8s/jenkins/.gradle'),
                hostPathVolume(mountPath: '/var/run/docker.sock', hostPath: '/var/run/docker.sock'),
                //hostPathVolume(mountPath: '/usr/bin/docker', hostPath: '/usr/bin/docker')
            ]) {
    node('builder') {
        try {
            stage('Start') {
                /* Slack 메시지 전송 */
                notifyStarted(SLACK_CHANNEL)
            }
            stage('Checkout') {
                 checkout scm   // gitlab으로부터 소스 다운
            }
            stage('Test') {
                container('gradle') {
                    sh "gradle clean test"
                }
            }
            stage('Build') {
                container('gradle') {
                    /* 도커 이미지를 활용하여 gradle 빌드를 수행하여 ./build/libs에 jar파일 생성.
                    build 수행 후에는 sonarqube를 활용하여 소스정적분석을 실행한다 */
                    sh "gradle -x test build sonarqube"
                }
            }
            stage('Docker build') {
                container('docker') {
                    withCredentials([usernamePassword(
                        credentialsId: 'docker_hub_auth',
                        usernameVariable: 'USERNAME',
                        passwordVariable: 'PASSWORD')]) {
                            /* ./build/libs 생성된 jar파일을 도커파일을 활용하여 도커 빌드를 수행한다 */
                            sh "docker build -t ${DOCKER_IMAGE_NAME}:${DOCKER_IMAGE_TAGS} ."
                            sh "docker login -u ${USERNAME} -p ${PASSWORD}"
                            sh "docker push ${DOCKER_IMAGE_NAME}:${DOCKER_IMAGE_TAGS}"
                    }
                }
            }
            stage('Run kubectl') {
                container('kubectl') {
                    withCredentials([usernamePassword(
                        credentialsId: 'docker_hub_auth',
                        usernameVariable: 'USERNAME',
                        passwordVariable: 'PASSWORD')]) {
                            /* namespace 존재여부 확인. 미존재시 namespace 생성 */
                            sh "kubectl get ns ${NAMESPACE}|| kubectl create ns ${NAMESPACE}"

                            /* secret 존재여부 확인. 미존재시 secret 생성성 */
                            sh """
                                kubectl get secret my-secret -n ${NAMESPACE} || \
                                kubectl create secret docker-registry my-secret \
                                --docker-server=https://index.docker.io/v1/ \
                                --docker-username=${USERNAME} \
                                --docker-password=${PASSWORD} \
                                --docker-email=ekfrl2815@gmail.com \
                                -n ${NAMESPACE}
                            """
                            /* k8s-deployment.yaml 의 env값을 수정해준다(DATE로). 배포시 수정을 해주지 않으면 변경된 내용이 정상 배포되지 않는다. */
                            sh "echo ${DATE}"
                            sh "sed -i.bak 's#DATE_STRING#${DATE}#' ./k8s/k8s-deployment.yaml"

                            /* yaml파일로 배포를 수행한다 */
                            sh "kubectl apply -f ./k8s/k8s-deployment.yaml -n ${NAMESPACE}"
                            sh "kubectl apply -f ./k8s/k8s-service.yaml -n ${NAMESPACE}"

                    }
                }
            }
            notifySuccessful(SLACK_CHANNEL)
        } catch(e) {
        /* 배포 실패 시 */
            currentBuild.result = "FAILED"
            notifyFailed(SLACK_CHANNEL)
        }
    }
}

 

파일 수정 후 재배포를 해보면 Coverage가 나오는 것을 확인 할 수 있다.

 

 

마치며

이번 포스트에서는 쿠버네티스 클러스터 내에 SonarQube & Jacoco를 적용하는 방법에 대해 알아보았다.

다음 장에서는 쿠버네티스 클러스터에 NodeExporter & Prometheus & AlertManager를 적용하여 CPU/메모리 및 쿠버네티스 컨테이너 모니터링을 하는 법에 대해 알아볼 것이다. 

 

 

참고

https://coderise.io/installing-sonarqube-in-kubernetes/

 

Installing SonarQube in Kubernetes | CodeRise.io

SonarQube is the open source platform to analyze the code. SonarQube shows the health of the application as well as tricky issues which are newly introduced.

coderise.io

https://medium.com/@joypinkgom/%EC%86%8C%EC%8A%A4-%EC%A0%95%EC%A0%81-%EB%B6%84%EC%84%9D%EB%8F%84%EA%B5%AC-sonarqube-%EB%A6%AC%EC%84%9C%EC%B9%AD-9d48fc62b01f

 

소스 정적 분석도구 SonarQube 리서칭

프로그램 소스의 정적 분석 도구로 유명한 SonarQube에 대해 정리합니다.

medium.com

 

https://medium.com/@akamenev/running-sonarqube-on-azure-kubernetes-92a1b9051120

 

Running SonarQube on Kubernetes

Running SonarQube on Kubernetes

medium.com