ECS와 GitHub Action을 활용한 배포 자동화

Hyeon·2025년 4월 24일
post-thumbnail

기존 EC2 수동 배포 방식의 문제점

서비스를 개발하면서 가장 처음 마주한 배포 환경은 단순한 EC2 기반 수동 배포였다. 스케일링된 모든 인스턴스에 접속해서 수작업으로 코드를 최신화하고, 의존성을 설치하고, 서버를 재시작했다. 이 방식은 실수가 잦고 반복 작업이 많아 생산성과 안정성 모두에 문제를 야기했다. 배포 시간도 오래 소요될 뿐더러, 위험도가 높아 배포 가능인원이 제한됐다. 오류 발생 시 롤백하는 것도 어려웠다.

왜 Docker + ECS 인가? (vs Elastic Beanstalk)

배포 환경의 일관성과 자동화를 고민하면서, 일단 Docker 기반의 배포를 진행해야겠다고 생각했고, ECS와 Elastic Beanstalk 사이에서 고민을 하다, ECS로 선택했다.

Beanstalk의 한계

  • 초기 세팅은 쉽지만, 많이 추상화되어 있어 디버깅, 세부 설정에 제약
  • 멀티 컨테이너 구성이 가능하긴 하지만, 모든 컨테이너가 하나의 환경에 묶여 있어 각각의 서비스에 독립적인 배포/스케일링 정책 적용이 어려움

ECS

  • 서비스 단위 Task 정의 가능
  • ALB, CloudWatch, IAM 연동 유연
  • Task 단위 헬스체크 및 롤링 배포 가능
  • GitHub Actions와의 통합으로 배포 전과정을 자동화 가능

결론적으로, Beanstalk은 빠른 MVP 배포엔 적합하지만, 서비스 구조가 복잡해지고 운영 요소가 많아질수록 ECS가 훨씬 더 적합하다는 판단이었다.

ECS 개념 정리

  • ECS 클러스터: 하나 이상의 ECS 서비스를 실행할 수 있는 그룹
  • ECS 서비스: 특정 Task 정의를 기반으로 ECS에서 컨테이너를 실행하고 관리하는 단위
  • ECS Task: 실행될 컨테이너의 정의. Docker 이미지, 환경변수, 포트, CPU/메모리, 헬스체크 등 세부 실행 조건이 포함
  • ECR (Elastic Container Registry): Docker 이미지 저장소

클러스터 구조

production과 development 클러스터를 따로 구성했다. 각 클러스터는 다음과 같은 서비스들로 구성되어 있다.

  • 백엔드 (Ruby on Rails)
  • 프론트엔드 (Nextjs)
  • 스케줄러 (Cron)
  • 워커 (Sidekiq)

각 서비스마다 스케일링 정책, 배포 주기, 환경변수를 독립적으로 설정할 수 있다

GitHub Actions 워크플로우 단계

  1. yarn-build: Next.js 빌드
  2. container-build-and-push: Docker 이미지 빌드 및 ECR 푸시
  3. s3-upload: 정적 자산 S3 업로드
  4. ECS Task Definition 갱신 → 롤링 배포

처음에는 로컬에서 수동으로 Docker 이미지를 빌드하고, ECR에 푸시한 뒤 콘솔에서 ECS 서비스를 강제 업데이트했는데, 휴먼에러가 발생하거나 개발자마다 빌드환경이 달라서 이슈가 생기기도 했다.현재 Docker는 앱을 빌드하지는 않고, CI에서 미리 빌드된 Next.js standalone 결과물을 패키징해서 실행 가능한 이미지로 만드는 역할만 수행한다.

정적 자산 처리 & next.config.js 구성

Next.js 빌드 시 생성된 .next/static 및 public/next-assets를 S3에 업로드해 CloudFront와 연결했다. 정적 자산은 CloudFront를 통해 캐싱되고, SSR 페이지는 ECS에서 처리된다.

결론

배포 시간 단축: 수동 => 자동 배포
환경 일관성 확보: Docker 기반으로 어디서든 동일한 실행 환경
배포 신뢰도 향상: 커밋 태그 기반 이미지 관리, 롤백 쉬움
운영 효율 증가: 서비스별 배포/스케일링/모니터링 분리 운영

profile
즐겁게하자

0개의 댓글