Cloud Tech

SonarQube를 활용한 EKS CleanCode CI /CD 파이프라인 구축하기

Hanhorang31 2024. 4. 20. 18:01
 

Overview

클린코드를 위해 SonarQube를 배포하고 AWS CodeSeries를 통해 CI /CD 파이프라인 구성하겠습니다.

해당 문서는 EKS 워크샵을 참고하였습니다.

CleanCode

CleanCode(이하 클린코드)는 읽기 쉽고 이해하기 쉬운 코드를 뜻합니다. 클린 코드를 유지하면 소프트웨어의 가독성, 유지 보수성 및 신뢰성을 향상시켜 장기적인 개발 비용을 절감할 수 있습니다.

  • 유지보수의 용이성: 명확하고 이해하기 쉬운 코드는 유지보수 시 시간과 비용을 줄여줍니다.
  • 버그 감소: 잘 구조화되고 명확한 코드는 오류 발생 가능성을 낮춥니다.
  • 기술 부채 관리: 클린 코드 접근 방식은 기술 부채를 효과적으로 관리하고 미래의 확장성 및 유연성을 보장합니다.

SonarQube

클린코드를 유지하기 위한 툴로 SonarQube를 소개하겠습니다. SonarQube는 품질 향상을 위한 기능 및 CI 툴과의 통합 기능이 지원되어 품질 유지 툴로 가장 많이 사용하고 있습니다. 커뮤니티 버전 한정으로 무료이며 래퍼런스가 많다는 장점이 있습니다.

아키텍처 관점으로 보면 소스코드에 대해 스캐너 및 분석을 하여 SonarQube서버에 전달하여 대시보드로 결과를 확인할 수 있습니다.

스캐너와 분석 부분은 CLI 명령어를 통해 진행이 가능합니다. 본 워크샵에서도 아래 명령어를 Codebuild의 빌드 과정에서 수행될 수 있도록 구성되었습니다.

# Run compile, execute sonar scanner and create application package
mvn clean install sonar:sonar package -Dmaven.test.skip=true -Dcheckstyle.skip -Dsonar.projectKey=$SONAR_PROJECT -Dsonar.qualitygate.wait=true -Dsonar.qualitygate.timeout=120 -Dsonar.verbose=true $SQ_ANALYSIS_PARAMS

CodeSeries

AWS에서 제공하는 개발자 도구 서비스들입니다. Git과 같은 소스 저장소 역할을 하는 CodeCommit, 빌드 서비스인 CodeBuild, CI & CD 서비스를 제공하는 CodePipeline이 있습니다.

깃, 젠킨스등의 오픈소스 도구와 비교하면 다음 표와 같이 특징을 비교할 수 있습니다.

장점
단점
완전 통합된 환경 AWS 클라우드 서비스와의 완벽한 통합으로 상호작용이 매우 효율적입니다.
비용 무료 오픈소스 도구에 비해 사용량에 따라 비용이 발생하며, 증가할 수 있습니다.
관리의 용이성 완전 관리형 서비스로 서버 유지관리나 업그레이드에 대한 걱정이 없습니다.
유연성 제한 AWS 환경에 최적화되어 있어 다른 클라우드 또는 온프레미스 환경에서는 유연성이 제한될 수 있습니다.
보안 및 규정 준수 AWS의 높은 보안 모델을 상속받아 강력한 보안과 규정 준수 기능을 제공합니다.
학습 곡선 AWS 서비스 사용법, 용어, 설정 방법 등을 새로 배워야 할 필요가 있습니다.
확장성 사용자 요구에 맞게 자동으로 리소스를 조정할 수 있는 자동 확장 기능을 제공합니다.
 

보안 요구사항, 비용, 오픈소스 도구 관리 측면으로 비교하여 사용하는 것을 추천드립니다. 본 장에서는 AWS 워크샵 기반 실습으로 사용한 서비스인 AWS codeSeries를 사용하였습니다.

CI / CD 구성 

워크샵 아키텍처를 이해하고 각 서비스별 배포를 진행하겠습니다. 아키텍처를 다시 확인하면 다음과 같은데요, CodeCommit브랜치에 따라 빨간 영역과 하얀 영역으로 나뉘어 수행됩니다.

  • 좌측 상단(빨간 영역) : CodeCommit Repo에서 PR 이벤트가 감지되는 경우 SonarQube에서 품질 검사하는 단계까지만 진행합니다.
  • 하얀 영역 : CodeCommit에서 Main 브랜치가 커밋된 경우 빨간 영역에서 진행한 SonarQube에서 품질 검사와 애플리케이션 배포(이미지 생성 및 푸쉬와 EKS 배포)를 수행합니다.

