[V1 V2] IaC 및 마이그레이션 - 100-hours-a-week/KTB4-15th-CLOUD GitHub Wiki
목차
- 1. V1 인프라 import
- 2. V1 Dev 서버 구축
- 3. V2 Dev 서버 구축
- 작업 순서
- 1. V2 네트워크
- 2. V2 보안그룹
- 3. V2 IAM·ECS Cluster 기반 구성
- 4. V2 ALB & DNS
- 5. RDS Postgres 생성
- 6. Crawler 기반 구성
- 7. V2 애플리케이션 Task Definition
- 8. ECS Service 생성
- 9. CI/CD 설정 (V2 미구축)
- 10. SQS 구성
- 11. V2 dev ECS 런타임 설정 및 CloudWatch Logs 구성
- 12. V2 ALB 배포 전제 정비
- 13. Task Definition 최초 등록
- 14. ECS Service 및 Task 활성화
- 15. 런타임 검증
- 16. 테스트 도메인 연결 및 E2E 확인
- 17. 최종 컷오버
- 18. Crawler 후속 작업
- 작업 상황
- 작업 순서
1. V1 인프라 import
작업 순서
- 이슈 생성(리소스)
- AWS CLI로 리소스 ID 조회
- imports.tf에 import 블록 작성 → terraform plan -generate-config-out으로 코드 자동 생성
- 생성된 코드 정리 (기본값·충돌 속성 제거, 하드코딩 ID → 참조로 변경)
- 로컬 plan으로 "N to import, 0 to add, 0 to destroy" 확인 (태그 추가만 change로 나옴)
- PR → CI plan 확인 → merge → CI가 apply (실제 import는 CI에서 실행)
- 정리 PR: imports.tf 삭제 + 미사용 리소스 있으면 코드에서 지워서 삭제
1-1. 최종 파일 구조
KTB4-15th-CLOUD/
├── .github/
│ └── workflows/
│ ├── terraform-pr.yaml # 모든 PR: fmt·validate·tflint·plan (필수 체크 terraform)
│ └── terraform-apply.yaml # main push: plan → apply
│
├── infra/
│ ├── environments/ # Terraform Root (State 1개)
│ │ ├── backend.tf # S3 Backend
│ │ ├── providers.tf # Terraform/AWS/Archive Provider, default_tags
│ │ ├── main.tf # shared · prod · dev V1 모듈 호출
│ │ ├── dev-v2.tf # dev V2 모듈 호출 (main.tf와 분리)
│ │ ├── .terraform.lock.hcl
│ │ ├── .tflint.hcl
│ │ │
│ │ ├── shared/
│ │ │ └── core/
│ │ │ ├── ecr.tf # prod ECR: Next.js, Spring, FastAPI
│ │ │ ├── route53.tf # Hosted Zone, MX, SPF
│ │ │ └── outputs.tf
│ │ │
│ │ ├── prod/
│ │ │ ├── parameter-store/ # V1/V2 공용 MySQL String 설정
│ │ │ ├── s3/ # prod 이미지 버킷
│ │ │ ├── v1/
│ │ │ │ ├── network/ # VPC, Subnet, IGW, Route Table, App/DB SG
│ │ │ │ ├── compute/ # EC2, EIP, EIP Association
│ │ │ │ ├── database/ # MySQL RDS (modules/rds-mysql 사용)
│ │ │ │ ├── iam/ # ec2.tf, cicd.tf, discord-alert.tf, eventbridge.tf
│ │ │ │ ├── cicd-deploy-policy/ # 앱 CD 배포 Role 정책 (prod EC2만)
│ │ │ │ ├── edge/ # lookddak.com, www A 레코드
│ │ │ │ ├── monitoring/ # Logs, Alarm, EventBridge, SQS, SNS, Discord Lambda
│ │ │ │ │ └── lambda/discord_alert/lambda_function.py
│ │ │ │ └── parameter-store/ # V1 EC2 PostgreSQL String 설정
│ │ │ └── v2/ # database/, ecs/, edge/ (Placeholder)
│ │ │
│ │ └── dev/
│ │ ├── ecr/ # dev ECR: nextjs, spring, fastapi, crawler (V1·V2 공용)
│ │ ├── parameter-store/ # dev MySQL String 설정
│ │ ├── s3/ # dev 이미지 버킷
│ │ ├── database/ # Placeholder (.gitkeep)
│ │ ├── v1/
│ │ │ ├── network/ # dev VPC 10.30.0.0/16, App/DB SG
│ │ │ ├── database/ # dev MySQL RDS
│ │ │ ├── parameter-store/ # dev EC2 PostgreSQL String 설정
│ │ │ ├── iam/ # dev EC2 Role, 앱 dev CD Role
│ │ │ ├── compute/ # dev EC2, EIP
│ │ │ ├── cicd-deploy-policy/ # 앱 dev CD 배포 Role 정책 (dev EC2만)
│ │ │ ├── edge/ # dev.lookddak.com A 레코드
│ │ │ └── logging/ # dev 로그 그룹 (알림 없음)
│ │ └── v2/
│ │ ├── network/ # Public 2c, Private App 2a/2c, NAT, V2 SG
│ │ ├── alb/ # ALB, ACM, Listener, Target Group
│ │ ├── ecs-cluster/ # lookddak-dev-v2
│ │ ├── ecs-services/ # ECS Service, Service Connect, Auto Scaling
│ │ ├── iam/ # Task Execution/Task/Scheduler Role
│ │ ├── database/ # PostgreSQL RDS + Parameter Store
│ │ ├── crawler/ # EventBridge Scheduler
│ │ ├── logging/ # V2 CloudWatch Log Group
│ │ ├── sqs/ # 메인 Queue·DLQ·CloudWatch Alarm
│ │ ├── cicd-deploy-policy/ # V2 ECS 배포 권한
│ │ └── task-definitions/ # Task Definition 기본 정의
│ │
│ └── modules/
│ ├── rds-mysql/ # prod·dev V1 MySQL
│ ├── rds-postgres/ # dev V2 PostgreSQL
│ ├── ecs-service/ # dev V2 ECS Service
│ └── network/ # 비어 있음
│
└── scripts/
└── v1/
├── README.md
├── docker-compose.yaml
├── docker-compose.dev.yaml # dev 서버 덮어쓰기 (로그 그룹, Nginx 설정)
├── nginx/
│ ├── nginx.conf
│ ├── conf.d/default.conf # prod
│ └── conf.d.dev/default.conf # dev
├── scripts/
│ ├── bootstrap.sh
│ ├── deploy.sh
│ ├── load-parameters.sh # prod
│ ├── load-parameters-dev.sh # dev (서버에는 load-parameters.sh 이름으로 배치)
│ ├── container-metric.sh
│ └── docker-alert.sh
└── systemd/
└── docker-alert.service
-
Terraform Root 단일화
infra/environments만 Terraform root로 사용하고 모든 리소스가 S3 State 하나를 공유한다. Child Module에는 별도 Backend, Provider,versions.tf를 두지 않는다. (infra/modules의 재사용 모듈만versions.tf를 가진다.) -
환경 분리와 V1/V2 분리
VPC는 환경마다 하나씩 둔다. 처음에는 VPC를
shared/core에 두었지만 dev VPC를 prod와 분리하면서prod/v1/network로 옮겼다. (moved블록으로 이동, 리소스 재생성 없음)환경 VPC V1 V2 prod 10.20.0.0/16prod/v1prod/v2(예정, 같은 VPC 재사용)dev 10.30.0.0/16dev/v1dev/v2(같은 VPC 재사용)shared/core에는 환경과 무관한 Route53 Hosted Zone과 prod ECR만 남는다. V1/V2와 무관하게 계속 쓰는 리소스(S3, ECR, 공용 Parameter Store)는prod/,dev/바로 아래에 둔다. -
공용 설정과 V1 전용 설정 분리
MySQL 및 공용 설정은
{prod,dev}/parameter-store, EC2 내부 PostgreSQL 컨테이너 설정은{prod,dev}/v1/parameter-store, V2 PostgreSQL RDS 설정은dev/v2/database에서 관리한다. -
운영 서버 파일의 기준선 보관
scripts/v1은 EC2의/opt/lookddak파일을 복구하거나 변경 이력을 확인하기 위한 기준본이다. Terraform 관리 대상이 아니며 자동 배포되지 않는다. prod와 dev는 같은 파일을 쓰고, dev 차이는 dev 전용 파일(docker-compose.dev.yaml,conf.d.dev,load-parameters-dev.sh)로 분리한다. -
V2 코드 분리
dev V2 코드는
dev/v2/에만 두고 루트 연결은dev-v2.tf에서 한다. V1 파일과 충돌하지 않게 하기 위함이다.prod/v2,dev/database,modules/network는 아직 비어 있다. -
순환 참조 회피용 분리
cicd-deploy-policy는 배포 정책에 EC2 ARN이 필요하고, EC2는iam의 인스턴스 프로파일이 있어야 만들어지므로iam과 별도 폴더로 둔다.
1-2. Secret 관리
1. 문제 상황
현재 운영 환경에서는 일반 설정값(String) 7개와 민감정보(SecureString) 12개를 AWS Systems Manager Parameter Store에서 관리하고 있다.
- 일반 설정값(
String): 7개 - 민감정보(
SecureString): 12개
일반 설정값은 Terraform 코드와 State에 저장되어도 문제가 크지 않지만, SecureString을 Terraform으로 관리하면 다음 문제가 발생할 수 있다.
-
Terraform은 Parameter Store의 값 입력을 요구한다.
aws_ssm_parameter리소스를 생성하려면value가 필요하다. 따라서 Parameter Store에서는 이름과 설명 같은 “보관 공간”만 Terraform으로 만들고, 실제 값은 콘솔에서 별도로 입력하는 방식이 어렵다. -
민감정보가 Terraform State에 남을 수 있다.
비밀번호와 API Key를 Terraform 코드에 직접 작성하지 않더라도 Terraform이 값을 읽으면 State에 저장될 가능성이 있다.
State를 S3에서 안전하게 관리하더라도 민감정보가 State에 들어가는 것 자체가 보안 범위와 접근 권한을 불필요하게 넓힌다.
-
ignore_changes도 완전한 해결책이 되지는 못한다.다음과 같이 임시 값을 넣고 실제 값 변경을 무시하는 방법도 있다.
resource "aws_ssm_parameter" "example" { name = "/lookddak/prod/example" type = "SecureString" value = "CHANGE_ME" lifecycle { ignore_changes = [value] } }하지만 이 방식에는 다음 위험이 있다.
- 처음 생성될 때
CHANGE_ME가 실제 값으로 등록된다. - Terraform이 실제 값을 조회하면서 State에 저장할 수 있다.
- 실수로 리소스를 재생성하면 임시 값으로 덮어쓸 수 있다.
- 실제 값의 변경 여부를 Terraform이 관리하거나 검증하지 못한다.
따라서 민감정보 관리 방식으로는 사용하지 않기로 한다.
- 처음 생성될 때
2. 해결 방안
장기적으로는 민감정보를 Secrets Manager로 이전하는 것이 적절하지만, V1 Import 단계에서 바로 이전하면 운영 방식이 함께 변경된다. Secrets Manager로 이전하려면 다음 항목을 함께 변경해야 한다.
load-parameters.sh- EC2 IAM 권한
- Lambda IAM 권한과 조회 코드
- 애플리케이션 환경변수 생성 과정
- 장애 복구 및 배포 절차
- 운영 서버에 있는 실제 스크립트
- 롤백 절차
이를 V1 Import와 동시에 진행하면 인프라 기준선 등록과 운영 애플리케이션 동작 변경이 하나의 작업에 섞인다. 배포 실패 또는 Secret 조회 실패가 발생하면 원인이 Import인지 Secret 전환인지 구분하기 어려워진다.
따라서 V1 Import와 V1/V2 무중단 마이그레이션 기간에는 기존 운영 방식을 유지한다. 민감하지 않은 String 7개는 Terraform으로 관리한다. 민감정보(SecureString) 12개는 terraform으로 Import 하지 않고 Parameter Store Console에서 수동 관리한다.
| 관리 대상 | 현재 관리 방법 |
|---|---|
비민감 String 7개 |
Terraform + Parameter Store |
민감 SecureString 12개 |
Parameter Store Console |
| 실제 민감값 | Terraform 코드·State에 저장하지 않음 |
| EC2의 Parameter 조회 권한 | IAM + Terraform |
| Lambda의 Discord Parameter 조회 권한 | IAM + Terraform |
| V1 운영 스크립트 | scripts/v1에 기준본 보관, 서버 반영은 수동 |
3. 마이그레이션 기간의 운영 방식
V1과 V2가 동시에 동작하는 동안에는 기존 Parameter Store 값을 유지한다.
필요한 경우 V2 애플리케이션도 마이그레이션 기간에는 기존 SecureString을 읽도록 구성한다. 이를 통해 V2 장애 시 V1으로 다시 트래픽을 돌릴 수 있도록 한다.
이 기간에는 다음 작업을 하지 않는다.
- 기존 SecureString 삭제
- Secret 이름 변경
- V1
load-parameters.sh의 Secrets Manager 전환 - Terraform을 통한 실제 Secret 값 관리
- 검증되지 않은 Secret 자동 Rotation 적용
4. 최종 전환 방안
V1/V2 무중단 마이그레이션과 V2 안정화가 완료된 후 다음 구조로 변경한다.
- 비민감 설정값: Parameter Store
- 민감정보: Secrets Manager
Terraform에서는 Secrets Manager의 보관함과 IAM 권한만 관리한다.
| 관리 대상 | 최종 관리 방법 |
|---|---|
| Secret 이름, 설명, 태그 | Terraform |
| Secret 보관함 생성 및 삭제 방지 | Terraform |
| 실제 비밀번호 및 API Key 입력 | AWS Console |
| Secret 값과 버전 | Secrets Manager |
| 애플리케이션 접근 권한 | IAM + Terraform |
| 비민감 설정값 | Parameter Store + Terraform |
전환은 다음 순서로 진행한다.
- V2 트래픽 전환 및 안정화 완료
- Secrets Manager 보관함 생성
- 기존 SecureString 값을 Console에서 수동 복사
- V2 IAM에
secretsmanager:GetSecretValue권한 추가 - V2 애플리케이션의 Secret 조회 방식 변경
- 조회 및 장애 복구 테스트
- 일정 기간 기존 Parameter Store 값과 병행 유지
- 롤백 필요성이 사라진 후 기존 SecureString 정리
- V1 폐기
2. V1 Dev 서버 구축
prod V1과 같은 구조의 dev 환경을 dev.lookddak.com에 구축했다. prod와 리소스·권한을 모두 분리한다.
2-1. 구성
| 영역 | 리소스 | prod와 다른 점 |
|---|---|---|
| 네트워크 | lookddak-dev-vpc 10.30.0.0/16, Public 2a 10.30.1.0/24, Private DB 2a/2c 10.30.11.0/24, 10.30.12.0/24, App/DB SG |
VPC 자체를 분리 |
| DB | RDS MySQL 8.4 lookddak-dev-mysql (db.t4g.micro, prod와 같은 파라미터) |
삭제 보호 꺼짐, 최종 스냅샷 생략, 관리자 비밀번호 write-only |
| 컴퓨팅 | EC2 lookddak-dev-app-v1 (t4g.medium, Ubuntu 24.04 ARM, gp3 30GB), EIP |
종료 보호 꺼짐 |
| 컨테이너 | Nginx, Next.js, Spring, FastAPI, PostgreSQL(pgvector) | /opt/lookddak, 데이터 /srv/lookddak/postgresql |
| 이미지 | dev ECR lookddak-dev-{nextjs,spring,fastapi} |
prod ECR과 분리, 최신 10개 유지 |
| 설정 | /lookddak/dev/rds/*, /lookddak/dev/postgresql/* (String), dev SecureString 9개 |
|
| IAM | EC2 Role lookddak-dev-ec2 |
dev ECR·dev S3·/lookddak/dev/*·dev 로그 그룹만 |
| 도메인 | dev.lookddak.com A → dev EIP, Let's Encrypt 인증서 |
www 없음 |
| 로그 | /lookddak/dev/{nginx,spring,fastapi,nextjs,postgresql,deploy}, 30일 |
알림 없음 (메트릭 필터·알람·EventBridge·Discord 미구성) |
dev ECR을 prod와 분리한 이유
- ECR이 태그 덮어쓰기 금지(IMMUTABLE)라 같은 커밋 SHA 태그를 dev·prod가 함께 쓸 수 없다. FE는
NEXT_PUBLIC_API_BASE_URL이 빌드 시점에 이미지에 들어가 dev·prod 이미지 내용이 다르다. - dev 빌드가 prod 수명 주기(최신 35개)를 채워 prod 롤백용 이미지가 삭제되는 것을 막는다.
2-2. 서버 파일과 설정
-
서버 파일은
scripts/v1과 같은 구조로/opt/lookddak에 배치한다. dev는load-parameters-dev.sh를load-parameters.sh이름으로 복사한다.COMPOSE_FILE=docker-compose.yaml:docker-compose.dev.yaml # dev 덮어쓰기 적용 ECR_REPOSITORY_PREFIX=lookddak-dev # dev ECR 사용 (없으면 lookddak) S3_BUCKET, CORS_ALLOWED_ORIGINS, NEXT_PUBLIC_API_BASE_URL # dev 값 NEXTJS_IMAGE_TAG / SPRING_IMAGE_TAG / FASTAPI_IMAGE_TAG # deploy.sh가 갱신
2-3. 앱 dev CD
| 항목 | 내용 |
|---|---|
| 트리거 | 앱 레포 develop, hotfix/** push |
| GitHub Environment | develop (변수 이름은 production과 같고 값만 dev) |
| AWS Role | {fe,be,ai}-dev-cd-{build,deploy}-role (신뢰 조건: environment:develop, develop, hotfix/*) |
| 권한 | build: dev ECR push / deploy: dev ECR 조회, dev EC2에만 SSM Run Command |
| job 이름 | dev-build, dev-deploy |
| 배포 로그 | /lookddak/dev/deploy |
- prod 배포 Role 정책에서 dev EC2를 제거했다. prod Role은 prod EC2에만 배포할 수 있다.
- 앱 레포 브랜치 규칙: develop은 CI 필수, main은 CI +
dev-deploy필수 → dev 서버에 배포 성공한 커밋만 main에 merge된다.
3. V2 Dev 서버 구축
VPC
- v1 dev - v2 dev 동일 (
10.30.0.0/16) - v1 prod - v2 prod 동일 (
10.20.0.0/16)
총 2개로 운영
작업 순서
숙지사항
- 모든 PR에서 Prod 리소스 변경이 0인지 확인
- 각 번호가 issue & pr 단위
- V2 코드는
infra/environments/dev/v2/아래에 배치 - 루트 모듈 연결은 기존
main.tf를 수정하지 않고infra/environments/dev-v2.tf에서 처리 - 기존 V1 Dev VPC
10.30.0.0/16와 DB subnet을 재사용 - Dev는 운영과 같은 구성 요소·네트워크 경로·Task 크기를 사용 — 평상시 Task 수만 서비스별 1개로 축소
- V2 Spring은 V2 Dev MySQL(
lookddak-dev-v2-mysql)을 사용한다. 해당 MySQL Green 생성 과정은 현재 Terraform 관리 범위에서 제외한다. - V1 EC2 → PostgreSQL RDS 연결도 지금은 허용하지 않는다. 마이그레이션 단계에서 별도 SG 변경으로 연다
- 평상시 개발 중에는 V2 PostgreSQL 쓰기를 허용한다. 리허설 시작 전에 데이터를 초기화하고 쓰기 주체를 통제
- SQS는 Spring이 VTON 작업을 발행하고 FastAPI가 소비하기 위한 큐
- Crawler는 EventBridge Scheduler로 실행되며 29CM 크롤링 후 PostgreSQL을 갱신
1. V2 네트워크
기존 Public 2a 10.30.1.0/24 (V1 dev)
신규 Public 2c 10.30.2.0/24 lookddak-dev-v2-public-c
Private App 2a 10.30.21.0/24 lookddak-dev-v2-private-app-a
Private App 2c 10.30.22.0/24 lookddak-dev-v2-private-app-c
기존 Private DB 2a 10.30.11.0/24 (V1 dev)
기존 Private DB 2c 10.30.12.0/24 (V1 dev)
추가로 아래 리소스 구축
- Public 2c subnet
- Public 2c route table (
lookddak-dev-v2-public-c-rt) - 기존 Dev VPC의 Internet Gateway 조회 (data source)
- NAT Gateway 1개 (
lookddak-dev-v2-nat, Public 2c) - NAT용 EIP (
lookddak-dev-v2-nat-eip) - Private App route table (
lookddak-dev-v2-private-app-rt) - Private App 2a/2c route association
- V2 전용 Security Group
NAT(1개)
Public 2c:
Internet Gateway 연결
NAT Gateway 배치
Private App 2a:
0.0.0.0/0 → NAT Gateway
Private App 2c:
0.0.0.0/0 → NAT Gateway
2. V2 보안그룹
lookddak-dev-v2-alb-sg
lookddak-dev-v2-nextjs-sg
lookddak-dev-v2-spring-sg
lookddak-dev-v2-fastapi-sg
lookddak-dev-v2-crawler-sg
lookddak-dev-v2-postgresql-sg
주요 통신 규칙
Internet:
├─ 80 → ALB (HTTPS 리다이렉트용)
└─ 443 → ALB
ALB:
├─ 3000 → Next.js
└─ 8080 → Spring
Next.js:
└─ 8080 → Spring (SSR, Service Connect)
Spring:
├─ 3306 → V2 Dev MySQL (해당 DB 보안 그룹에 Spring 허용 규칙 추가)
├─ 8000 → FastAPI
└─ 443 → AWS API (SQS 등)
FastAPI:
├─ 5432 → PostgreSQL RDS
└─ 443 → OpenAI·Runware 및 AWS API
Crawler:
├─ 5432 → PostgreSQL RDS
└─ 443 → 29CM 크롤링 대상
PostgreSQL RDS:
├─ 5432 ← FastAPI
└─ 5432 ← Crawler
- ECS Task(Next.js·Spring·FastAPI·Crawler)는 443 아웃바운드를 공통으로 허용한다. SQS 접근 권한은 SG가 아니라 IAM으로 제어한다.
3. V2 IAM·ECS Cluster 기반 구성
- ECS Cluster
lookddak-dev-v2 - Container Insights 활성화
- ECS Task Execution Role
- 서비스별 Task Role
- CloudWatch Log Group 및 로그 전송 권한 구성(세부 내용은 11번 참고)
Role
| Role | 이름 | 권한 |
|---|---|---|
| Execution Role | lookddak-dev-v2-task-execution |
ECR pull (lookddak-* 저장소 패턴), /lookddak/dev/* Parameter 조회, SSM 경유 KMS decrypt |
| Next.js Task Role | lookddak-dev-v2-nextjs-task |
기본적으로 AWS 권한 없음. 서버 측 S3 접근이 확인될 때만 추가 |
| Spring Task Role | lookddak-dev-v2-spring-task |
Dev S3 읽기·쓰기, 필요한 SQS 발행·소비 권한(10번 참고), ECS Exec용 ssmmessages 권한 |
| FastAPI Task Role | lookddak-dev-v2-fastapi-task |
Dev S3 읽기·쓰기, 필요한 SQS 발행·소비 권한(10번 참고) |
| Crawler Task Role | lookddak-dev-v2-crawler-task |
AWS 권한 없음. PostgreSQL 접속은 네트워크와 DB 계정으로 제어 |
| Crawler Scheduler Role | lookddak-dev-v2-crawler-scheduler |
Crawler Task에 대한 ecs:RunTask, Task/Execution Role에 대한 iam:PassRole |
- Execution Role의 ECR 권한이
lookddak-*패턴이라 V1 dev와 같은 dev ECR(lookddak-dev-*)을 그대로 사용한다.
4. V2 ALB & DNS
- Internet-facing ALB
lookddak-dev-v2-alb(Public 2a/2c) - HTTP 80 Listener → HTTPS Redirect (301)
- HTTPS 443 Listener
- ACM 인증서 및 Route 53 DNS 검증
- V2 Next.js Target Group
lookddak-dev-v2-nextjs-tg(ip, 3000) - V2 Spring Target Group
lookddak-dev-v2-spring-tg(ip, 8080) - CDN 제외
- FastAPI는 외부 ALB에 연결하지 않고 Service Connect로만 접근
V2 Test Domain: v2-dev.lookddak.com
Listener Rule (HTTPS 443):
priority 10: /api, /api/* → V2 Spring Target Group : 8080
default: 그 외 경로 → V2 Next.js Target Group : 3000
V1 Domain:
dev.lookddak.com → V1 Dev EC2 유지
/v2URL prefix는 사용하지 않는다.- V2 검증 완료 전까지
dev.lookddak.com은 V1을 유지한다. - 최종 헬스 체크와 테스트 도메인 정비 내용은 12번에서 다룬다.
5. RDS Postgres 생성
Identifier: lookddak-dev-postgresql
Engine: PostgreSQL 16.15
Parameter family: postgres16
Instance class: db.t4g.small
Database: lookddak
Storage type: gp3
Allocated storage: 20GiB
Max allocated storage: 100GiB
Deployment: Multi-AZ DB instance
Standby: RDS가 다른 AZ에 자동 생성
Public access: false
Subnet: 기존 Private DB 2a/2c
Security Group: lookddak-dev-v2-postgresql-sg
Encryption: true
Backup retention: 7일 (18:00-18:30 UTC)
Maintenance: sun:19:00-19:30 UTC
Deletion protection: true
Skip final snapshot: false
Final snapshot: lookddak-dev-postgresql-final
Logs:
- postgresql
- upgrade
Admin password: SecureString → ephemeral → master_password_wo (State·plan에 남지 않음)
Extension:
pgvector: 0.8.2 (수동 SQL로 활성화 필요)
Multi-AZ 구성
Primary:
위치: Private DB subnet 중 한 AZ
역할: 애플리케이션 연결 및 읽기·쓰기
Standby:
위치: 다른 AZ
역할: 동기식 복제 및 장애 시 승격
직접 연결: 불가능
읽기 분산: 불가능
Endpoint:
애플리케이션은 하나의 RDS Endpoint 사용
장애 조치 시 Endpoint가 새로운 Primary를 가리킴
- Primary와 Standby를 Terraform 리소스 두 개로 생성하지 않는다.
aws_db_instance하나에multi_az = true를 지정한다. (modules/rds-postgres)- Multi-AZ는 가용성 구성이지 V1→V2 데이터 마이그레이션 수단이 아니다.
- Multi-AZ 비용은 Primary와 Standby 두 인스턴스를 기준으로 발생한다.
Parameter group
rds.logical_replication = 1 (pending-reboot)
max_replication_slots = 10 (pending-reboot)
max_wal_senders = 10 (pending-reboot)
max_logical_replication_workers = 4 (pending-reboot)
max_worker_processes = 8 (pending-reboot)
max_slot_wal_keep_size = 2048 (immediate)
| 파라미터 | 역할 | 이번 구성에서 필요한 이유 |
|---|---|---|
rds.logical_replication |
RDS의 논리 WAL 생성을 활성화 | RDS가 역방향 복제의 Publisher가 되기 위해 필요 |
max_replication_slots |
유지 가능한 Slot 및 Subscription 추적 수의 상한 | 정방향 Subscription과 역방향 Slot, 초기 테이블 동기화용 여유 확보 |
max_wal_senders |
WAL을 외부 Subscriber에 전달할 수 있는 연결 수 | RDS → V1 역방향 복제 연결에 필요 |
max_logical_replication_workers |
Apply·초기 동기화·병렬 적용 Worker 상한 | RDS가 정방향 Subscriber로 동작할 때 변경 사항 적용에 필요 |
max_worker_processes |
논리 복제를 포함한 전체 Background Worker 상한 | 논리 복제 Worker가 실행될 상위 Worker Pool 제공 |
max_slot_wal_keep_size |
Slot으로 보존할 WAL 크기의 안전 상한 | 복제 중단 시 WAL이 계속 누적되어 스토리지를 소진하는 상황 방지 |
pgvector 설치
CREATE EXTENSION IF NOT EXISTS vector VERSION '0.8.2';
- RDS 엔진이 pgvector를 지원한다고 해서 확장이 자동 활성화되지는 않는다.
- RDS 생성 후 Migration 계정 또는 Admin 계정으로 대상 Database에 확장을 생성한다.
- 논리 복제 초기화 전에 V1과 V2의 확장 및 스키마를 동일하게 준비한다.
Parameter Store
V1 dev EC2 PostgreSQL 컨테이너가 이미
/lookddak/dev/postgresql/*를 사용하므로, V2 RDS는/lookddak/dev/v2/postgresql/*경로를 사용한다.
# Terraform 관리: String (dev/v2/database/parameter-store.tf)
/lookddak/dev/v2/postgresql/admin/username lookddak_admin
/lookddak/dev/v2/postgresql/host
/lookddak/dev/v2/postgresql/port
/lookddak/dev/v2/postgresql/database lookddak
/lookddak/dev/v2/postgresql/app/username lookddak_app
/lookddak/dev/v2/postgresql/migration/username lookddak_migration
# 콘솔에서 수동 생성: SecureString
/lookddak/dev/v2/postgresql/admin/password
/lookddak/dev/v2/postgresql/app/password
/lookddak/dev/v2/postgresql/migration/password
원칙은 다음을 고수한다.
host,port,database,username은 Terraform이String으로 생성한다.- 비밀번호는 Terraform 리소스로 생성하지 않는다.
- Terraform 코드에는 SecureString의 경로만
locals, IAM 정책 또는 문서로 정의한다. - 빈 값이나
CHANGE_ME같은 임시 값은 넣지 않는다. - Parameter Store는 값 없이 Parameter 이름만 생성할 수 없으므로, SecureString의 실제 리소스는 콘솔에서 값을 입력할 때 생성한다.
- 전체
DATABASE_URL은 저장하지 않고 ECS Task 시작 시 개별 Parameter를 조합한다. - Admin 계정은 애플리케이션에서 사용하지 않는다.
- App 계정과 Migration 계정을 분리한다.
- App/Migration DB Role 생성은 별도 SQL 작업으로 수행한다.
- Admin 비밀번호 SecureString이 없을 때 전체 plan을 보기 위한 plan 전용 입력(
dev_v2_postgresql_admin_password_wo)이 있다. apply에는 사용하지 않는다.
6. Crawler 기반 구성
작업 범위
- Crawler용 Dev ECR (
dev/ecr에crawler추가) - Crawler ECS Task Definition JSON
- EventBridge Scheduler Group
- EventBridge Schedule
- 기존 Scheduler 실행 Role 연결
- 초기 Schedule 상태
DISABLED - SQS 및 Queue 관련 IAM 권한 제외
- CloudWatch Log Group 및 로그 설정 제외
Crawler 실행 구조
EventBridge Scheduler:
└─ ECS RunTask
└─ Crawler Fargate Task
├─ HTTPS 443 → NAT Gateway → 29CM
└─ PostgreSQL 5432 → 상품 데이터 INSERT/UPDATE
ECS Cluster: lookddak-dev-v2
Task Execution Role: lookddak-dev-v2-task-execution
Crawler Task Role: lookddak-dev-v2-crawler-task
Scheduler Role: lookddak-dev-v2-crawler-scheduler
Security Group: lookddak-dev-v2-crawler-sg
Private App Subnet: Private App 2a, Private App 2c
- Crawler Task Role은 현재 AWS API 권한이 없다.
- PostgreSQL 접속은 IAM이 아니라 Security Group과 DB 계정으로 제어한다.
- 동일한 IAM Role과 Security Group을 중복 생성하지 않는다.
Crawler ECR
Name: lookddak-dev-crawler
Image tag mutability: IMMUTABLE
Image scanning: true
Encryption: AES256
Lifecycle:
Untagged image: 1일 후 삭제
Tagged image: 최신 10개 유지
- V1 dev와 같은
dev/ecr모듈에서for_each로 함께 관리한다. (nextjs,spring,fastapi,crawler) - 운영 환경에서는 별도
lookddak-crawler를 생성하거나 이미지 승격 정책을 별도로 설계 - Task Definition에는
latest가 아니라 Git SHA 태그의 Image Digest를 사용 - Crawler 저장소용 GitHub Actions ECR Push 권한은 별도 CI/CD 작업에서 구성
EventBridge Scheduler
Group: lookddak-dev-v2
Schedule: lookddak-dev-v2-crawler
Expression: cron(0 4 * * ? *) (Asia/Seoul, 매일 오전 4시)
State: DISABLED
Flexible time window: OFF
Target:
ECS Cluster: lookddak-dev-v2
Task Definition: lookddak-dev-v2-crawler (family ARN, 최신 revision)
Launch type: FARGATE
Platform version: LATEST
Task count: 1
Network:
Private App 2a/2c
lookddak-dev-v2-crawler-sg
Assign public IP: false
Retry:
maximum_event_age: 3600초
maximum_retry: 2
DLQ: 제외
- Schedule 이름은 기존 Scheduler Role의 IAM 조건과 일치하도록
lookddak-dev-v2-crawler접두사를 사용한다. - Scheduler는 ECS Task 실행 요청 성공 여부만 확인하며, Crawler 프로세스의 최종 성공·실패까지 감시하지 않는다.
- Task 종료 상태 모니터링은 이후 PLG 또는 ECS Task State Change 연동 단계에서 구성한다.
- Crawler가 V2 PostgreSQL의
lookddak_app계정을 재사용 (SELECT, INSERT, UPDATE)
Task Definition (dev/v2/task-definitions/crawler.json)
Family: lookddak-dev-v2-crawler
Launch type: FARGATE
Network mode: awsvpc
OS / Arch: LINUX / X86_64
CPU / Memory: 1024 / 2048
Ephemeral storage: 20GiB
Image: lookddak-dev-crawler@sha256:REPLACED_BY_CI
Command: 이미지의 ENTRYPOINT/CMD
Port / Health check / Logging / Environment / Secrets: 미구성
배포 흐름
- Git SHA 태그로 Crawler 이미지 빌드
lookddak-dev-crawlerECR에 Push- Git SHA 태그의 Image Digest 조회
- Task Definition JSON의
REPLACED_BY_CI교체 - 새 Task Definition Revision 등록
- Scheduler 활성화 전 수동 RunTask 검증
- 검증 완료 후 Schedule을
ENABLED로 변경
이 단계에서는 Crawler 로그·환경변수·시크릿을 제외했으며, 이후 11번 런타임 구성에서 보완했다.
7. V2 애플리케이션 Task Definition
| Task | CPU | Memory | Port (이름) | 실행 형태 |
|---|---|---|---|---|
Next.js lookddak-dev-v2-nextjs |
0.5 vCPU | 1GiB | 3000 (nextjs-http) |
ECS Service |
Spring lookddak-dev-v2-spring |
1 vCPU | 2GiB | 8080 (spring-http) |
ECS Service |
FastAPI lookddak-dev-v2-fastapi |
1 vCPU | 2GiB | 8000 (fastapi-http) |
ECS Service |
Crawler lookddak-dev-v2-crawler |
1 vCPU | 2GiB | 없음 | Scheduler RunTask |
공통 실행 설정
Launch type: FARGATE
Network mode: awsvpc
Operating system: LINUX
Container count: Task당 1개
Essential container: true
Public IP: false
Execution Role: lookddak-dev-v2-task-execution
Task Role: lookddak-dev-v2-{nextjs,spring,fastapi}-task
Image: lookddak-dev-{nextjs,spring,fastapi}@sha256:REPLACED_BY_CI
- ECR 저장소는 V1 dev와 같은
lookddak-dev-{nextjs,spring,fastapi}를 사용한다. (V1 dev CD가 이미 이미지를 push 중) latest와 Git SHA 태그만으로 실행하지 않고 Digest까지 고정
Port Mapping, Service Connect 기본 이름, 스토리지 설정, 관리 소유 형태는 이전 계획과 같다.
Service Connect:
Namespace: lookddak-dev-v2
Discovery name: nextjs / spring / fastapi
Internal address: Spring → FastAPI http://fastapi:8000
Terraform 관리:
ECS Cluster, IAM Role, Network, ECS Service, Service Connect, Auto Scaling
애플리케이션 GitHub Actions 관리 (예정):
이미지 빌드 및 ECR Push → Digest 조회 → Task Definition Revision 등록 → ECS Service 배포
현재:
Task Definition 기본 JSON만 준비 (AWS 등록 안 됨, Terraform apply 대상 아님)
위 내용은 최초 등록 전 상태를 설명한다. 실제 최초 Revision 등록 결과는 13번에서 다룬다.
8. ECS Service 생성(코드 완료, 생성 보류)
- ECS Service Terraform 모듈(
modules/ecs-service) 및 설정 작성 - Service Connect Namespace 생성 (
lookddak-dev-v2, Cloud Map HTTP Namespace) - 서비스별 네트워크·Security Group·Target Group 연결 구조 작성
- Auto Scaling 설정 코드 작성
dev_v2_create_services = false(기본값) → 실제 ECS Service, Task, Auto Scaling 리소스 생성 보류- Service 생성 시
dev_v2_task_definition_arns에 등록된 Task Definition Revision ARN을 넘긴다.
ECS Service
Next.js: lookddak-dev-v2-nextjs-service (Target Group: nextjs-tg, Service Connect: Client only)
Spring: lookddak-dev-v2-spring-service (Target Group: spring-tg, Service Connect: spring:8080)
FastAPI: lookddak-dev-v2-fastapi-service (Target Group 없음, Service Connect: fastapi:8000)
공통 설정: FARGATE, REPLICA, Private App 2a/2c, Public IP false, Desired count 1
Service Connect 통신 구조
외부 사용자
├─ ALB → Next.js:3000
└─ ALB → Spring:8080
Next.js SSR
└─ Service Connect → spring:8080
Spring
└─ Service Connect → fastapi:8000
FastAPI
└─ 5432 → PostgreSQL RDS
Auto Scaling
| 서비스 | Min | Max | 지표 | Scale-out | Scale-in |
|---|---|---|---|---|---|
| Next.js | 1 | 5 | CPU 60% | 180초 | 300초 |
| Spring | 1 | 5 | CPU 60% | 120초 | 300초 |
| FastAPI | 1 | 5 | CPU 70%, Memory 70% | 300초 | 300초 |
위 내용은
create_services=false상태에서 인프라 코드를 먼저 준비한 단계다. 실제 Service 및 Task 활성화 결과는 14번에서 다룬다.
9. CI/CD 설정 (V2 미구축)
- V1 dev CD는 완료 (2-3 참고). V2 CI/CD는 아래 순서로 별도 구성한다.
- 앱 레포가 같은 dev ECR에 push하므로, V2 배포는 "이미지 Digest 조회 → Task Definition 등록 → ECS Service 배포" 단계만 추가하면 된다.
- 필요 권한:
ecs:RegisterTaskDefinition,ecs:UpdateService,ecs:DescribeServices, Task/Execution Role에 대한iam:PassRole(dev CD Role 또는 V2 전용 Role) - Crawler 레포 CI/CD Role (dev ECR
lookddak-dev-crawlerpush) 별도 구성
10. SQS 구성
| 용도 | 권장 실제 큐 이름 | 설정 |
|---|---|---|
| BE → AI 요청 | lookddak-dev-v2-be-fitting-request |
Visibility 120초, 보관 4일 |
| 요청 DLQ | lookddak-dev-v2-be-fitting-request-dlq |
보관 14일 |
| AI 내부 후처리 | lookddak-dev-v2-ai-fitting-response |
Visibility 300초, 보관 4일 |
| 후처리 DLQ | lookddak-dev-v2-ai-fitting-response-dlq |
보관 14일 |
| AI → BE 결과 | lookddak-dev-v2-be-fitting-response |
Visibility 60초, 보관 4일 |
| 결과 DLQ | lookddak-dev-v2-be-fitting-response-dlq |
보관 14일 |
- Queue type: Standard
maxReceiveCount = 3receive_wait_time_seconds = 20- Long Polling으로 빈 조회 요청과 비용을 줄임
delay_seconds = 0- 메시지 최대 크기:
256 KiB- 실제 이미지나 Base64를 넣지 않고 ID, S3 Key, 상태 정보만 전달
- 암호화:
SSE-SQSsqs_managed_sse_enabled = true- 별도 KMS 키와 복호화 권한이 필요 없어 dev에 적합
- 각 DLQ의 Redrive Allow Policy는 대응하는 메인 큐 하나만 허용
- TLS가 아닌 요청을 거부하는 Queue Policy 적용
- 태그:
Environment = devVersion = v2Component = sqsService = fitting
구현 방식
infra/environments/dev/v2/sqs/
├── main.tf
├── alarms.tf
├── variables.tf
└── outputs.tf
main.tf- 메인 큐 3개와 DLQ 3개 생성
- 메인 큐와 DLQ 연결
- Redrive Policy 및 Redrive Allow Policy 설정
- 암호화 및 TLS Queue Policy 설정
alarms.tf- 메인 큐 지연 알람 3개 생성
- DLQ 메시지 발생 알람 3개 생성
outputs.tf- 각 Queue URL과 ARN 출력
- IAM과 ECS Task Definition에서 사용할 수 있도록 제공
루트 dev-v2.tf에서 SQS 모듈을 호출하고, 생성된 Queue ARN을 V2 IAM 모듈에 전달한다.
메시지 처리 방식
별도의 Worker ECS Service는 생성하지 않는다.
기존 Spring/FastAPI 애플리케이션이 실행될 때 코드에 구현된 SQS Consumer도 함께 실행되는 구조를 사용한다.
Spring ECS Service
├── 기존 HTTP API 처리
├── be-fitting-request 메시지 발행
└── be-fitting-response 메시지 수신 및 처리
FastAPI ECS Service
├── 기존 HTTP/Webhook API 처리
├── be-fitting-request 메시지 수신 및 처리
├── ai-fitting-response 메시지 발행 및 처리
└── be-fitting-response 메시지 발행
따라서 기존 Spring/FastAPI ECS Service와 Task Definition을 그대로 사용하며, 새로운 Worker용 ECS Service와 Task Definition은 생성하지 않는다
IAM 권한 설계
SQS 권한은 ECS Task Execution Role이 아니라 각 애플리케이션 Task Role에 부여한다. Execution Role은 ECS 에이전트가 이미지와 시크릿을 준비할 때 사용하고, 애플리케이션이 SQS API를 호출할 때는 Task Role을 사용한다.
-
spring(lookddak-dev-v2-spring-task)
큐 권한 be-fitting-requestsqs:SendMessagebe-fitting-responsesqs:ReceiveMessage,sqs:DeleteMessage,sqs:ChangeMessageVisibility -
fastapi(lookddak-dev-v2-fastapi-task)
큐 권한 be-fitting-requestsqs:ReceiveMessage,sqs:DeleteMessage,sqs:ChangeMessageVisibilityai-fitting-responsesqs:SendMessage,sqs:ReceiveMessage,sqs:DeleteMessage,sqs:ChangeMessageVisibilitybe-fitting-responsesqs:SendMessage
권한은 Resource = "*"로 열지 않고 각 큐의 ARN으로 제한한다.
Queue URL을 환경변수로 직접 전달하기 때문에 sqs:GetQueueUrl은 기본 권한에 포함하지 않는다. 사용 중인 라이브러리가 큐 속성 조회를 요구하는 경우에만 sqs:GetQueueAttributes를 추가한다.
애플리케이션 Task Role에는 DLQ 조회, 삭제 및 Redrive 권한을 부여하지 않는다.
ECS 환경 변수
Queue URL은 민감정보가 아니므로 Parameter Store가 아닌 ECS Task Definition의 일반 환경변수로 전달한다. Spring
BE_FITTING_REQUEST_QUEUE_URL
BE_FITTING_RESPONSE_QUEUE_URL
AWS_REGION=ap-northeast-2
FastAPI
BE_FITTING_REQUEST_QUEUE_URL
AI_FITTING_RESPONSE_QUEUE_URL
BE_FITTING_RESPONSE_QUEUE_URL
AWS_REGION=ap-northeast-2
AWS Access Key와 Secret Key는 환경변수로 전달하지 않는다. Spring과 FastAPI는 ECS Task Role이 제공하는 임시 자격 증명을 사용한다.
CloudWatch 모니터링
-
메인 queue 지연 알람
큐 알람 조건 be-fitting-request가장 오래된 메시지가 180초를 초과한 상태가 2회 연속 발생 ai-fitting-response가장 오래된 메시지가 600초를 초과한 상태가 2회 연속 발생 be-fitting-response가장 오래된 메시지가 120초를 초과한 상태가 2회 연속 발생
공통 설정:
-
Metric:
ApproximateAgeOfOldestMessage -
Period: 1분
-
Statistic:
Maximum -
Missing data: 정상으로 처리
-
DLQ 알람
각 DLQ에 메시지가 1개 이상 존재하면 알람을 발생시킨다.
- Metric:
ApproximateNumberOfMessagesVisible - Period: 1분
- 평가 횟수: 1회
- Missing data: 정상으로 처리
- Metric:
알람 이름은 lookddak- 접두사로 생성하여 기존 알림 파이프라인을 사용한다.
CloudWatch Alarm
→ 기존 EventBridge 규칙
→ lookddak-alert-queue
→ Discord 알림 Lambda
→ Discord
DLQ 운영 방식
메시지가 동일한 메인 큐에서 3회 처리되지 못하면 대응하는 DLQ로 이동한다.
처리 실패 3회
→ DLQ 이동
→ CloudWatch 알람
→ Discord 알림
→ 인프라 운영자가 원인 확인
DLQ 메시지는 자동으로 재처리하지 않는다. 원인을 수정하고 중복 처리 가능성을 확인한 후, 기존 AWS 운영 권한을 가진 인프라 담당자가 원래 메인 큐로 수동 Redrive한다.
작업 상황
| 영역 | 상태 | 설명 |
|---|---|---|
| VPC·Subnet·NAT·보안 그룹 | 완료 | ECS가 실행될 네트워크 준비 완료 |
| ECS Cluster | 완료 | lookddak-dev-v2, Container Insights 활성화 |
| ECS Task Definition | 완료 | Next.js·Spring·FastAPI 최초 Revision 등록 완료, Crawler 제외 |
| ECS Service | 완료 | Next.js·Spring·FastAPI Service 활성화 |
| 실행 중인 ECS Task | 완료 | Next.js·Spring·FastAPI Task 실행 및 안정성 확인 |
| ALB·HTTPS 인증서·Target Group | 완료 | v2-dev.lookddak.com, Next.js·Spring Target Healthy |
| Service Connect | 완료 | Spring 컨테이너에서 http://fastapi:8000 실제 호출 확인 |
| PostgreSQL·MySQL | 런타임 연결 확인 | Spring의 V2 MySQL 연결과 FastAPI의 PostgreSQL 연결 확인 |
| S3·ECR | 부분 완료 | 앱 저장소에는 이미지가 있지만 Crawler 저장소는 비어 있음 |
| SQS·DLQ·알람·IAM | 완료 | 6개 큐와 6개 알람이 AWS에 실제 생성됨 |
| SQS 애플리케이션 흐름 | 대기중 | Producer/Consumer 코드와 관련 이미지가 준비된 뒤 전체 메시지 흐름 검증 필요 |
| Crawler Scheduler | 부분 완료 | 스케줄은 비활성화 상태이며 이미지·Task Definition 등록 및 수동 RunTask 검증 필요 |
| CloudWatch 앱 로그 | 완료 | V2 Log Group, awslogs, 로그 스트림 및 애플리케이션 로그 확인 |
| V2 CI/CD | 미구축 | 앱 빌드부터 ECS 재배포까지 자동화가 없음 |
| Route 53 | 테스트 도메인 완료 | v2-dev.lookddak.com은 V2 ALB, dev.lookddak.com은 V1 EC2 유지 |
| PLG 모니터링 | 미구축 | Prometheus·Loki·Grafana 미구축 |
11. V2 dev ECS 런타임 설정 및 CloudWatch Logs 구성(완료)
-
CloudWatch Log Group 생성
-
V2 전용 로그 그룹을 생성
/lookddak/dev/v2/nextjs /lookddak/dev/v2/spring /lookddak/dev/v2/fastapi /lookddak/dev/v2/crawler*보관 기간 14일
-
-
Task Execution Role에 로그 전송 권한 추가
logs:CreateLogStream logs:PutLogEvents권한 대상은 전체 Log Group이 아니라 위 V2 Log Group ARN으로 제한
-
기존 Task Definition JSON에 로그 설정 추가
"logConfiguration": { "logDriver": "awslogs", "options": { "awslogs-group": "/lookddak/dev/v2/spring", "awslogs-region": "ap-northeast-2", "awslogs-stream-prefix": "ecs" } } -
Spring 런타임 설정 보완
일반 환경변수:
SPRING_PROFILES_ACTIVE AWS_REGION S3_BUCKET_NAME AI_SERVER_URL BE_FITTING_REQUEST_QUEUE_URL BE_FITTING_RESPONSE_QUEUE_URL TZParameter Store에서 주입할 값:
DB_URL DB_USERNAME DB_PASSWORD JWT_SECRET AI_INTERNAL_API_KEY SENTRY_DSNSpring에서 FastAPI를 호출하는 주소는 외부 주소가 아니라 Service Connect 내부 주소를 사용
http://fastapi:8000 -
FastAPI 런타임 설정 보완
일반 환경변수:
AWS_REGION S3_BUCKET_NAME BE_FITTING_REQUEST_QUEUE_URL AI_FITTING_RESPONSE_QUEUE_URL BE_FITTING_RESPONSE_QUEUE_URL TZParameter Store에서 주입할 값:
DATABASE_URL 또는 PostgreSQL 개별 연결 정보 CHECKPOINT_DSN INTERNAL_API_KEY OPENAI_API_KEY RUNWARE 관련 API KEY BODY_IMAGE_VALIDATION_URL BODY_IMAGE_VALIDATION_PROXY_KEY BODY_IMAGE_VALIDATION_PROXY_SECRET BODY_IMAGE_VALIDATION_API_KEY네 개의 BODY IMAGE 값은 앞서 구성한 공유 Parameter Store 경로를 그대로 사용
비밀번호와 API Key는 Task Definition의 일반
environment에 넣지 않고 반드시secrets로 연결 -
Next.js 런타임 설정 보완
- 서버 런타임 환경변수만 Task Definition에 추가
NEXT_PUBLIC_*값은 향후 애플리케이션 CI/CD가 빌드 시 주입하도록 구분/v2base path를 사용하지 않도록 확인- CloudWatch Logs 설정 추가
-
Crawler Task Definition 보완
- CloudWatch Logs 설정
- DB 연결 환경변수와 시크릿
- AWS Region·S3 등 공통 설정
- Task Role·Execution Role 연결
12. V2 ALB 배포 전제 정비(완료)
-
Spring Target Group 헬스 체크를
/actuator/health로 설정 -
Next.js Target Group은 우선
/로 헬스 체크 -
v2-dev.lookddak.com을 V2 ALB에 연결 -
/api/*는 Spring, 나머지는 Next.js로 전달 -
FastAPI는 외부 ALB에 연결하지 않고 Service Connect로만 접근
-
/v2URL prefix는 사용하지 않음 -
V2 검증 완료 전까지
dev.lookddak.com은 V1 유지
13. Task Definition 최초 등록(완료)
- Next.js, Spring, FastAPI의 사용할 ECR 이미지 digest 확인
- 기존 JSON의 placeholder를 실제 digest로 치환
aws ecs register-task-definition으로 최초 revision 등록- Crawler는 제외
14. ECS Service 및 Task 활성화(완료)
- 등록된 Task Definition ARN 입력
create_services = true- 로컬
terraform plan - PR 병합 후 기존 Terraform GitHub Actions로 적용
ECS Service 모듈은 Task Definition 변경을 Terraform이 계속 덮어쓰지 않도록 ignore_changes가 설정되어 있다. 따라서 이후 애플리케이션 CD가 새 Revision으로 서비스를 업데이트해도 충돌하지 않는다.
- Terraform에서 Spring Service의 ECS Exec 활성화
- Spring Task Role에 다음
ssmmessages권한 추가CreateControlChannelCreateDataChannelOpenControlChannelOpenDataChannel
- 적용 후 Spring Task 새로 배포
15. 런타임 검증(완료)
- ECS Task가 재시작 없이 유지되는지
- ALB Target이 Healthy인지
- CloudWatch에 로그 스트림이 생성되는지
- Spring/FastAPI DB 연결 여부
- Service Connect 내부 통신
- S3 및 SQS IAM 권한
애플리케이션에 SQS Producer/Consumer 코드가 아직 반영되지 않았다면 큐 자체와 IAM까지만 검증할 수 있다. 전체 메시지 흐름 검증은 관련 이미지가 나온 뒤 진행한다.
16. 테스트 도메인 연결 및 E2E 확인
v2-dev.lookddak.com으로 V2 테스트- 프론트 → Spring → FastAPI → DB 흐름 확인
V2 dev 인프라 구성을 완료했다. SQS 애플리케이션 메시지 흐름을 제외한 인프라 항목을 검증했다.
테스트 도메인 https://v2-dev.lookddak.com에서 프론트 로그인 화면이 표시되는 것을 확인했다.
현재 확인된 항목
- Next.js, Spring, FastAPI ECS Service 실행
- Next.js와 Spring ALB Target Healthy
- CloudWatch Logs 스트림 생성 및 애플리케이션 로그 확인
- Spring의 V2 MySQL 연결 확인
- FastAPI의 PostgreSQL 연결 확인
- Spring 컨테이너에서
http://fastapi:8000호출 성공- FastAPI가 HTTP 404 응답을 반환해 Service Connect를 통한 실제 통신을 확인 완료
/경로가 FastAPI의 유효 API가 아니라서 404가 나온 것이며, 통신 오류는 아님
- Spring/FastAPI Task Role의 S3·SQS 권한을 정책 및 IAM 시뮬레이션으로 확인
따라서 인프라와 서비스 간 기본 연결은 확인됐다. 실제 기능 E2E 및 SQS 메시지 흐름 검증은 관련 애플리케이션 코드가 준비된 뒤 진행한다. CI/CD 구성은 별도 담당 범위로 제외했다.
17. 최종 컷오버
- 검증 완료 후
dev.lookddak.com을 V1 EC2에서 V2 ALB로 전환 - 문제 발생 시 V1로 되돌릴 절차 준비
18. Crawler 후속 작업
- 실제 이미지와 Task Definition 실행 명령이 준비된 후 Scheduler 및 RunTask 검증