AWS ECS + RDS - Nginx + FastAPI + MySQL (Bridge Mode) 템플릿
🔧 템플릿 사용법:
<PREFIX>,<REGION>,<ACCOUNT_ID>등 플레이스홀더를 실제 값으로 교체하세요.
플레이스홀더 목록
| 플레이스홀더 | 설명 | 예시 |
|---|---|---|
<PREFIX> | 리소스 접두사 | team1-project, pista |
<REGION> | AWS 리전 | ap-northeast-2, ap-southeast-1 |
<ACCOUNT_ID> | AWS 계정 ID | 123456789012 |
<AZ1> | 가용 영역 1 | ap-northeast-2a |
<AZ2> | 가용 영역 2 | ap-northeast-2b |
<VPC_CIDR> | VPC CIDR | 10.0.0.0/16 |
<DB_PASSWORD> | RDS 비밀번호 | (Secrets Manager 권장) |
<DB_NAME> | 초기 DB 이름 | mydb |
아키텍처 개요
아키텍처 요약
| 구성 요소 | 설명 |
|---|---|
| 네트워크 | Public 서브넷(ALB) + Private 서브넷(ECS, RDS) 2-tier |
| 컨테이너 | nginx(리버스 프록시) + fastapi(백엔드) bridge 모드 |
| 데이터베이스 | RDS MySQL (Multi-AZ 선택) |
| 통신 | ALB → nginx → fastapi (links) → RDS |
bridge 모드 + links + RDS: nginx → fastapi → MySQL
선행 작업
목차
구성 프로세스
| 단계 | 작업 | 설명 | 콘솔 경로 |
|---|---|---|---|
| 1 | VPC/Subnet 생성 | VPC, 서브넷(Public/Private), IGW, NAT 구성 | VPC > VPC 생성 |
| 2 | 보안 그룹 생성 | ALB용, ECS용, RDS용 보안 그룹 생성 | VPC > 보안 그룹 |
| 3 | RDS 서브넷 그룹 | Private 서브넷 2개로 DB 서브넷 그룹 생성 | RDS > 서브넷 그룹 |
| 4 | RDS 인스턴스 생성 | MySQL 배포 | RDS > 데이터베이스 생성 |
| 5 | ECR 리포지토리 생성 | <PREFIX>-web, <PREFIX>-fastapi-mysql | ECR > 리포지토리 생성 |
| 6 | Docker 이미지 빌드 & 푸시 | nginx, fastapi-mysql 이미지 빌드 | EC2 SSH 접속 |
| 7 | ECS 클러스터 생성 | EC2 기반 클러스터 생성 | ECS > 클러스터 생성 |
| 8 | 태스크 정의 생성 | <PREFIX>-web + <PREFIX>-fastapi-mysql | ECS > 태스크 정의 |
| 9 | ⭐ 대상 그룹 생성 | <PREFIX>-tg-nginx 1개만 생성 | EC2 > 대상 그룹 |
| 10 | ⭐ ALB 생성 | 대상 그룹 연결 | EC2 > 로드 밸런서 |
| 11 | ECS 서비스 생성 | 단일 서비스 생성 | ECS > 서비스 생성 |
| 12 | 점검 | Health Check, DB 연결 확인 | - |
⚠️ bridge 모드: ALB와 대상 그룹을 서비스 생성 전에 미리 생성 필요
보안 그룹
리소스(<PREFIX>-sg-alb)
| 변수 | 값 | 소스 |
|---|---|---|
| 보안 그룹 이름 | <PREFIX>-sg-alb | - |
| 인바운드 규칙 | TCP 80 | 0.0.0.0/0 |
| 인바운드 규칙 | TCP 443 | 0.0.0.0/0 |
| 아웃바운드 규칙 | 모든 트래픽 | 0.0.0.0/0 |
리소스(<PREFIX>-sg-ecs)
| 변수 | 값 | 소스 |
|---|---|---|
| 보안 그룹 이름 | <PREFIX>-sg-ecs | - |
| 인바운드 규칙 | TCP 32768-65535 | <PREFIX>-sg-alb |
| 인바운드 규칙 | TCP 22 | 관리자 IP |
| 아웃바운드 규칙 | 모든 트래픽 | 0.0.0.0/0 |
| 설명 | 동적 포트 매핑 (bridge 모드) | - |
리소스(<PREFIX>-sg-rds) ⭐
| 변수 | 값 | 소스 |
|---|---|---|
| 보안 그룹 이름 | <PREFIX>-sg-rds | - |
| 인바운드 규칙 | TCP 3306 | <PREFIX>-sg-ecs |
| 아웃바운드 규칙 | 모든 트래픽 | 0.0.0.0/0 |
| 설명 | ECS에서만 MySQL 접근 허용 | - |
💡 RDS 보안 그룹은 ECS 보안 그룹에서만 접근 허용
RDS (MySQL)
DB 서브넷 그룹
RDS > 서브넷 그룹 > DB 서브넷 그룹 생성
| 변수 | 값 |
|---|---|
| 이름 | <PREFIX>-db-subnet-group |
| 설명 | DB Subnet Group |
| VPC | <PREFIX>-vpc |
| 가용 영역 | <AZ1>, <AZ2> |
| 서브넷 | <PREFIX>-subnet-private1, <PREFIX>-subnet-private2 |
RDS 인스턴스 생성
| 구분 | 변수 | 값 |
|---|---|---|
| 엔진 옵션 | 엔진 유형 | MySQL |
| 엔진 버전 | MySQL 8.0.x | |
| 템플릿 | 템플릿 | 개발/테스트 또는 프로덕션 |
| 가용성 | 배포 옵션 | Multi-AZ (선택) |
| 설정 | DB 인스턴스 식별자 | <PREFIX>-rds-mysql |
| 마스터 사용자 이름 | admin | |
| 마스터 암호 | <DB_PASSWORD> | |
| 인스턴스 | DB 인스턴스 클래스 | db.t3.micro / db.t3.small |
| 스토리지 | 스토리지 유형 | gp3 |
| 할당된 스토리지 | 20 GiB | |
| 연결 | VPC | <PREFIX>-vpc |
| DB 서브넷 그룹 | <PREFIX>-db-subnet-group | |
| 퍼블릭 액세스 | ❌ (아니요) | |
| VPC 보안 그룹 | <PREFIX>-sg-rds | |
| 추가 구성 | 초기 데이터베이스 이름 | <DB_NAME> |
RDS 엔드포인트
<PREFIX>-rds-mysql.xxxxxxxxxx.<REGION>.rds.amazonaws.com
ECR 이미지 준비
| 이미지 | 소스 경로 | 포트 |
|---|---|---|
| nginx | ECR/images/fastapi-mysql/nginx/ | 80 |
| fastapi-mysql | ECR/images/fastapi-mysql/ | 3000 |
빌드 및 푸시
# ECR 로그인
aws ecr get-login-password --region <REGION> | \
docker login --username AWS --password-stdin <ACCOUNT_ID>.dkr.ecr.<REGION>.amazonaws.com
# fastapi-mysql 빌드 및 푸시
cd ECR/images/fastapi-mysql
docker build -t <PREFIX>-fastapi-mysql .
docker tag <PREFIX>-fastapi-mysql:latest <ACCOUNT_ID>.dkr.ecr.<REGION>.amazonaws.com/<PREFIX>-fastapi-mysql:latest
docker push <ACCOUNT_ID>.dkr.ecr.<REGION>.amazonaws.com/<PREFIX>-fastapi-mysql:latest
# nginx 빌드 및 푸시
cd nginx
docker build -t <PREFIX>-web .
docker tag <PREFIX>-web:latest <ACCOUNT_ID>.dkr.ecr.<REGION>.amazonaws.com/<PREFIX>-web:latest
docker push <ACCOUNT_ID>.dkr.ecr.<REGION>.amazonaws.com/<PREFIX>-web:latest
클러스터 생성
리소스(<PREFIX>-cluster)
| 구분 | 항목 | 값 |
|---|---|---|
| 클러스터 이름 | 클러스터 이름 | <PREFIX>-cluster |
| 인프라 | 컴퓨팅 용량 확보 방법 | EC2 자체 관리형 ✅ |
| Auto Scaling 그룹 | 새 ASG 생성 | |
| 프로비저닝 모델 | 온디맨드 | |
| AMI | Amazon Linux 2023 | |
| EC2 인스턴스 유형 | t3.medium | |
| EC2 인스턴스 역할 | ecsInstanceRole | |
| 원하는 용량 (최소/최대) | 2 / 3 | |
| 네트워크 | VPC | <PREFIX>-vpc |
| 서브넷 | <PREFIX>-subnet-private1, <PREFIX>-subnet-private2 | |
| 보안 그룹 | <PREFIX>-sg-ecs | |
| 퍼블릭 IP 자동 지정 | ❌ |
태스크 정의
인프라 요구 사항
| 구분 | 항목 | 값 |
|---|---|---|
| 태스크 정의 구성 | 패밀리 | <PREFIX>-task |
| 인프라 요구 사항 | 시작 유형 | Amazon EC2 인스턴스 ✅ |
| 네트워크 모드 | bridge | |
| CPU | 0.75 vCPU | |
| 메모리 | 1.5 GB | |
| 태스크 실행 역할 | ecsTaskExecutionRole |
컨테이너 1: <PREFIX>-web (nginx)
| 구분 | 항목 | 값 |
|---|---|---|
| 컨테이너 | 이름 | <PREFIX>-web |
| 이미지 URI | <ACCOUNT_ID>.dkr.ecr.<REGION>.amazonaws.com/<PREFIX>-web:latest | |
| 포트 매핑 | 호스트 포트 | 0 (동적) |
| 컨테이너 포트 | 80 | |
| 필수 컨테이너 | ✅ | |
| 네트워크 설정 | 링크 | <PREFIX>-fastapi-mysql:fastapi |
컨테이너 2: <PREFIX>-fastapi-mysql ⭐
| 구분 | 항목 | 값 |
|---|---|---|
| 컨테이너 | 이름 | <PREFIX>-fastapi-mysql |
| 이미지 URI | <ACCOUNT_ID>.dkr.ecr.<REGION>.amazonaws.com/<PREFIX>-fastapi-mysql:latest | |
| 포트 매핑 | 호스트 포트 | 0 (동적) |
| 컨테이너 포트 | 3000 | |
| 필수 컨테이너 | ✅ | |
| 환경 변수 | DB_HOST | <PREFIX>-rds-mysql.xxx.<REGION>.rds.amazonaws.com |
| DB_PORT | 3306 | |
| DB_USER | admin | |
| DB_PASSWORD | <DB_PASSWORD> | |
| DB_NAME | <DB_NAME> |
⚠️ 보안 권장:
DB_PASSWORD는 Secrets Manager 사용 권장
대상 그룹
💡 links 방식: nginx가 fastapi로 프록시하므로 대상 그룹은 nginx 1개만 필요
리소스(<PREFIX>-tg-nginx)
| 변수 | 값 |
|---|---|
| 설정-대상 유형 | 인스턴스 |
| 설정-대상 그룹 이름 | <PREFIX>-tg-nginx |
| 설정-프로토콜 | HTTP |
| 설정-포트 | 80 |
| 설정-VPC | <PREFIX>-vpc |
| 상태 검사-상태 검사 경로 | / |
로드 밸런서
리소스(<PREFIX>-alb)
| 변수 | 값 |
|---|---|
| 기본 구성-로드 밸런서 이름 | <PREFIX>-alb |
| 기본 구성-체계 | 인터넷 경계 |
| 네트워크 매핑-VPC | <PREFIX>-vpc |
| az1 | <PREFIX>-subnet-public1 |
| az2 | <PREFIX>-subnet-public2 |
| 보안 그룹 | <PREFIX>-sg-alb |
| 리스너 및 라우팅-대상그룹 | <PREFIX>-tg-nginx |
서비스 정의
리소스(<PREFIX>-service)
| 구분 | 항목 | 값 |
|---|---|---|
| 서비스 세부 정보 | 서비스 이름 | <PREFIX>-service |
| 태스크 정의 패밀리 | <PREFIX>-task | |
| 환경 | 컴퓨팅 옵션 | 용량 공급자 전략 |
| 배포 구성 | 원하는 태스크 | 2 |
| 로드 밸런싱 | 로드 밸런서 | <PREFIX>-alb (기존) |
| 대상 그룹 | <PREFIX>-tg-nginx (기존) | |
| 컨테이너 선택 | <PREFIX>-web (포트 80) |
점검
- RDS 인스턴스 상태 확인 (Available)
- RDS 엔드포인트 복사
- ECR 이미지 푸시 확인
- ECS 클러스터 인스턴스 등록 확인
- 대상 그룹 Health Check 통과 확인
- ALB DNS로 접속 테스트
-
http://<ALB-DNS>/→ nginx 정적 페이지 -
http://<ALB-DNS>/api/health→ DB 연결 확인 -
http://<ALB-DNS>/api/db/test→ MySQL 버전 확인
-
- nginx → fastapi → RDS 통신 테스트
Troubleshooting
| 증상 | 원인 | 해결 |
|---|---|---|
| DB 연결 실패 | 보안 그룹 미허용 | <PREFIX>-sg-rds에 <PREFIX>-sg-ecs 허용 확인 |
| RDS 접근 불가 | 서브넷 라우팅 | Private 서브넷의 NAT 라우팅 확인 |
| 환경 변수 미적용 | 태스크 정의 | ECS 태스크 정의에서 환경 변수 확인 |
| Health Check 실패 | fastapi 에러 | CloudWatch 로그 확인 |
| 502 Bad Gateway | 대상 그룹 타입 불일치 | Instance 타입 사용 확인 |
| links 통신 실패 | 링크 설정 누락 | 태스크 정의에서 links 확인 |