Spring Boot CI/CD 파이프라인: GitHub Actions부터 ECS 배포까지
Spring Boot 프로젝트에 GitHub Actions 기반 CI/CD 파이프라인을 구성하는 방법을 단계별로 설명합니다. 테스트 자동화, Docker 이미지 빌드, ECR 푸시, 롤링/블루그린/카나리 배포 전략을 실전 예제로 다룹니다.
지난 글에서 Spring Boot 애플리케이션을 Docker 이미지로 효율적으로 패키징하는 방법을 살펴봤다. 이번 글에서는 그 이미지를 자동으로 빌드하고 프로덕션에 배포하는 CI/CD 파이프라인을 구성한다. GitHub Actions를 기반으로 테스트 자동화부터 AWS ECS 배포까지 실전 파이프라인을 단계별로 만들어 본다.
CI/CD의 목표
CI(지속적 통합)는 코드 변경이 생길 때마다 자동으로 빌드·테스트해서 문제를 조기 발견하는 것이 목표다. CD(지속적 배포)는 테스트를 통과한 빌드 산출물을 자동으로 스테이징 혹은 프로덕션에 배포하는 것이다. 이 두 가지를 잘 구성하면 배포가 두려운 이벤트에서 평범한 일상으로 바뀐다.
기본 CI 워크플로우 구성
GitHub Actions에서 워크플로우는 .github/workflows/ 디렉터리에 YAML 파일로 정의한다.
# .github/workflows/ci.yml
name: CI
on:
push:
branches: [main, develop]
pull_request:
branches: [main]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-java@v4
with:
java-version: '21'
distribution: 'temurin'
cache: 'gradle' # Gradle 캐시 자동 관리
- name: Grant execute permission
run: chmod +x gradlew
- name: Run tests
run: ./gradlew test
- name: Upload test report
uses: actions/upload-artifact@v4
if: failure()
with:
name: test-report
path: build/reports/tests/
cache: 'gradle' 하나만 추가해도 의존성 다운로드 시간이 대폭 줄어든다. 첫 번째 실행에서 캐시를 채우고, 이후 실행은 ~/.gradle/caches를 재사용한다.
Docker 이미지 빌드 및 ECR 푸시
테스트 통과 후 Docker 이미지를 빌드해서 AWS ECR에 업로드한다.
build-and-push:
needs: test
runs-on: ubuntu-latest
if: github.ref == 'refs/heads/main'
permissions:
id-token: write # OIDC 토큰 발급
contents: read
steps:
- uses: actions/checkout@v4
- uses: actions/setup-java@v4
with:
java-version: '21'
distribution: 'temurin'
cache: 'gradle'
- name: Build JAR
run: ./gradlew bootJar -x test
- name: Configure AWS credentials (OIDC)
uses: aws-actions/configure-aws-credentials@v4
with:
role-to-assume: ${{ secrets.AWS_ROLE_ARN }}
aws-region: ap-northeast-2
- name: Login to ECR
id: login-ecr
uses: aws-actions/amazon-ecr-login@v2
- name: Build and push Docker image
env:
ECR_REGISTRY: ${{ steps.login-ecr.outputs.registry }}
ECR_REPO: my-spring-app
IMAGE_TAG: ${{ github.sha }}
run: |
docker build -t $ECR_REGISTRY/$ECR_REPO:$IMAGE_TAG .
docker build -t $ECR_REGISTRY/$ECR_REPO:latest .
docker push $ECR_REGISTRY/$ECR_REPO:$IMAGE_TAG
docker push $ECR_REGISTRY/$ECR_REPO:latest
echo "image=$ECR_REGISTRY/$ECR_REPO:$IMAGE_TAG" >> $GITHUB_OUTPUT
id: image
비밀 키 대신 OIDC(id-token: write)를 사용하면 AWS 크리덴셜이 워크플로우에 저장되지 않아 더 안전하다. AWS 콘솔에서 GitHub Actions OIDC 공급자를 등록하고 역할에 ECR 푸시 권한을 부여하면 된다.
배포 전략 선택
롤링 배포
기존 인스턴스를 순차적으로 새 버전으로 교체한다. 추가 리소스 없이 배포 가능하지만, 교체 중간에 v1과 v2가 동시에 서비스되므로 API 하위 호환성이 보장돼야 한다.
- name: Deploy to ECS (Rolling)
run: |
aws ecs update-service \
--cluster my-cluster \
--service my-service \
--task-definition my-task:$TASK_DEF_REVISION \
--force-new-deployment
블루/그린 배포
완전히 새로운 환경(Green)에 새 버전을 배포하고, 로드밸런서에서 트래픽을 한 번에 전환한다. 즉시 롤백이 가능하지만 동시에 두 배의 인프라가 필요하다. AWS CodeDeploy와 ECS를 함께 사용하면 쉽게 구성할 수 있다.
카나리 배포
트래픽의 일부(예: 10%)만 새 버전으로 보내고, 오류율·응답시간 등 메트릭을 모니터링하면서 점진적으로 비율을 높인다. 가장 안전하지만 구성이 복잡하다. ALB의 가중치 라우팅이나 AWS App Mesh를 활용한다.
환경별 파이프라인 분리
스테이징과 프로덕션은 별도로 관리하는 것이 좋다.
deploy-staging:
needs: build-and-push
runs-on: ubuntu-latest
environment: staging # GitHub Environments로 보호
steps:
- name: Deploy to staging
run: |
aws ecs update-service \
--cluster staging-cluster \
--service my-service \
--task-definition my-task:latest
deploy-production:
needs: deploy-staging
runs-on: ubuntu-latest
environment: production # 수동 승인 게이트
steps:
- name: Deploy to production
run: |
aws ecs update-service \
--cluster prod-cluster \
--service my-service \
--task-definition my-task:latest
GitHub Environments의 Required reviewers를 설정하면 프로덕션 배포 전에 승인 단계가 추가된다.
Gradle 빌드 캐시 최적화
CI 환경에서 Gradle 빌드를 빠르게 만드는 핵심 설정들이다.
# gradle.properties
org.gradle.daemon=false # CI에서 데몬 불필요
org.gradle.parallel=true # 멀티 프로젝트 병렬 빌드
org.gradle.caching=true # 빌드 캐시 활성화
org.gradle.jvmargs=-Xmx2g -XX:+HeapDumpOnOutOfMemoryError
GitHub Actions의 cache: 'gradle' 옵션과 함께 사용하면 의존성 재다운로드와 증분 컴파일이 모두 캐시된다.
배포 상태 확인
ECS에 배포한 뒤 서비스가 정상적으로 안정화됐는지 확인하는 단계를 추가하면 배포 실패를 조기에 감지할 수 있다.
# ECS 서비스 안정화 대기 (최대 10분)
aws ecs wait services-stable \
--cluster my-cluster \
--services my-service
# 헬스체크 엔드포인트 확인
curl -f https://api.example.com/actuator/health || exit 1
aws ecs wait services-stable 명령은 새 태스크가 모두 RUNNING 상태가 되고 이전 태스크가 STOPPED될 때까지 폴링한다. Spring Boot Actuator의 /actuator/health를 추가로 확인하면 애플리케이션 레벨의 준비 상태까지 검증할 수 있다.
정리
효율적인 CI/CD 파이프라인의 핵심은 빠른 피드백 루프다. 테스트 → 빌드 → 이미지 생성 → 배포의 각 단계를 자동화하되, 스테이징과 프로덕션 사이에 적절한 승인 게이트를 두면 속도와 안전성을 모두 확보할 수 있다.
지난 글: Spring Boot 애플리케이션 도커라이징
다음 글: Spring Boot 시크릿 관리: 환경변수부터 AWS Secrets Manager까지
읽어주셔서 감사합니다. 😊