Project/reciping

[reciping] ECS(EC2, Fargate)로 백엔드 애플리케이션 배포하기 + 기본 EC2배포까지 (2)

S_N_Y 2025. 11. 5. 05:43

※ 기존에 기록해둔 노션 글을 옮겨적은 것으로, 노션 템플릿에 맞게 적게된 글이라 해당 링크를 통해 더 가독성있게 보실 수 있습니다.

https://www.notion.so/ECS-EC2-Fargate-EC2-2690661ce6288002b649e41cce173d12

 

ECS(EC2, Fargate)로 백엔드 애플리케이션 배포하기 + 기본 EC2배포까지 | Notion

1. 각각에 넣을 보안그룹 생성하기( ALB, Gateway Server, ECS )

pleasant-sand-55a.notion.site


 

4. ALB 생성하기

4-1) alb 생성하기

로드 밸런서 생성 클릭
ALB 생성 클릭
1번 가용영역과 2번 가용영역을 번갈아가면서 배정해야 하므로 퍼블릭 서브넷을 아래와 같이 매핑합니다.
만들어둔 alb 보안그룹 설정하고, 80과 443을 설정하되, 80으로 들어갔을 때 443으로 바로 리디렉션하는 설정을 alb 만들고 할 것이니 일단 이렇게 만들어두세요.

[ 정책 이름 ] :

최신 TLS 1.2/1.3 지원하는 ELBSecurityPolicy-TLS13-1-2-Res-2021-06 추천 (보안성 가장 좋음)

⇒ 현업에서도 많이 쓰이는 모던 보안 정책입니다.

[ 클라이언트 인증서 처리 → 상호 인증(m TLS) ] :

  • 일반적인 웹사이트나 API 통신에서는 클라이언트 인증서는 필요 X
  • Mutual TLS는 금융권/내부망에서만 특수하게 사용합니다.

alb 생성 완료

 

4-2) 80으로 들어갔을 때 443으로 바로 리디렉션하는 설정하기

80 체크 후, 규칙 편집 클릭
기본값 체크 후, 규칙 편집 클릭
해당 화면과 같이 리디렉션으로 변경
사용자 지정 호스트, 경로, 쿼리를 사용하십시오… 체크 (= 리디렉션 대상 URL을 좀 더 정밀하게 제어할 수 있도록 도와주는 옵션)
작업(다음 수행) - 리디렉션 대상 이  HTTPS://#{host}:443/#{path}?#{query} 로 변경된 것을 볼 수 있음


 

5. Route 53에서 A 레코드 생성 (api.reciping.kr → ALB)

reciping.kr 클릭
레코드 생성 클릭
백엔드는 https://api.reciping.kr~ 로 갑니다. DNS→IP이니 A레코드로 설정하고, 별칭(alias) 켜주시고, 아까 만들어둔 alb도 넣어줍니다. 다 끝나면 레코드 생성 클릭
A 레코드 생성 완료


 

+) 게이트웨이 서버를 기본 EC2에 배포하기

⇒ 그 다음 절차는 gateway server를 EC2에 띄우는 것인데 그 과정은 다른 페이지에 따로 담았습니다.

기본 EC2 배포하기(Gateway Server)

 

기본 EC2 배포하기(Gateway Server) | Notion

1. EC2 키페어 만들기

pleasant-sand-55a.notion.site


 

6. ECS 클러스터 생성하기

클러스터 생성 클릭
기본 네임스페이스는 새 네임스페이스 생성 클릭해서 아래와 같이 만든 후, 그것으로 설정

+) 네임스페이스 설정하기

✅ [ 기본 네임스페이스 ] :

선택사항이긴 하지만, 클러스터 내부에서 서비스들을 논리적으로 그룹화하고 싶을 때 사용합니다.

향후 Prometheus, CloudWatch, Service Connect 등을 고려하면 reciping-ns 같은 네임스페이스를 만들어 두는 것도 나쁘지 않기에 만들어두겠습니다.

+) 네임스페이스의 다른 좋은 점 정리

기능 네임스페이스 사용 시 효과
서비스 격리 reciping-user-service 와 reciping-recommend-service 를 분리해서 관리 가능
Cloud Map 연동 서비스 디스커버리 이름이 recommend.reciping.local 이런 식으로 구조화 가능
Observability 향상 CloudWatch, X-Ray, Prometheus 연동 시 태깅 기준으로 분석 용이
Multi-tenant 운영 대비 나중에 하나의 클러스터에 여러 팀/서비스를 운영할 때 구조적으로 구분 가능

