Dev Environment Setup
이 문서는 surfai-vivid의 dev 환경 구축 기준을 정리합니다.
확정 기준
| 항목 | 값 |
|---|---|
| AWS account | local과 같은 계정 |
| Region | ap-northeast-2 |
| Domain | dev.surfai.org |
| Runtime | EC2 1대 + Docker Compose |
| EC2 instance | t3.medium부터 시작 |
| Database | EC2 내부 PostgreSQL container |
| Redis | EC2 내부 Redis container |
| Queue | SQS |
| Storage | S3 private media bucket |
초기 dev는 비용을 우선하여 backend, frontend, PostgreSQL, Redis, ComfyUI, ai-node-agent, post-processing-worker를 같은 EC2에서 Docker Compose로 실행합니다. SQS/S3만 AWS managed service로 둡니다.
dev.surfai.org
-> nginx
-> frontend container
-> backend container
-> postgres container
-> redis container
-> SQS computation queue
-> S3 media bucket
-> ai-node-agent + ComfyUI
-> post-processing-worker
AWS 리소스
S3
surfai-vivid-dev-media
Bucket 설정은 local media bucket과 동일하게 둡니다.
| 설정 | 값 |
|---|---|
| Region | ap-northeast-2 |
| Block Public Access | 4개 항목 모두 ON |
| Object Ownership | Bucket owner enforced |
| Versioning | OFF로 시작 |
| Encryption | SSE-S3 |
Prefix 구조:
raw/
temp/
uploads/original/
generation/source/
generation/work-input/
creations/
post-processing/failed/
exports/user-downloads/
초기 CORS:
[
{
"AllowedOrigins": [
"http://localhost:4000",
"https://dev.surfai.org"
],
"AllowedMethods": ["GET", "PUT", "POST", "HEAD"],
"AllowedHeaders": ["*"],
"ExposeHeaders": [
"ETag",
"x-amz-request-id",
"x-amz-id-2",
"x-amz-checksum-crc32"
],
"MaxAgeSeconds": 3000
}
]
Lifecycle 기준은 S3 media bucket 설정을 따릅니다.
SQS
다음 queue를 생성합니다.
surfai-computation-queue-dev
surfai-computation-queue-dev-dlq
surfai-post-processing-worker-queue-dev
surfai-post-processing-worker-queue-dev-dlq
surfai-email-dev
surfai-email-dev-dlq
권장값:
| 항목 | 값 |
|---|---|
| Type | Standard |
| Receive message wait time | 20초 |
| Visibility timeout | generation/post-processing은 900초부터 시작 |
| DLQ maxReceiveCount | 3 |
ai-node-agent와 post-processing-worker가 처리 중 timeout보다 오래 걸릴 수 있으므로 visibility timeout은 짧게 잡지 않습니다.
PostgreSQL
dev는 DB 초기화가 허용되는 환경이므로 초기에는 EC2 내부 PostgreSQL container를 사용합니다.
| 항목 | 값 |
|---|---|
| Engine | PostgreSQL 16 container |
| Network | Docker internal network |
| Port | host 외부 공개 금지 |
| Volume | named volume 또는 EBS 경로 bind mount |
| Backup | 필수 아님. 필요 시 pg_dump cron 추가 |
dev 데이터를 보존해야 하는 요구가 생기면 RDS surfai-vivid-dev-postgres로 분리합니다.
Redis
dev는 EC2 내부 Redis container를 사용합니다.
| 항목 | 값 |
|---|---|
| Engine | Redis 7 Alpine container |
| Network | Docker internal network |
| Port | host 외부 공개 금지 |
| Persistence | AOF on |
dev Redis 상태는 재구성 가능해야 합니다. 장기 보존이 필요한 상태를 Redis에만 두지 않습니다.
EC2
surfai-vivid-dev-app
권장 초기값:
| 항목 | 값 |
|---|---|
| AMI | Ubuntu 22.04 LTS 또는 24.04 LTS |
| Instance | t3.medium |
| EBS | gp3 50GB |
| IAM | EC2 instance profile 사용 |
| Public IP | Elastic IP 할당 권장 |
Security group:
| Port | Source | 용도 |
|---|---|---|
| 22 | 관리자 IP | SSH |
| 80 | 0.0.0.0/0 | HTTP challenge/redirect |
| 443 | 0.0.0.0/0 | HTTPS |
다음 포트는 외부 공개하지 않습니다.
3000 backend
4000 frontend
8188 ComfyUI
5432 PostgreSQL container
6379 Redis container
IAM Role
dev EC2에는 access key 대신 IAM role을 붙이는 것을 기본으로 합니다.
예시 이름:
surfai-vivid-dev-ec2-role
필요 권한:
S3:
- s3:GetObject
- s3:PutObject
- s3:DeleteObject
- s3:ListBucket
SQS:
- sqs:SendMessage
- sqs:SendMessageBatch
- sqs:ReceiveMessage
- sqs:DeleteMessage
- sqs:ChangeMessageVisibility
- sqs:GetQueueAttributes
SES:
- ses:SendEmail
- ses:SendRawEmail
권한 범위는 dev bucket과 dev queue ARN으로 제한합니다. 같은 AWS 계정을 쓰더라도 local/prod 리소스까지 열지 않습니다.
SES 발송은 verified identity의 From 주소로 제한합니다.
{
"Sid": "DevSesSend",
"Effect": "Allow",
"Action": [
"ses:SendEmail",
"ses:SendRawEmail"
],
"Resource": "*",
"Condition": {
"StringEquals": {
"ses:FromAddress": "noreply@surfai.org"
}
}
}
SES sandbox 상태에서는 dev에서도 verified recipient에게만 발송됩니다. 일반 사용자 대상 발송은 SES production access 승인 후 활성화합니다.
Domain And Reverse Proxy
초기 dev는 별도 API subdomain을 두지 않고 dev.surfai.org 단일 origin으로 운영합니다.
https://dev.surfai.org/ -> frontend
https://dev.surfai.org/api -> backend API
https://dev.surfai.org/api/socket.io/ -> backend websocket
장점:
- frontend CORS 구성이 단순합니다.
- cookie 인증 정책이 단순합니다.
NEXT_PUBLIC_API_URL=https://dev.surfai.org/api하나로 충분합니다.
Route53:
dev.surfai.org A record -> dev EC2 Elastic IP
EC2 host에 nginx를 설치하고 TLS는 certbot으로 발급합니다. ALB/ACM을 붙이는 방식도 가능하지만 초기 dev에는 비용과 운영 복잡도가 더 큽니다.
nginx proxy 기준:
server {
listen 80;
server_name dev.surfai.org;
return 301 https://$host$request_uri;
}
server {
listen 443 ssl http2;
server_name dev.surfai.org;
location /api/socket.io/ {
proxy_pass http://127.0.0.1:3000;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $host;
proxy_set_header X-Forwarded-Proto $scheme;
}
location /api/v1/agent-relay/ws {
proxy_pass http://127.0.0.1:3000;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $host;
proxy_set_header X-Forwarded-Proto $scheme;
}
location /api/ {
proxy_pass http://127.0.0.1:3000;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-Proto $scheme;
}
location /api-docs {
proxy_pass http://127.0.0.1:3000;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-Proto $scheme;
}
location / {
proxy_pass http://127.0.0.1:4000;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-Proto $scheme;
}
}
Environment Variables
backend
NODE_ENV=development
PORT=3000
POSTGRES_HOST=postgres
POSTGRES_PORT=5432
POSTGRES_USER=<dev-db-user>
POSTGRES_PASSWORD=<dev-db-password>
POSTGRES_DB=<dev-db-name>
POSTGRES_SSL_ENABLED=false
REDIS_HOST=redis
REDIS_PORT=6379
REDIS_TLS_ENABLED=false
AWS_REGION=ap-northeast-2
SQS_COMPUTATION_QUEUE_URL=https://sqs.ap-northeast-2.amazonaws.com/<account-id>/surfai-computation-queue-dev
SQS_POST_PROCESSING_QUEUE_URL=https://sqs.ap-northeast-2.amazonaws.com/<account-id>/surfai-post-processing-worker-queue-dev
SQS_EMAIL_QUEUE_URL=https://sqs.ap-northeast-2.amazonaws.com/<account-id>/surfai-email-dev
SQS_EMAIL_DLQ_URL=https://sqs.ap-northeast-2.amazonaws.com/<account-id>/surfai-email-dev-dlq
S3_TEMP_BUCKET_NAME=surfai-vivid-dev-media
S3_PERMANENT_BUCKET_NAME=surfai-vivid-dev-media
EMAIL_FROM=noreply@surfai.org
EMAIL_DELIVERY_MODE=ses
EMAIL_DISPATCH_INTERVAL_MS=3000
EMAIL_WORKER_POLL_INTERVAL_MS=5000
EMAIL_DISPATCH_BATCH_SIZE=10
EMAIL_DISPATCH_MAX_BATCHES_PER_TICK=5
EMAIL_DISPATCH_LEASE_MS=60000
# 설정한 SES configuration set이 있을 때만 추가한다.
# SES_CONFIGURATION_SET_NAME=surfai-dev-email
CORS_ORIGIN=https://dev.surfai.org
PUBLIC_FRONTEND_URL=https://dev.surfai.org
SECURE_COOKIES=true
EC2 IAM role을 사용할 경우 AWS_ACCESS_KEY_ID와 AWS_SECRET_ACCESS_KEY는 설정하지 않습니다.
frontend
Docker build arg로 주입합니다.
NEXT_PUBLIC_APP_ENV=dev
NEXT_PUBLIC_API_URL=https://dev.surfai.org/api
Next standalone 서버는 dev EC2에서 PORT=4000으로 실행합니다. backend도 3000을 사용하므로 frontend를 4000으로 분리합니다.
ai-server
AI_AGENT_ENV=dev
AWS_REGION=ap-northeast-2
SQS_COMPUTATION_QUEUE_DEV=https://sqs.ap-northeast-2.amazonaws.com/<account-id>/surfai-computation-queue-dev
SQS_POST_PROCESSING_QUEUE_DEV=https://sqs.ap-northeast-2.amazonaws.com/<account-id>/surfai-post-processing-worker-queue-dev
S3_TEMP_BUCKET_DEV_NAME=surfai-vivid-dev-media
S3_SYSTEM_CONFIGURATION_BUCKET_NAME=<dev-system-configuration-bucket>
COMFY_API_URL=http://127.0.0.1:8188/prompt
COMFY_WS_URL=ws://127.0.0.1:8188/ws
COMFY_OUTPUT_DIR=/app/temp
COMFY_HTTP_TIMEOUT_SECONDS=15
COMFY_ORG_API_KEY=<server-only>
COMFY_USAGE_SOURCE=surfai-vivid-dev
AI_AGENT_RELAY_HTTP_URL=https://dev.surfai.org/api/v1/agent-relay
AI_AGENT_RELAY_WS_URL=wss://dev.surfai.org/api/v1/agent-relay/ws
AI_AGENT_RELAY_TOKEN=<dev-relay-token>
NODE_ID=dev-ai-node-01
post-processing-worker
TARGET_ENV=dev
AWS_REGION=ap-northeast-2
SQS_POST_PROCESSING_QUEUE_DEV=https://sqs.ap-northeast-2.amazonaws.com/<account-id>/surfai-post-processing-worker-queue-dev
S3_TEMP_BUCKET_DEV_NAME=surfai-vivid-dev-media
S3_PERMANENT_BUCKET_DEV_NAME=surfai-vivid-dev-media
BACKEND_API_URL=http://127.0.0.1:3000/api
POST_PROCESSING_MAX_WORKERS=2
SQS_VISIBILITY_TIMEOUT_SECONDS=900
FFMPEG_TIMEOUT_SECONDS=300
Docker Compose 배치 원칙
현재 각 repository에 compose 파일이 나뉘어 있으므로 dev EC2에서는 다음 중 하나를 선택합니다.
- 서버 배포용 통합
docker-compose.dev.yml을 별도로 만든다. - 각 repository 디렉터리에서 개별 compose/build/run 스크립트를 실행한다.
초기 운영은 1번이 낫습니다. 단일 EC2에서 컨테이너 생명주기를 한 번에 볼 수 있기 때문입니다.
주의:
- backend와 frontend 모두 기본 포트가 3000입니다. frontend container는
PORT=4000으로 실행합니다. - PostgreSQL과 Redis는 같은 compose network 안에서만 접근하게 하고 외부 공개하지 않습니다.
- ComfyUI의 8188은 외부 공개하지 않습니다.
ai-node-agent와 ComfyUI는 input/output/temp volume을 공유해야 합니다.post-processing-worker는 backend API에 내부 주소로 접근합니다.
GitHub Actions 자동 배포
dev는 root repository가 아니라 각 서브모듈 repository의 development 브랜치 push를 기준으로 자동 배포합니다.
backend-vivid-ai development push
-> EC2 backend-vivid-ai 갱신
-> docker compose build backend
-> docker compose up -d --no-deps --force-recreate backend
frontend-vivid-ai development push
-> EC2 frontend-vivid-ai 갱신
-> docker compose build frontend
-> docker compose up -d --no-deps --force-recreate frontend
post-processing-worker development push
-> EC2 post-processing-worker 갱신
-> docker compose build post-processing-worker
-> docker compose up -d --no-deps --force-recreate post-processing-worker
ai-server development push
-> EC2 ai-server 갱신
-> docker compose build ai-node-agent
-> docker compose up -d --no-deps --force-recreate ai-node-agent
ComfyUI container는 build 시간이 길고 모델/output/input 디렉터리 영향이 크므로 기본 자동 배포에서는 rebuild하지 않습니다. ComfyUI Dockerfile 또는 custom node 변경을 dev에 반영해야 할 때는 ai-server repository의 Deploy AI Server to Dev EC2 workflow를 수동 실행하면서 rebuild_comfyui=true를 선택합니다.
각 서브모듈 repository에 동일하게 설정할 GitHub secret:
DEV_EC2_HOST=<dev EC2 public IP 또는 dev.surfai.org>
DEV_EC2_SSH_KEY=<EC2 접속용 private key>
DEV_EC2_KNOWN_HOSTS=<선택, ssh-keyscan 결과>
각 서브모듈 repository에 동일하게 설정할 GitHub variable:
DEV_APP_DIR=/srv/surfai/vivid-ai
DEV_EC2_USER=ubuntu
DEV_EC2_PORT=22
DEV_EC2_KNOWN_HOSTS를 엄격하게 설정하려면 로컬에서 다음 값을 복사해 secret에 넣습니다.
ssh-keyscan -p 22 dev.surfai.org
EC2 전제 조건:
/srv/surfai/vivid-ai 에 root repository clone 완료
.env.dev 파일 배치 완료
각 서브모듈이 development 브랜치로 checkout 가능
EC2의 GitHub SSH key가 root repo와 모든 private submodule에 접근 가능
Docker/Compose 설치 완료
ubuntu 사용자가 docker 그룹 소속
각 workflow는 EC2에서 /tmp/surfai-dev-deploy.lock을 사용해 동시 배포를 직렬화합니다. backend와 frontend push가 동시에 발생해도 EC2에서는 한 번에 하나의 compose 작업만 실행됩니다.
구축 순서
- S3 dev media bucket 생성 및 CORS/lifecycle 설정
- SQS dev queue/DLQ 생성
- EC2
t3.medium생성, IAM role 연결 - Route53
dev.surfai.org를 EC2 Elastic IP로 연결 - EC2에 Docker, Docker Compose, nginx, certbot 설치
- 통합 Docker Compose에 PostgreSQL/Redis/backend/frontend/ai-server/post-processing-worker 구성
.env파일 작성- backend migration 실행
- 컨테이너 실행
- nginx TLS 설정
- smoke test 수행
Smoke Test
순서대로 확인합니다.
1. https://dev.surfai.org 접속
2. https://dev.surfai.org/api/health 또는 backend health endpoint 확인
3. backend -> PostgreSQL container 연결 확인
4. backend -> Redis container 연결 확인
5. backend -> SQS SendMessage 확인
6. ai-node-agent -> SQS ReceiveMessage 확인
7. ai-node-agent -> ComfyUI prompt 호출 확인
8. ai-node-agent -> S3 raw/ 업로드 확인
9. post-processing-worker -> SQS receive 확인
10. post-processing-worker -> S3 creations/ 업로드 확인
11. backend creation result update 확인
12. frontend realtime progress 확인
운영 기준
- dev도 local/prod와 queue, bucket, DB를 반드시 분리합니다.
- dev credential 또는 EC2 role은 prod resource에 접근하지 못해야 합니다.
- dev PostgreSQL/Redis는 EC2 내부 container로 두며, 데이터 초기화 가능성을 전제로 운영합니다.
- Comfy API node concurrency limit을 고려해 dev
ai-node-agent는 초기에 1 job씩 처리합니다. t3.medium에서 memory pressure가 보이면t3.large로 올립니다.- EC2 하나에서 감당하기 어려워지는 시점까지 dev는 Kubernetes를 사용하지 않습니다.