intro : 백엔드(spring boot) 프로젝트에 다양한 ci/cd 방식을 적용해보자.
하기 글은 다음과 같은 과정이 선행되어야 합니다. 다른 글들에 비해 생략된 과정이 많습니다.
[aws] ec2를 통해 백엔드 api 서버 배포하기 (1)
[aws] ec2를 통해 백엔드 api 서버 배포하기 (2)
[pipeline] ci/cd와 github actions의 기본 개념
개인 프로젝트에서 많이 쓰는 CI/CD 구축 방법
이 방법은 주로 개인 및 소규모 프로젝트에서 CI/CD를 심플하고 빠르게 적용시키고 싶을 때 사용한다. 해당 방식은 다음과 같은 장점과 단점을 가진다.
장점
git pull
을 활용해서 변경된 부분의 프로젝트 코드에 대해서만 업데이트 하기 때문에 CI/CD 속도가 빠르다. 대부분의 CI/CD 방식들은 전체 프로젝트를 통째로 갈아끼우는 방식
을 사용한다. CI/CD 툴로 Github Actions
만 사용하기 때문에 인프라 구조가 복잡하지 않고 간단
하다.
단점
빌드 작업을 EC2
에서 직접 진행
하기 때문에 운영하고 있는 서버의 성능에 영향을 미칠 수 있다. Github 계정 정보가 해당 EC2에 저장되기 때문에 개인 프로젝트 또는 믿을만한 사람들과 같이 진행하는 토이 프로젝트에서만 사용해야 한다.
CI/CD를 적용하기 전의 과정
CI/CD를 도입하기 전의 배포 과정을 살펴보자. 일반적으로 AWS EC2를 사용하는 환경을 가정하면 기본적인 아키텍처와 배포 절차는 다음과 같다.
1. 코드 Push
: 개발자가 GitHub의 Main Branch에 코드를 Push 한다.
2. 소스 Pull
: AWS EC2에 SSH로 접속한 후, GitHub에서 최신 Main Branch 코드를 Pull 한다.
3. 빌드 및 실행
: EC2 내에서 코드를 빌드하고 애플리케이션을 재 실행 한다.
이 과정의 가장 큰 문제는 최신 코드를 배포할 때마다 개발자가 직접 EC2에 접속하여 수작업으로 Pull, 빌드, 실행을 반복해야 한다는 점이다. 이는 번거롭고, 효율성이 떨어지며, 실수 발생 가능성도 높다. 이러한 문제를 해결하기 위해 우리는 CI/CD를 도입하여 배포 프로세스를 자동화할 것이다.
하기 단계를 진행하기전에 반드시 진행해야 합니다.
sample 프로젝트 로컬에 설치
해당 프로젝트 AWS EC2
에 직접 Git Pull 받고 Build하여 실행
정상적으로 Public IP
로 접근되는지 확인하기
GitHub Actions에서 EC2에 직접 접근 후 빌드 및 실행
주의사항
AWS EC2 구축시에, 인스턴스 유형으로 t3a.small
을 선택하여 구성하자, 프리티어의 t2.micro
는 빌드시에 엄청난 리소스에 서버가 먹통이돤다.
Step1. sample 프로젝트 다운로드
기존의 단점이었던 문제를 해결하기 위해서 GitHub Actions에서 AWS의 EC2에 SSH로 접근하여 빌드 후 실행하는 과정을 yml파일로 작성해 보자. 서버파일은 sample 프로젝트를 사용하는것을 권장한다. 해당 프로젝트를 로컬에 다운받고 먼저 실행해 보자. 별다른 문제없이 다음과 같은 화면이 나온다면 문제없이 실행된 것을 알 수 있다.
Step2. sample 프로젝트 deploy.yml 파일 생성
sample
프로젝트 루트에 .github/workflows
폴더를 생성하고 deploy.yml
파일을 생성한다. 그렇다면 다음과 같은 구조가 되어있을 것이다.
Step3. deploy.yml 파일에 코드 작성
이어서 deploy.yml
파일에 다음과 같은 코드를 작성한다.
name: Deploy To EC2
on:
push:
branches:
- main
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- name: SSH로 EC2에 접속하기
uses: appleboy/ssh-action@v1.0.3
with:
host: ${{ secrets.EC2_HOST }} # EC2의 주소
username: ${{ secrets.EC2_USERNAME }} # EC2 접속 username
key: ${{ secrets.EC2_PRIVATE_KEY }} # EC2의 Key 파일의 내부 텍스트
script_stop: true
script: |
cd /home/ubuntu/api-server-pipeline # 여기 경로는 자신의 EC2에 맞는 경로로 재작성하기
git pull origin main
./gradlew clean build
sudo fuser -k -n tcp 8080 || true
nohup java -jar build/libs/*SNAPSHOT.jar > ./output.log 2>&1 &
secrets 값 설정은 아래 공간에서 해야 합니다
Step4. Code를 GitHub에 Push 하기
위 단계에서 deploy.yml 파일에 코드를 작성하였는데, 해당 코드를 push
한다. 그럼 GitHub Actions
탭에서 다음과 같은 화면을 확인 할 수 있다.
이 방법의 단점
가장 처음에는 AWS EC2의 유형으로 t2.micro
를 사용하여 똑같은 deploy.yml
파일의 설정으로 CI/CD를 진행해 보았는데, 서버가 먹통이 되었다. 아무래도 빌드하는 과정이 생각보다 리소스를 크게 잡아먹는듯 하여 타임아웃 오류가 났는데, 인스턴스 유형을 t3a.small
변경하고 재 진행하여보니 정상적으로 실행된 것을 확인할 수 있었다. 또다른 해결책으로는 swap을 통해서 메모리 사용량을 늘리는 방법이 있다고 한다. 무료 인스턴스 유형으로 해결하기에는 좀 버거워 보인다.
일반 프로젝트에서 많이 쓰는 CI/CD 구축 방법
이 방법은 주로 현업에서 초기 서비스를 구축할 때 이 방법을 많이 활용한다. 처음 서비스를 구현할 때는 대규모 서비스에 적합한 구조로 구현하지 않는다. 확장의 필요성이 있다고 느끼는 시점에 인프라를 고도화하기 시작한다. 왜냐하면 복잡한 인프라 구조를 갖추고 관리하는 건 생각보다 여러 측면에서 신경(비용
)쓸 게 많아지기 때문이다. 해당 방식은 다음과 같은 장점과 단점을 가진다.
장점
빌드 작업을 Github Actions에서 하기 때문에 운영하고 있는 서버의 성능에 영향을 거의 주지 않는다.
CI/CD 툴로 Github Actions만 사용하기 때문에 인프라 구조가 복잡하지 않고 간단하다.
단점
무중단 배포를 구현하거나 여러 EC2 인스턴스에 배포를 해야 하는 상황이라면, 직접 Github Actions에 스크립트를 작성해서 구현해야 한다. 직접 구현을 해보지는 않았는데 꽤 복잡하다고 한다.
하기 단계를 진행하기 전에 위 단계를 꼭 거쳐주세요
lsof -i :8080 // 8080 포트로 실행중인 프로세스 찾기
kill -9 PID // pid 값에 8080 포트로 실행중인 프로세스 종료하기
rm -rf api-server-pipeline // 기존에 EC2에 설치되어있는 sample 프로젝트 삭제하기
GitHub Actions에서 빌드 후 EC2에 빌드 파일 실행
기존에는 GitHub Actions의 역할이 EC2에 접근하여 직접 빌드 및 실행 하는 역할이었다. 이번에는 EC2에서 빌드하는 것이 아닌, GitHub Actions에서 직접 빌드하고, 빌드된 파일을 EC2에 전달만하여 빌드파일을 EC2는 실행만 하는 역할로 CI/CD를 구축해볼 것이다.
Step1. deploy.yml 파일 수정
deploy.yml
파일을 다음과 같이 수정한다. 중요한 부분은 SCP
를 통해서 GitHub Action에서 빌드한 파일을 EC2
에 전달한다는 점이다.
name: Deploy To EC2
on:
push:
branches:
- main
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- name: Github Repository 파일 불러오기
uses: actions/checkout@v4
- name: JDK 17버전 설치
uses: actions/setup-java@v4
with:
distribution: temurin
java-version: 17
- name: 테스트 및 빌드하기
run: ./gradlew clean build
- name: 빌드된 파일 이름 변경하기
run: mv ./build/libs/*SNAPSHOT.jar ./project.jar
- name: SCP로 EC2에 빌드된 파일 전송하기
uses: appleboy/scp-action@v0.1.7 # 빌드 파일 전송 라이브러리
with:
host: ${{ secrets.EC2_HOST }}
username: ${{ secrets.EC2_USERNAME }}
key: ${{ secrets.EC2_PRIVATE_KEY }}
source: project.jar
target: /home/ubuntu/api-server-pipeline/tobe
- name: SSH로 EC2에 접속하기
uses: appleboy/ssh-action@v1.0.3 # SSH 접속 라이브러리
with:
host: ${{ secrets.EC2_HOST }}
username: ${{ secrets.EC2_USERNAME }}
key: ${{ secrets.EC2_PRIVATE_KEY }}
script_stop: true
script: |
rm -rf /home/ubuntu/api-server-pipeline/current
mkdir /home/ubuntu/api-server-pipeline/current
mv /home/ubuntu/api-server-pipeline/tobe/project.jar /home/ubuntu/api-server-pipeline/current/project.jar
cd /home/ubuntu/api-server-pipeline/current
sudo fuser -k -n tcp 8080 || true
nohup java -jar project.jar > ./output.log 2>&1 &
rm -rf /home/ubuntu/api-server-pipeline/tobe
Step2. Controller 파일의 return 값을 수정
정상적으로 반영되었는지 확인을 하기위헤서 return 값을 수정해보자.
package com.example.api_server_pipeline.controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class AppController {
@GetMapping("/")
public String index() {
return "Hello World, GitHub Actions CI/CD Success! + 변경하였습니다!";
}
}
Step3. 변경된 파일들을 git push 하고 결과 확인
수정된 deploy.yml 파일과 Controller 파일을 git push 하여 정상적으로 EC2에 빌드하여 실행되었는지 확인해보자.
SCP를 통한 CI/CD 구축
위 방법은 굉장히 개인적으로 진행하는 서비스에 당장 적용해보고싶은 방법이다. EC2 성능에 영향을 미치지도 않고, 빌드도 GitHub 에서 진행하는거라서 부담도 없는거 같다.