해당과 같이 적은 후, 네임스페이스 생성 클릭

[ 네임스페이스 이름 ] :

[프로젝트명].local 도메인은 VPC 내부 전용 용도로 AWS가 자동 인식해서 설정해줍니다.

⇒ 내부 DNS에서 user.reciping.local, event.reciping.local처럼 사용

[ 인스턴스 검색 ] :

옵션 설명 권장 여부
API 호출 서비스 디스커버리 이름으로 ECS에서 호출할 때 Cloud Map을 API 통해 조회함 기본 + 요금 없음
VPC에서 API 호출 및 DNS 쿼리 DNS 기반 + API 호출을 모두 지원함 (비용 발생) 외부 서비스가 DNS로 호출해야 할 때만 사용
퍼블릭 DNS 쿼리 포함 퍼블릭 DNS로도 외부에서 접근 가능 (비용 발생) 지금 상황에서는 불필요

[ Auto Scaling 그룹(ASG) ]

: 클러스터에 EC2 Capacity Provider로 사용할 Auto Scaling Group을 설정

⬇️ 여기서 EC2 Capacity Provider란?

  • EC2 인스턴스를 ECS에서 자동으로 사용할 수 있게 해주는 중개자 매개체
  • 즉, Auto Scaling Group(ASG) 기반의 EC2 인스턴스를 ECS가 자동으로 관리하게끔 연결해주는 "다리”
  • ECS가 태스크를 실행해야 할 때 ASG의 용량을 보고 자동으로 EC2 인스턴스를 띄움
  • 예) 태스크 수 증가 → Capacity 부족하면 → ASG 스케일 아웃 유도
# 구성 요소 흐름도
Auto Scaling Group (EC2 인스턴스 묶음)
     ↓ 연결
EC2 Capacity Provider
     ↓ 연결
ECS 클러스터 내 서비스

 

결론 : 일단 새 ASG 생성으로 자동생성

→ 이후 세밀 조절을 위해 EC2 > Auto Scaling > 생성된 그룹으로 이동해서 정책을 추가해야 합니다.

→ 예) CPU 60% 이상이면 EC2 한 대 더 생성 등

→ fargate는 ECS 서비스 > Auto Scaling 설정에서 task수를 자동 증가 정책 필요

→ 예) 예: CPU 평균 60% 이상이면 Task 수를 2 → 3으로 자동 증가

 

[ 컨테이너 인스턴스 AMI ]

: Amazon Linux 2 그대로 두면 ECS Agent 포함된 최적화된 AMI로 자동 선택됨

[ EC2 인스턴스 역할 ]

: 기본값 ecsInstanceRole 선택 ⇒ ECS 에이전트가 동작하고 CloudWatch, ECR 등에 접근할 권한을 가짐

[ 시작 인스턴스 수 ]

: 기본값 최소가 0으로 되어있는데 그대로 두면 ‘최초 배포 안 해도 된다’ 는 표시이니 최소 1 / 최대 2로 초기 세팅

[ SSH 키 페어 ]

: EC2 인스턴스에 접속해서 디버깅하고 싶다면 꼭 필요 ⇒ reciping-prod-ec2-key 선택

[ 루트 EBS 볼륨 크기 ]

  • EC2 인스턴스의 기본 디스크 공간(운영체제, 로그, 컨테이너 캐시 등 저장)
  • ECS에서 컨테이너를 EC2에서 실행할 경우, 도커 이미지/로그도 이 루트 볼륨에 저장됨

⇒ 대부분의 AMI에서 기본값은 30 GiB인데, 지금 MSA 서비스 여러 개 구성 예정이니 넉넉하게 50GiB로 세팅

 

[ 퍼블릭 IP 자동 할당 ]

: 프라이빗 서브넷에서 동작할 것이므로 끄기 선택

태그 입력 후, 생성 클릭


 

7. ECR 생성하기

리포지토리 생성 클릭
각 서비스별로 ecr을 만들 것임(reciping-user-service, reciping-recipe-service..) 입력 후, 생성 클릭

 