위 기능들에 대해 테스트를 진행하겠습니다. EKS의 경우 eksctl 및 콘솔에서 클러스터를 생성합니다. 클러스터 명은 sonar-cluster로 설정하며 버전은 1.28로 설정하여 진행하였습니다. (클러스터 명이 다른 경우 아래 예제 애플리케이션 buildspec.yaml의 변수에서 클러스터 변수를 설정해야 합니다.)

SonarQube 배포

EKS 구성 확인 후 Helm 를 통해 SonarQube를 배포하겠습니다. 차트에서 주의할 값은 edtion 인데요.

Edition의 설정 값에 따라 유료(developer), 무료 버전(Community)으로 나뉘어 설치가 진행됩니다. 이번 글에서는 무료 버전으로 배포할 예정입니다. edtion 별 지원 기능 및 언어 차이가 있습니다. 지원 기능별 비교는 링크를 참고해주세요.

helm repo add sonarqube https://SonarSource.github.io/helm-chart-sonarqube
helm repo update

helm show values sonarqube/sonarqube > sonarqube-values.yaml

vi sonarqube-values.yaml
-- 
# edtion 값 Community 확인 
OpenShift:
  enabled: false
  createSCC: true

edition: "community"

# service 타입 변경
service:
  type: LoadBalancer
  externalPort: 9000
  internalPort: 9000
  labels:
  
..
 
kubectl create ns sonarqube-namespace 

helm upgrade --install -f sonarqube-values.yaml -n sonarqube-namespace sonarqube-release sonarqube/sonarqube

차트 배포 후 파드가 running되기 까지 약 5분~10분이 소요됩니다.

배포 구성 확인 후 sonarqube 대시보드에 접속하여 브랜치를 설정하겠습니다.

kubectl get all -n sonarqube-namespace

구성 내용 중 로드밸런서 IP를 확인하여 포트 9000으로 접속하여 로그인합니다.

 

초기 아이디와 비밀번호는 admin입니다.

대시보드 접근 후 클린코드 분석을 위한 브랜치 생성 및 API 키 발급이 필요합니다.

브랜치 생성

상단 탭 Projects > Create a local project 클릭 > java-spring-example, main 브랜치로 설정

API 키 발급

우측 상단 A 메뉴 My Account > Security > Token Generate

CodeCommit 구성

예제 애플리케이션 코드 저장소를 구성하겠습니다.

먼저, AWS CodeCommit 에 액세스하기 위한 자격 증명 생성이 필요합니다. IAM 사용자에서 사용중인 사용자에 대해 자격 증명을 생성해주세요.

그 다음 CodeCommit를 구성합니다. Repo 구성 후 HTPS(GRC) 복제를 클릭하여 클립보드에 해당 repo의 URL을 복사합니다.

예제 애플리케이션을 가져와서 구성한 Repo에 올리겠습니다.

git clone https://github.com/SonarSource-Demos/sonar-aws-java-app.git

cd sonar-aws-java-app

git remote rename origin upstream

# git remote add origin REPLACE_WITH_URL_OF_CODECOMMIT_REPO
git remote add origin codecommit::ap-northeast-2://sonar-sample-app

git push origin

git checkout -t upstream/new-service

# IAM CodeCommit 자격증명 아이디, 비밀번호 입력
git push origin 

ECR(컨테이너 이미지용 저장소) 구성

예제 애플리케이션을 이미지로 저장시키기 위해 ECR을 구성하겠습니다. 아래 명령어를 입력하면 프라이빗 레지스트리로 구성됩니다.

aws ecr create-repository --repository-name app-container-repo 

AWS SecretManager 구성

SonarQube를 액세스하기 위한 API 토큰와 호스트를 암호화하여 데이터 노출을 방지하겠습니다. 워크샵에서는 AWS SecretManager를 통해 데이터를 관리하지만 비용이 발생합니다. 다른 CI 툴(Jenkins)에서는 키 값을 자체적으로 관리할 수 있습니다. 또한, AWS 데이터 관리의 다른 서비스인 파라미터 스토어는 무료이며DB 파라미터 등의 접근키가 아니면 비용측면에서 좋은 선택이 될 수 있습니다.

