AWS 인프라 - ECS Fargate

2026. 1. 2. 17:14·TIL

EC2를 넘어 ECS Fargate로 서버리스 컨테이너와 무중단 배포 아키텍처 구축

저번 글에선 AWS의 가장 기본적인 컴퓨팅 리소스인 EC2 인스턴스 하나에 Docker를 띄워 서비스를 배포하는 과정을 다뤘다.

하지만 서비스를 운영하다 보니 몇 가지 한계에 부딪혔다.

  1. 배포 시 다운타임 발생: 새로운 버전을 배포할 때마다 잠시 서비스를 멈춤
  2. 서버 관리의 부담: EC2 인스턴스의 OS 보안 패치, 스케일링 등을 직접 신경 써야함.
  3. 수동 배포의 번거로움: 코드를 짜는 시간보다 배포 스크립트를 돌리고 확인하는 데 쓰는 에너지가 큼.

이번 글에서는 이러한 문제를 해결하기 위해 ECS Fargate를 도입하여 서버리스(Serverless) 아키텍처로 전환하고,
GitHub Actions를 통해 무중단 자동 배포 파이프라인을 구축한 과정을 공유한다.


1. 전체 아키텍처 개요 (As-Is vs To-Be)

가장 큰 변화는 '관리형 서비스'의 적극적인 도입이다.
기존에는 Public Subnet의 EC2 하나가 모든 짐을 짊어졌다면, 이번에는 역할별로 리소스를 분리하고 가용성을 높였다.

🛠 전체 아키텍처 다이어그램

주요 변경 포인트:

  • Compute: EC2 → ECS Fargate (서버 관리 불필요)
  • Network: Public Subnet 단일 배포 → Private Subnet 배포 (보안 강화)
  • Load Balancer: 직접 접속 → ALB (Application Load Balancer)
  • Deploy: 수동 스크립트 → GitHub Actions + ECR

2. 왜 ECS Fargate인가? (Server vs Serverless)

컨테이너 오케스트레이션 도구를 선택할 때, 사실상 표준인 Kubernetes(EKS)와 AWS Native인 ECS 사이에서 고민했다.
결론적으로 ECS Fargate를 선택했는데, 이유는 다음과 같다.

  1. 낮은 러닝 커브와 운영 비용: 쿠버네티스는 강력하지만 클러스터 자체를 관리하고 유지보수하는 데 상당한 학습 비용과 리소스가 든다. 반면 ECS는 AWS 서비스들과 가장 직관적으로 통합되어 있어 설정이 간편하다.
  2. 비즈니스 로직 집중: 현재 단계에서는 인프라 자체를 복잡하게 관리하는 것보다, 안정적인 환경 위에서 애플리케이션 기능을 빠르게 배포하는 것이 우선이라 판단했다.

Fargate Launch Type

ECS에는 두 가지 모드가 있다.

  • EC2 모드: 내가 띄운 EC2 위에서 컨테이너를 관리 (인스턴스 관리 필요)
  • Fargate 모드: 인스턴스 없이 컨테이너 실행에 필요한 CPU/Memory만 할당받아 사용 (Serverless)

저는 인프라 관리보다 애플리케이션 로직에 집중하고 싶었기에, OS 관리가 전혀 필요 없는 Fargate를 선택했다.


3. 배포의 시작: Dockerfile과 ECR

Fargate는 '이미지' 기반으로 동작하기에 애플리케이션을 불변(Immutable)의 이미지로 만드는 것이 첫걸음이다.

3.1 Dockerfile 최적화 (Multi-stage Build)

빌드 환경과 실행 환경을 분리하여 이미지 크기를 줄였다.

Dockerfile
 
# Build stage
FROM gradle:8.5-jdk21 AS build
WORKDIR /app
COPY build.gradle settings.gradle gradlew ./
COPY gradle gradle
COPY src src
RUN gradle bootJar --no-daemon -x test