[ 이미지 태그 변경 가능성 ]

옵션 설명 구분 기준
Mutable (기본값) 같은 태그로 이미지를 덮어쓰기 가능
(예: latest)
개발/테스트 환경에서 많이 씀
Immutable 같은 태그로는 한 번만 푸시 가능 (덮어쓰기 불가) 실운영 배포 환경에서 선호 (보안 및 추적성 확보)

 

[ 암호화 설정 ]

옵션 설명 구분 기준
AES-256 (기본값) AWS가 자동으로 암호화 관리 대부분 이걸 씀
AWS KMS 직접 생성한 KMS 키로 암호화 보안이 매우 민감한 조직에서 사용 (예: 금융, 의료)

 

[ 이미지 스캔 설정 ] - 푸쉬된 이미지 취약점 검사해주는 옵션

⇒ 저희는 현재 리포지토리 단위에서 구성되므로, 신경 쓰지 말고 그냥 꺼두면 됩니다.

각 서비스별로 ECR 생성 완료


 

8. Dockerfile 작성하기

멀티 스테이지 빌드를 이용하겠습니다. & 경량 베이스 이미지도 사용
# 1단계 : Build Stage
# 빠른 빌드에 최적화된 이미지인 openjdk:17-jdk-slim 사용, Builder 단계에서만 사용
FROM openjdk:17-jdk-slim AS builder

WORKDIR /app

# gradle 설정 복사
COPY gradlew build.gradle settings.gradle ./
COPY gradle ./gradle

# 소스 복사
COPY src ./src

# 실행 권한 부여 + 빌드
# bootJar만 빌드하여 build 전체 안 쓰고 필요한 아티팩트만 생성해서 빠르게 함
# --no-daemon 사용하여 도커 환경에서 데몬 프로세스 오류 방지
RUN chmod +x ./gradlew && ./gradlew bootJar --no-daemon

# 2단계: Runtime Stage (이 또한 경량 이미지)
# 런타임은 경량 + AWS 친화적 (ECS, EC2 AMI와 일관성 유지)
FROM amazoncorretto:17-alpine
WORKDIR /app

