1. 배경
이전 글을 통해 기본 적인 애플리케이션의 스켈레톤 코드를 작성하고, Spring Cloud on Kubernetes 프레임워크를 사용해서 쿠버네티스에 배포하는 과정까지 진행하였습니다. 이후 실제 로직을 작성하며 개발을 진행하면서 당연하게도 CI/CD 구성에 필요성을 느끼게 되었는데, 테스트 애플리케이션의 경우 MS(MicroService)가 2개이지만 실제 개발되는 애플리케이션의 경우 수많은 마이크로 서비스로 구성되기 때문에 각 모듈을 수동으로 빌드, 배포하는 과정에 시간을 많이 소모하였습니다. 이를 위하여 작업의 효율성을 위해 CI/CD를 구축하며 테스트 한 과정을 기록합니다.
사실 이전에도 작성한 Kubernetes CI/CD 구축 글을 통하여 동일한 구조로 Jenkins-Argo 조합으로 간단하게 구축 할 수 있지만, 이번에는 Github Action을 이용해서 CI를 구현하고, Argo를 통해 배포하는 구성을 생각하였습니다. Gihub Action을 사용하는 이유는 사실 Jenkins를 설치하고 관리하는 부분이 귀찮았습니다.(설치야 간단하겠지만, 초기 설정, 유지보수 부분에서 신경을 쓰고 싶지 않았습니다). 또한 Github Action 또한 사용해 본 적이 없기 때문에 경험 삼아해 보고자 Github Action을 사용하였습니다.
제가 CI/CD를 구성하면서 중요하게 생각한 부분은 한 개의 Github Repo에 여러 개의 MS 코드가 관리되다 보니 파이프라인 실행 시 전체 모듈에 영향을 줄 수 있습니다. 이러한 상황에서 의존성을 최소화하고, 코드의 변화가 일어난 모듈만 CI/CD를 수행하도록, 다른 모듈에는 영향이 없도록 구성하였습니다. 대략적인 CI/CD Flow는 다음과 같습니다.
- 기능 별 Branch 작업 후 Main PR 요청
- Code Review 후 Merge 시 Github Action 동작
- MicroService 중 코드 변화가 이루어진 모듈 확인
- 변화된 모듈 Gradle build
- 빌드된 Jar 파일 바탕으로 이미지 빌드 및 푸쉬
- Argo CD에서 변화된 이미지 감지
- Auto Sync를 통해 자동 배포
실제 개발/배포 과정과는 차이가 있을 수 있습니다. MSA를 구성할 때, 모든 환경에서 동일한 실행 보장, 모든 마이크로서비스의 Health 체크/모니터링, 서비스와 분리된 컨피그 관리, 일체형 소프트웨어 관리 등 DevOps 입장에서 고려할 부분이 많습니다. 이 글은 테스트를 위한 목적으로 간략한 흐름을 기록합니다.
2. Github Action 구성
Github Action은 Repo내에. github/workflows 폴더에 yml 파일을 작성하여 파이프라인을 정의합니다. on
은 Action이 동작하는 Event를 설정합니다. 저는 Main에 푸시가 디렉트로 되는 경우를 강제로 막아 놓았기 때문에 PR을 통해 Merge가 이루어지면 Push 이벤트를 통해 동작하도록 설정하였습니다.
이후 워크플로우를 정의하는 jobs를 작성합니다. action은 특정 가상머신에서 파이프라인을 실행하기 때문에 실행되는 가상머신을 지정해야 됩니다. 본인이 직접 관리하는 컴퓨팅 자원을 이용하여 사용할 수 있는 Self hosted Runner 나 Github에서 제공하는 Github hosted Runner를 이용할 수 있습니다. 당연하게도 Github에서 제공하는 경우 사용에 제한이 있어 배포가 많은 프로젝트에서는 유료버전을 사용해야 할 수 있습니다. 저는 테스트 환경에서 동작하므로 Github에서 제공하는 Runner를 사용하고 OS ubuntu-latest
를 설정하였습니다.
각각의 Job은 독립된 가상머신에서 실행되므로 독립적으로 실행이 필요하거나 서로 연계가 필요한 작업들은 묶어서 처리할 수 있습니다.
2-1. Action 추가/ 변화 코드 감지
이후 각각의 Job에서 각 명령을 수행하는 Steps를 정의합니다. Action의 가장 큰 장점으로 MarketPlace에 등록되어 있는 action을 간편하게 추가해 사용할 수 있습니다. 라이브러리처럼 단순하게 추가하여 활용할 수 있습니다. 저는 코드를 Checkout(복사) 하기 위한 actions/checkout@v2
, 이미지 빌드, 푸시를 위한 docker, gradle build를 위하여 Java, build를 위하여 gradle-build를 설정하였습니다.
name: my-action
on:
push:
branches: [ "main" ]
jobs:
build-and-push:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v2
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v2.9.1
- name: Set up QEMU
uses: docker/setup-qemu-action@v2
- name: Setup Gradle
uses: gradle/gradle-build-action@v2
- uses: actions/setup-java@v3
with:
distribution: temurin
java-version: 17
Workflow에 정의에 필요한 Action을 모두 추가한 후 초기 단계인 코드 변화가 이루어진 모듈을 찾는 작업을 진행합니다. 이 작업 또한 이미 Marketplace에 등록되어 있어 사용하면 됩니다. Root Directory 기준으로 변화가 이루어진 모듈의 명을 추출합니다. 이후 echo를 통해 정상적으로 감지하는지 확인합니다.
- name: Find change modules
uses: Stockopedia/action-get-changed-files@v1
id: get_changed
with:
github-token: ${{ secrets.GIT_TOKEN }}
ignore: "**/+(.github)"
foldersOnly: true
format: csv
- name: Echo changed files
run: echo ${{ steps.get_changed.outputs.changed }}
${{ secrets.GIT_TOKEN }}
의 경우 action에서 사용하기 위한 Secret을 정의해야 합니다.
이후 테스트를 위하여 Employee 모듈에 코드를 추가한 후 푸시하게 되면 다음과 같이 Commit 메시지와 함께 Action이 실행되는 것을 확인할 수 있습니다.
이후 세부 사항을 조회하면 다음과 같이 각 Step이 실행되고, 코드 변화가 이루어진 모듈을 정상적으로 찾는 것을 확인 할 수 있습니다.
2-2. Gradle build
Change_module
을 통하여 get_changed
변수에 변화된 모듈을 저장하였고 이는 employee, departmnent와
같이 여러 개 일 경우, 로 이어집니다. 저는 Run
을 통해 Script를 작성하여, For 문을 돌며 gradle build를 실행합니다. 추가로 IF 문을 통해 Dockerfile이 폴더 내에 존재하면 빌드를 실행하는데 모듈 외의 폴더에서는 실행하지 않도록 설정하기 위함입니다.
- name: Execute Gradle build
run: |
for folder in $(echo ${{ steps.get_changed.outputs.changed }} | tr ',' '\n'); do
echo "Building in $folder..."
if [ -f "$folder/Dockerfile" ]; then
echo "Building gradle in $folder..."
(cd "$folder" && ./gradlew build && ls ./build/libs)
fi
done
마찬 가지로 테스트 진행하면 다음과 같이 가상머신 내에서 빌드가 정상적으로 이루어지고 ls로
jar 파일이 생성된 것을 확인할 수 있습니다.
2-3 Image build/push
이후 생성된 Jar 파일을 바탕으로 이미지를 빌드합니다. 코드가 변화된 모듈을 For문을 통해 전부 이미지를 생성하도록 설정합니다. 또한 변화된 폴더 중 Dockerfile이 있는 경우에만 실행합니다.( 모듈 외의 디렉터리는 실행하지 않음)
- name: Login to ECR
uses: docker/login-action@v2
with:
registry: 077728726991.dkr.ecr.us-east-2.amazonaws.com
username: ${{ secrets.AWS_ACCESS_KEY_ID }}
password: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
- name: Build and push Docker images
run: |
IFS=',' read -ra MODULES <<< "${{ steps.get_changed.outputs.changed }}"
for module in "${MODULES[@]}"; do
echo "Building Docker image in $module..."
if [ -f "$module/Dockerfile" ]; then
docker build -t 077728726991.dkr.ecr.us-east-2.amazonaws.com/$module "$module"
docker push 077728726991.dkr.ecr.us-east-2.amazonaws.com/$module
fi
done
이를 통하여 빌드하고 푸시하도록 합니다. 현재 테스트과정에서 이미지 버전을 따로 관리하지 않기 때문에 태그를 설정하지 않았고, latest
로 푸시하도록 설정하였습니다. 이후 실행 시 다음과 같이 정상적으로 이미지가 생성되는 것을 확인할 수 있습니다.
다음 과정을 통해 CI는 마무리되고, 이후 Argo를 통해 쿠버네티스에 배포하도록 설정합니다.
3. Helm 차트 생성
우선 배포하기 전에 저희는 헬름을 통해 전체 애플리케이션을 배포하기 때문에 차트를 생성해야 됩니다. 실제 차트를 생성하는 과정에서는 Deployment의 배포전략, 리소스 설정, HA 구성, 보안을 위한 SA 설정 등 다양한 요구사항에 따라서 좀 더 복잡한 템플릿이 되지만, 현재 테스트 단계에서는 단순 Deployment와 Ingress를 연결하여 외부에서 통신하는 정도로 템플릿을 생성합니다. 다음과 같이 helm create
커맨드를 통해 차트 템플릿을 생성합니다.
폴더 내부에는 다음과 같이 Chart.yaml과 template 폴더에 샘플용 매티페스트가 저장되어 있습니다.
저는 Department, Employee에 대한 Deployment, Service 템플릿을 만들고 서로 연결하도록 설정하였습니다. Values.yaml에는 이미지 레포에 대한 주소와 함께 컨테이너 포트 정도 설정만 진행하였습니다.
department:
image:
repository: 077728726991.dkr.ecr.us-east-2.amazonaws.com/department
tag: latest
service:
port: 8082
employee:
image:
repository: 077728726991.dkr.ecr.us-east-2.amazonaws.com/employee
tag: latest
service:
port: 8082
이후 배포 시 다음과 같이 파드가 정상적으로 배포되는 것을 확인할 수 있습니다. SCG나 Ingress 역시 마찬가지로 템플릿을 추가하여 헬름을 통해 관리될 수 있도록 설정합니다.
4. Argo CD를 통한 배포
Argo CD를 설치하기 위해 다음 링크를 통해 배포합니다. 설치 후 ArgoCD에서 Application을 추가하기 전에 Github에 접근하기 위한 Secret을 생성합니다. Settings→Repository→Connect Repo→ VIA HTTPS
를 통하여 github 주소와 token을 입력합니다.
이후 애플리케이션 탭에서 New APP을 클릭하여 어플리케이션 정보를 입력합니다.
Repository URL에 깃허브 주소를 입력하고 Path에는 헬름 차트가 저장된 폴더 경로를 입력합니다. 배포 대상이 되는 쿠버네티스 주소와 네임스페이스를 입력합니다.
이후 Value를 설정합니다. 기본 values.yaml
파일을 바탕으로 수동으로 값을 입력할 수 있지만, 저는 values.yml파일을 통해 관리하기 때문에 파일만 지정해 줍니다.
이후 애플리케이션이 등록되고, Sync를 누르면 배포가 시작됩니다.
이후 싱크가 완료되면 쿠버네티스에 어플리케이션이 배포된 것을 확인할 수 있습니다. 실제 CI/CD 과정에서는 테스트 빌드, 여러 스테이지 환경을 통한 구성 등 더 복잡한 과정이 추가됩니다.
'Spring Cloud' 카테고리의 다른 글
SpringCloud를 이용한 MSA 개발#1 (Kubernetes) (0) | 2023.10.12 |
---|