다른 유형의 보안 암호와 키/값을 위에 구성한 SonarQube의 URL 및 토큰을 입력해주세요.

URL은 포트 번호까지 입력하여 포맷은 http://ingressALB:9000/ 와 같이 입력해야 합니다.

구성 완료 후 SecretManager ARN은 코드시리즈 연동을 위해 사용됩니다. 따로 복사하여 기록해주세요.

CodeBuild 구성

이어서 Java 애플리케이션을 컴파일하고, Sonar를 사용하여 정적 코드 분석을 트리거하고, 이미지 빌드 및 Amazon ECR 레지스트리에 푸시하는 데 사용되는 AWS CodeBuild 프로젝트를 생성하겠습니다.

아래 설정 옵션을 참고하여 CodeBuild를 구성해주세요.

프로젝트 구성 이름 : clean-java-code-build

소스 선택 : AWS CodeCommit, sonar-sample-app, main 브랜치 설정

환경 : 관리형 이미지, AWS linux 2, Standard, aws/codebuild/amazonlinux2-x86_64-standard:5.0 선택

권한있음 옵션 태그 설정

환경 변수 추가

  • AWS_ACCOUNT_ID : your AWS account
  • IMAGE_TAG : Latest
  • IMAGE_REPO_NAME : app-container-repo

이어서 CodeBuild 에서 ECR 푸쉬와 Secret Manager 액세스를 위한 IAM 정책을 추가해야 합니다.

IAM > Role 에서 Codebuild-clean-java-code 로 시작하는 role을 확인하여 아래 인라인 정책을 추가 합니다.

  1. ECR
{
	"Version": "2012-10-17",
	"Statement": [
		{
			"Action": [
				"ecr:GetAuthorizationToken"
			],
			"Resource": "*",
			"Effect": "Allow"
		},
		{
			"Action": [
				"ecr:BatchCheckLayerAvailability",
				"ecr:CompleteLayerUpload",
				"ecr:InitiateLayerUpload",
				"ecr:PutImage",
				"ecr:UploadLayerPart"
			],
			"Resource": "arn:aws:ecr:ap-northeast-2:REPLACE_AWS_ACCOUNT_ID:repository/app-container-repo",
			"Effect": "Allow"
		}
	]
}
  • REPLACE_AWS_ACCOUNT_ID 에는 AWS Account를 입력해주세요.
  1. AWS Secret Manager
{
	"Version": "2012-10-17",
	"Statement": [
		{
			"Effect": "Allow",
			"Action": [
				"secretsmanager:GetResourcePolicy",
				"secretsmanager:GetSecretValue",
				"secretsmanager:DescribeSecret",
				"secretsmanager:ListSecretVersionIds"
			],
			"Resource": [
				"REPLACE-WITH-YOUR-SECRET-MANAGER-ARN" 
			]
		},
		{
			"Effect": "Allow",
			"Action": "secretsmanager:ListSecrets",
			"Resource": "*"
		}
	]
}
  • REPLACE-WITH-YOUR-SECRET-MANAGER-ARN 는 AWS Secret Manager Arn을 입력하여 생성해주세요.

IAM 정책을 추가에 이어 EKS 클러스터 권한 설정도 필요합니다. CodeBuild를 활용하여 코드 분석을 트리거하고 컨테이너 이미지를 구축할 뿐만 아니라 Amazon EKS 클러스터에 Java 애플리케이션을 배포하기 때문입니다. 아래 명령어를 통해 CodeBuild 프로젝트가 사용하는 IAM 역할을 클러스터 configmap 에 추가합니다.

eksctl create iamidentitymapping --cluster  <cluster-name> --arn $(aws iam get-role --role-name codebuild-clean-java-code-build-service-role --query Role.Arn --output text | sed -r 's/\/service-role//') --group system:masters --username admin

CodePipeline 구성