# 빌드된 JAR 복사
COPY --from=builder /app/build/libs/*.jar app.jar

# 도커 컨테이너 내부에서 사용하는 헬스체크용 포트 노출
EXPOSE 8080

# Spring Boot 실행
ENTRYPOINT ["java", "-jar", "app.jar"]

 

+) MacOS에 AWS CLI 설치

curl "https://awscli.amazonaws.com/AWSCLIV2.pkg" -o "AWSCLIV2.pkg"
sudo installer -pkg AWSCLIV2.pkg -target /

# 설치 끝나면 아래로 확인
# 예시 출력: aws-cli/2.x.x 이런 식이면 성공
aws --version

# ECR에 로그인하기 위해 인증 정보 입력해야 함
aws configure

# 그 뒤에는 아랫 내용 입력하면 됩니다.

 

+) IAM에서 내 계정(내 사용자) 엑세스 키 만들기

IAM → 사용자 → 본인 계정 → 액세스키 생성 클릭 후, 이 화면이 나오는데 CLI 선택후 다음 클릭
이 페이지 벗어나면 더이상 볼 수 없으니, 저거 복사해서 메모장에 붙여넣기 하거나 .csv파일로 보관해둬야 합니다!

 

항목 설명 입력값
AWS Access Key ID IAM 사용자로 받은 키 AKIA...
AWS Secret Access Key IAM 사용자 시크릿 xxyyzz...
Default region name 서울 리전 ap-northeast-2
Default output format 그대로 엔터 쳐도 됨 json 혹은 그냥 엔터

🚨 docker desktop 실행 중인 상태로 아랫 명령어 쭉 진행합니다.

# 일단 user-service 기준으로 문서 남겨둡니다.
# 892117097999.dkr.ecr.ap-northeast-2.amazonaws.com => ecr에서 유저 서비스 url 그대로 가져오면 됩니다.
aws ecr get-login-password --region ap-northeast-2 | \
docker login --username AWS --password-stdin 892117097999.dkr.ecr.ap-northeast-2.amazonaws.com

저희는 이미지 버전을 mutable하게 가져가지 않고(lastest ❌) Immutable하게 v1.0.0 형식으로 버전관리할 예정입니다.

<주의할 점> : 과정 중에 이후의 하나의 과정이라도 실패하면 그냥 해당 ECR 삭제하고 다시 만드는 것 추천

 

docker build -t reciping-user-service:v1.0.0 .

# 빌드한 도커 이미지'reciping-user-service:v1.0.0'를 태깅 후, ECR에 푸쉬
docker tag reciping-user-service:v1.0.0 \
892117097999.dkr.ecr.ap-northeast-2.amazonaws.com/reciping-user-service:v1.0.0

docker push 892117097999.dkr.ecr.ap-northeast-2.amazonaws.com/reciping-user-service:v1.0.0

성공한 모습

+) 나머지 서비스들도 다 똑같은 과정으로 진행하세요.

# 1. 디렉토리 이동 (예: reciping-recipe-service-BE)
cd ~/Desktop/dev_sini/reciping-recipe-service-BE

# 2. jar 빌드
./gradlew clean bootJar --no-daemon

# 3. 도커 이미지 빌드 (캐시 없이)
docker build --no-cache --platform linux/amd64 -t reciping-recipe-service:v1.0.0 .

# 4. 태깅
docker tag reciping-recipe-service:v1.0.0 \
892117097999.dkr.ecr.ap-northeast-2.amazonaws.com/reciping-recipe-service:v1.0.0

# 5. 푸시
docker push 892117097999.dkr.ecr.ap-northeast-2.amazonaws.com/reciping-recipe-service:v1.0.0

 

8. 태스크 정의 생성하기

8-1) fargate 태스크 정의 예시 (reciping-user-service)

새 태스크 정의 생성 클릭

[ 운영 체제/아키텍처 ]

: Linux/X86_64 대부분 기본 설정.(ARM 기반 컨테이너가 맞을 때는 다른거 사용)

[ 태스크 크기 ]

: fargate는 EC2와 달리 조합을 미리 정해둡니다.

현업에서는 웹 서비스: 0.5 vCPU + 1GB ~ 1 vCPU + 3GB, 검색, 머신러닝 등 고성능: 2~4 vCPU + 8GB~

[ 태스크 역할 ]

: 컨테이너 내 애플리케이션이 AWS 리소스에 접근할 수 있도록 부여된 IAM Role

현업에서는 ecsTaskExecutionRole 또는 서비스 전용 Role 따로 생성해서 사용한다고 합니다.

[ 프라이빗 레지스트리 ]

: 프라이빗 레지스트리 인증을 켜면, Docker 이미지의 사용자명/비밀번호가 필요할 수 있습니다.

ECR이 아닌 타사 프라이빗 레지스트리(ex. DockerHub Pro, GitHub Container Registry 등)일 경우는 Secrets Manager를 통해 자격 증명을 받아야 하는데 ECR 사용하고 있으니 켜놓긴 하되, Secrets Manager 설정은 빈칸으로 두세요.

[ 포트 매핑 ]

: ECS에서 유저 서비스는 포트 8080로 스프링부트가 구동되고 있습니다.

[ 읽기 전용 루트 파일 시스템 ]

: 컨테이너 내의 루트 파일 시스템을 읽기 전용으로 설정할지 선택 ⇒ 스프링 부트 앱이면 읽기 전용은 체크 X

[ 리소스 할당 제한 ]

목적 CPU Memory
소규모 API 서비스 (트래픽 적음) 0.5 vCPU 1~2 GiB
일반 SpringBoot 서비스 (유저 로그인/조회 등) 1 vCPU 2~3 GiB
대용량 또는 병렬 처리 서비스 2 vCPU 4 GiB 이상

[ 환경 변수 ]

배포 자동화 환경(Jenkins)에서 환경 변수를 주입할 거면 ECS 환경변수 설정은 생략해도 됩니다.


 

8-2) EC2 태스크 정의 예시 (reciping-search-service)

[ 네트워크 모드 ]

: awsvpc : ECS Task가 ENI(IP)를 직접 받아서 통신함 → 보안 그룹, IAM 관리도 쉬움

[ 작업 배치 ]

추가 X

[ 결함 주입 ]

: AWS Fault Injection Simulator (FIS)와 연동하여 장애 유도 테스트 수행 옵션이며, 보통 장애 대응 훈련, 복원력 테스트 시 QA 환경에서 사용 가능하며 현재 프로덕션 환경에선 사용 안 하므로 체크 X


 

9. 서비스 생성하기

9-1) fargate일 경우(예 : user-service)

서비스 생성 클릭

[ 태스크 정의 개정 ]

: 최신 개정 선택(예: :1, :2 등) → 수동 선택 가능, 일반적으로 최신 개정 선택 (기본값 유지 OK)

[ 서비스 이름 ]

: 자동으로 reciping-user-task-service-<랜덤>처럼 생성될텐데 이건 직접 reciping-user-service로 수정해야 팀 기준으로 서비스명 일관성이 유지가 되니 꼭 바꾸기

[ 컴퓨팅 구성(고급) ]

: 어떤 인스턴스에 태스크를 띄울지 ECS가 알아서 선택하는 방식 → 용량 공급자 전략 선택

( 시작 유형은 예전 방식이며, 간단하지만 오토 스케일링과 스팟 연동 등 제한)

[ 용량 공급자 전략 ]

  • fargate의 경우, 사용자 지정 사용(고급) 선택 ( EC2의 경우, 클러스터 기본값 사용 선택)

[ 용량 공급자 ]

  • 메인 컨테이너를 정규 fargate에서 실행한다는 의미로 FARGATE 선택
  • 가중치는 1 기본값 유지 (FARGATE_SPOT을 추가하려면 전략적으로 FARGATE: 1, FARGATE_SPOT: 1 같이 구성해서 비용 최적화도 가능)
  • ⇒ 하지만 user-service처럼 항상 켜져야 하는 서비스는 FARGATE 단독이 안정적

[ 플랫폼 버전 ]

: LATEST 선택 (보안 정책상 고정 버전 사용하는 금융/대겹은 1.4.0 사용하기도 한다고 합니다.)

[ 서비스 유형 ]

: 원하는 태스크 수만큼 ECS가 유지하는 구조 (MSA 대부분이 여기에 해당) → 복제본 선택

[ 원하는 태스크 ]

: 초기에는 1개로 시작, 필요 시 오토 스케일링 설정하면 되는데 저희는 나중에 설정할 것입니다.

(실무에서는 초기 서비스에서는 1~2개 선택하고 트래픽 증가에 따라 조정한다고 합니다.)

[ 가용 영역 리밸런싱 ]

: Fargate가 AZ 불균형 발생 시 자동으로 리밸런싱 해줌 (현업에서 반드시 켜놓음)

[ 상태 검사 유예 기간 ]

: 스프링 부트 서버의 경우, 기동이 20~30초 걸린다 생각하면 30으로 설정해두기

[ 배포 유형 ]

  • 롤링 업데이트 : 한 번에 일부 태스크를 교체하며 무중단 배포 → 규모 작을 때 적합합니다. 이거 선택
  • 블루/그린 : 테스트 후 승인 방식 배포 → 서버가 많이 떠있으면 가능(지금은 필요X / 나중에 고려)

[ 최소 실행 작업 비율 ]

: 업데이트 도중 기존 태스크는 100% 유지, 새 태스크 배포 후 교체

[ 최대 실행 작업 비율 ]

: 최대 몇 개까지 동시 실행 가능한지 (기존 + 신규) → 기본값 200% 적용(오토 스케일링 대응 포함됨)

[ 배포 실패 감지 ]

옵션 설명  
Amazon ECS 배포 회로 차단기 사용 배포 중 태스크가 unhealthy 상태에 도달하면 즉시 배포 중단 항상 ON(기본 보호 메커니즘)
실패 시 롤백 새 배포가 실패하면 이전 정상 상태로 자동 롤백 ON (중단 없는 운영을 위해)
CloudWatch 경보 사용 CloudWatch Alarm이 발생하면 배포 중단 알람 연동된 모니터링 세팅이 있는 경우만 사용 (없으면 꺼두는 게 맞습니다)

fargate가 프라이빗 서브넷에서 실행되고, ALB가 라우팅을 담당하니까 퍼블릭 IP도 꺼짐 선택
여기서 태그만 사용하겠습니다.(서비스 자동 크기 조정(오토 스케일링)같은 경우, 나중에 따로 설정할 것) alb → ecs 구조면 로드 밸런싱 옵션 사용해야 함