# Runtime stage
FROM eclipse-temurin:21-jre
WORKDIR /app
# 보안을 위해 root가 아닌 별도 유저 생성
RUN groupadd -r spring && useradd -r -g spring spring
USER spring:spring
COPY --from=build /app/build/libs/*.jar app.jar
EXPOSE 8080
ENV JAVA_OPTS="-Xms256m -Xmx512m -XX:+UseContainerSupport"
ENTRYPOINT ["sh", "-c", "java $JAVA_OPTS -Dspring.profiles.active=${SPRING_PROFILE:-default} -jar app.jar"]

3.2 ECR (Elastic Container Registry)

빌드된 이미지를 저장할 저장소이다. Docker Hub의 Private Repository와 같지만, AWS IAM과 연동되어 ECS가 이미지를 가져올 때 별도의 인증 번거로움이 줄어든다.


4. 핵심 구성 요소: Task Definition과 Service

ECS를 이해하려면 Task Definition(작업 정의)과 Service의 개념을 잡아야 한다.

  • Task Definition: "어떤 컨테이너를 띄울 것인가?" (청사진)
    • 사용할 이미지 URI
    • CPU/Memory 할당량 (Fargate는 이 설정에 따라 과금이 결정)
    • 환경 변수
  • Service: "어떻게 실행하고 유지할 것인가?"
    • Task의 개수 (Desired Count)
    • 로드 밸런서 연결
    • 배포 전략 (Rolling Update 등)

이번 프로젝트에서는 oddventure-task라는 패밀리 이름으로 정의하고, DB 접속 정보나 API 키 같은 민감 정보는 코드에 하드코딩하지 않고 실행 시점에 SSM에서 안전하게 주입받도록 구성했다.


5. 네트워크 구성과 보안 (Private Subnet & NAT Gateway)

아키텍처 그림을 보면 ECS Task들이 Private Subnet에 위치해 있다.

  • 보안: 외부에서 직접 접근이 불가능하여 데이터베이스나 애플리케이션이 공격에 노출될 위험이 줄어든다.
  • 트래픽 흐름:
    • Inbound: 사용자는 인터넷 → ALB (Public Subnet) → ECS Task (Private Subnet) 경로로 접속한다.
    • Outbound: ECS Task가 외부 API(Steam, Grid, Groq 등)를 호출하거나 ECR에서 이미지를 당겨올 때는 NAT Gateway를 통해 나간다.

Private Subnet에 있는 DB나 서버에 접근해서 디버깅해야 할 때는 다이어그램 좌측 하단에 있는 Bastion Host를 통해 터널링하여 접근하도록 구성했다.


6. 무중단 배포의 핵심: ALB와 Rolling Update

기존 EC2 배포의 가장 큰 불만이었던 '배포 중 서비스 중단'은 ECS의 Rolling Update 전략으로 해결되었다.

  1. 새로운 버전의 Task가 실행
  2. ALB가 새 Task의 Health Check( /actuator/health )를 수행
  3. 상태가 'Healthy'가 되면 트래픽을 새 Task로 흘려보냄
  4. 기존 버전의 Task를 종료(Draining)함

이 과정 덕분에 사용자는 배포가 일어나는지조차 모르게 서비스를 계속 이용할 수 있다.


7. 자동화의 완성: GitHub Actions

이제 코드를 main 브랜치에 푸시하기만 하면 이 모든 과정이 자동으로 일어난다.

워크플로우 요약:

  1. Checkout & Login: 코드 체크아웃 및 AWS ECR 로그인
  2. Build & Push: Docker 이미지를 빌드하고 ECR에 푸시 (태그는 Git SHA 활용)
  3. Deploy: 최신 Task Definition을 다운받아 새 이미지로 교체 후 ECS 서비스 업데이트
# .github/workflows/ecs-deploy.yml
    - name: Fill in the new image ID in the Amazon ECS task definition
      id: task-def
      uses: aws-actions/amazon-ecs-render-task-definition@v1
      with:
        task-definition: task-definition.json
        container-name: oddventure-container
        image: ${{ steps.login-ecr.outputs.registry }}/oddventure-repo:${{ github.sha }}

    - name: Deploy Amazon ECS task definition
      uses: aws-actions/amazon-ecs-deploy-task-definition@v1
      with:
        task-definition: ${{ steps.task-def.outputs.task-definition }}
        service: oddventure-service
        cluster: oddventure-cluster
        wait-for-service-stability: true

8. 마치며: EC2에서 Fargate로 넘어오며 느낀 점

EC2에서 직접 Docker를 띄우던 방식에서 ECS Fargate로 넘어오면서 느낀 장단점은 명확했다.

  • 장점:
    • 운영 스트레스 해소: 서버가 죽었나? 디스크가 찼나? 걱정할 필요가 없어짐
    • 안정성: Multi-AZ와 자동화된 로드 밸런싱으로 서비스가 훨씬 견고해짐
    • 배포 속도: 클릭 한 번(Merge)으로 배포가 완료되니 개발 생산성이 비약적으로 상승
  • 단점(고려사항):
    • 초기 설정 난이도: VPC, Subnet, IAM Role, Security Group 등 네트워크와 권한에 대한 이해가 필수적이라 초기 러닝 커브가 있었다.
    • 비용: 소규모 트래픽에서는 t2.micro 같은 EC2 인스턴스보다 Fargate 비용이 조금 더 나올 수 있다.

이번 마이그레이션을 통해 인프라에 대한 걱정을 덜고, Oddventure의 핵심 비즈니스 로직 개발에 더 집중할 수 있게 되었다.

'TIL' 카테고리의 다른 글

AWS 인프라 기초 설계  (1) 2025.12.08
Spring Boot 성능 최적화 10  (0) 2025.11.12
Spring Boot 성능 최적화 9  (0) 2025.11.11
Spring Boot 성능 최적화 8  (0) 2025.11.10
11/7  (0) 2025.11.07
'TIL' 카테고리의 다른 글
  • AWS 인프라 기초 설계
  • Spring Boot 성능 최적화 10
  • Spring Boot 성능 최적화 9
  • Spring Boot 성능 최적화 8
twodc
twodc
매일의 배움과 고민, 그리고 작은 성장을 담은 개발자의 성장일지입니다.
  • twodc
    The Answer is 42
    twodc
  • 전체
    오늘
    어제
    • 분류 전체보기 (122)
      • TIL (122)
  • 블로그 메뉴

    • 홈
    • 태그
    • 방명록
  • 링크

    • Github
  • 인기 글

  • 태그

  • 최근 댓글

  • 최근 글

  • hELLO· Designed By정상우.v4.10.4
twodc
AWS 인프라 - ECS Fargate
상단으로

티스토리툴바