파이프라인을 구성하겠습니다. CodeBuild 생성에서 각 스테이지를 추가해주세요.

  • 소스 스테이지 : 위 생성한 codecommit의 이름과 브랜치 설정
  • 빌드 스테이지 : 위 생성한 codebuild의 이름과 Sonarqube 브랜치 설정(#{SourceVariables.BranchName})

배포 스테이지는 스킵으로 넘어가주세요.

CodePipeline을 통해 build을 확인하였으면 SonarQube에서 코드 품질에 대해 확인이 가능합니다.

AWS Sonar EventBridge 배포

code Repo에 PR 이 요청되면, CodeBuilds StartBuildAPI를 트리거할 수 있도록 EventBridge를 설정하겠습니다. EventBridge 구성은 워크샵에서 제공하는 Cloudformation을 통해 구성하겠습니다. 구성을 위한 입력 변수로 아래 명령어 결과의 ARN을 복사합니다.

aws codecommit get-repository --repository-name sonar-sample-app
aws codebuild batch-get-projects --names clean-java-code-build

CloudFormation 스크립트 eventbridge-rule-codebuild.json을 다운받습니다. Cloudformation 에서 리소스 가져오기로 eventbridge-rule-codebuild.json 을 템플릿 업로드하고 ARN 값을 입력해주세요.

구성에는 약 5분정도 소요됩니다.

여기서 구성한 리소스는 EventBridge 로 CodeCommit에서 발생한 "Pull Request State Change" 이벤트를 감지할 때 마다 특정 조치를 취하도록 설정됩니다. 이 규칙은 이벤트 발생시 CodeBuild 프로젝트를 트리거하도록 설정되어 있습니다.

구성이 완료되었다면 실제 동작 기능을 확인하겠습니다.

앞서 배포한 소스를 수정하여 다시 PR을 요청한 후 SonarQube를 확인하겠습니다.

cd /sonar-aws-java-app/src/main/java/com/sonarsource/springdemo
  • Hello World에서 Hello AEWS 로 수정하였습니다.
git add .
git commit -m "update error code"
git push origin main

codecommit의 소스가 업데이트되면 codepipeline을 통해 codebuild가 수행됩니다. 빌드 완료 후 sonarqube 대시보드를 확인하면 코드 검사 및 이미지를 확인할 수 있습니다.

기능 이해

CI / CD 구성 동작은 예제 프로젝트 파일sonar-aws-java-app/buildspec.yml 에서 확인할 수 있습니다. 해당 파일은 Codebuild에서

빌드 스크립트로 사용됩니다. 변수와 각 단계별 수행 기능은 다음과 같습니다.

환경 변수 설정

version: 0.2
env:
  secrets-manager:
    SONAR_TOKEN: prod/sonar:SONAR_TOKEN
    SONAR_HOST_URL: prod/sonar:SONAR_HOST_URL
  variables:
    SourceBranch: ""
    DestinationBranch: ""
    IMAGE_REPO_NAME: "app-container-repo"
    IMAGE_TAG: "Latest"
    EKS_CLUSTER_NAME: "sonar-cluster"
    EKS_NS_APP: "sonar-aws-javaapp-ns"
    EKS_CODEBUILD_APP_SVC: "sonar-aws-javaapp-svc"
    EKS_DEPLOY_APP: "sonar-aws-javaapp-deploy"
    EKS_ROLE: "ClustWorkshopAdminRole"
    SONAR_PROJECT: "java-spring-example"
    PRKey: ""
  shell: bash
  • 위에서 데이터를 암호화했던 secrets-manager 의 값을 가져오며, variables에서 브랜치 설정 및 ECR, EKS 배포 옵션을 설정합니다. CD 테스트로 EKS에 예제 애플리케이션이 배포되지 않았다면 EKS_CLUSTER_NAME 을 구성한 클러스터로 설정해주세요.

빌드 단계별 기능

빌드 단계는 3단계로 pre_build, build, post_build 로 나뉘어져 있습니다.

  • pre_build : 빌드 서버에 테스트 수행 툴 설치 및 환경 설정하는 단계입니다.
  • build : sonarqube에 코드 품질을 위해 테스트하는 단계입니다. 설정된 sonarqube 브랜치를 설정하여 다음의 명령어로 서버에 스크립트를 전달합니다.
  • post_build : 빌드 후의 단계로 조건에 맞게 빌드가 된 경우 이미지를 빌드하여 ECR에 푸쉬하고, EKS에 deployment 에 배포합니다. 스크립트를 보면 다음과 같습니다.

리소스 삭제

콘솔에서 리소스를 생성한 만큼 비용에 주의해주세요. 아래 리소스에 대해 삭제 확인이 필요합니다.

콘솔에서 확인 후 삭제해주세요.

  • CodeSeries(Commit, Build, Pipeline)
  • EventBridge(CloudFormation 에서 스택 삭제)
  • Secrets Manager
  • EKS Cluster