스팟 플릿 요청이 아닌 인스턴스 생성의 스팟 인스턴스 유형이다.

우리가 쓰려는 SPOT 인스턴스는 일반 EC2 인스턴스를 SPOT 가격으로 실행하는 것이다.
SPOT 플릿 요청은 여러 Spot 인스턴스를 묶어서 목표 용량을 유지하기 위한 것이라고 한다.
차이는 다음과 같다.
| 항목 | Spot 인스턴스 | Spot Fleet |
| 규모 | 1대 | 다수 |
| 기동 방식 | 수동 | 자동 |
| 종료 | 수동 | 목표 용량 유지 |
| 복잡도 | 낮음 | 높음 |
| 제어권 | 사용자 | AWS |
| 적합한 용도 | 단일 GPU 워커 | 대규모 배치 |
우린 필요할때만 껐다 키는 용도기 때문에 SPOT 인스턴스가 맞다.
SPOT 인스턴스 생성

GPU AMI를 골라준다.

인스턴스 유형은 가장 저렴한 걸로..

기존 ec2에 쓰는 VPC를 고르고, 퍼블릿 서브넷을 고른다.
프라이빗으로 하지 않은 이유는
프라이빗으로 하면 SQS S3 접근이 불가해 NAT Gateway가 필요한데
그럼 GPU를 쓰지 않고 있는 상황에서도 돈이 계속 나간다. GPU를 SPOT 인스턴스로 호출해 금액이 저렴해지지만 얼마 나올지 아직 모르기 때문에... 돈 지출을 최소화시키고 싶었다.
대신 보안그룹 설정에서 인바운드 규칙을 0개로 놔두고 아웃바운드만 허용해 외부에서 접근 불가하게 만들었다.
이게 무슨 소리냐면!
인바운드는 외부 -> EC2로 들어오는 트래픽이다. (외부에서 먼저 말을 거는 경우)
예) 외부 사용자가 서버에 HTTP 요청
아웃바운드는 EC2 -> 외부로 나가는 트래픽이다. (서버가 먼저 요청을 보내는 경우)
예) EC2가 SQS 호출, S3 업로드
그럼 EC2 -> SQS 아웃바운드 규칙으로 요청을 보내면, SQS -> EC2로 응답을 해야하는데 인바운드 규칙이 없으니
응답이 막히는 게 아닌가?
아니다. 그 이유는 AWS 보안그룹의 Stateful 방화벽 때문이다.
내가 먼저 보낸 요청(outbound)에 대한 응답(inbound)은 인바운드 규칙이 없어도 자동으로 허용된다고 한다.
즉 실제 동작은
- EC2 -> SQS 요청
- SQS -> EC2 응답 반환
- 보안그룹: 엇 아까 요청한 연결의 응답이네 허용
이렇게 되는 것이다.
그럼 인바운드 규칙이 없으니 (외부에서 접근 X) 퍼블릭 IP가 필요한가?
필요하다!!
EC2가 외부로 나가기 위해서도 퍼블릭 IP가 필요하다고 한다.
정확히는 NAT Gateway(+프라이빗 서브넷 조합) or 퍼블릭 IP인데 전자는 제외했으니...
또 퍼블릭 IP가 존재한다고 외부에서 접근할 수 있는 게 아니다. 인바운드 규칙이 없으니 외부에서 접근이 불가능하다.

최대한 프리티어 안에서 해결하고 싶어서 gp2 30gb 조합으로 선택했다.
고급 설정

설정을 완료하고 인스턴스 시작을 눌렀더니
Max spot instance count exceeded
오류가 떴다.
원인은 기본적으로 계정에 SPOT 인스턴스 수 한도가 0으로 잡혀있는 것이였다.

요청하고 기다린다.
보통 몇 시간 걸리는진 모르겠으나 미국 현지 시간에 일을 해주는 것인지 (?) 3시간 째 기다리는 중...
거의 9시간 정도 걸린듯!
IAM 권한 설정
자동으로 인스턴스 만들때 필요한 권한을 부여했다.

사용자가 아닌 역할로 만들어줘야한다

User data 생성
User Data란?
User Data는 EC2가 처음 부팅될 때 root 권한으로 실행되는 스크립트이다.
GPU Spot 구조에서 User Data의 역할
Spot 인스턴스는 언제 죽을지 모르고, 사람이 SSH로 들어가 설정할 수 없으므로
따라서 부팅만 하면 자동으로 worker가 살아나야 한다.
User Data에서 실제로 하는 일
GPU 워커 기준으로 보면 보통 다음 순서입니다.
- (필요 시) ECR 로그인
- 최신 worker-gpu 이미지 pull
- GPU 옵션으로 컨테이너 실행
docker run --gpus all \
-e AWS_REGION=...
-e AWS_SQS_QUEUE_URL=...
-e AWS_S3_BUCKET=...
-e JOB_API_BASE_URL=...
-e WORKER_TOKEN=...
worker-gpu:latest
여기서 설정되는 환경 변수들이 worker의 실행 컨텍스트 전체를 결정한다.
#!/bin/bash
set -e
# === awscli 설치 (Ubuntu) ===
apt-get update -y
apt-get install -y awscli
# === SSM 파라미터 로드 ===
PARAM_PATH="만들어둔 경로"
export AWS_REGION="$(aws ssm get-parameter --name "$PARAM_PATH/AWS_REGION" --with-decryption --query 'Parameter.Value' --output text)"
export AWS_SQS_QUEUE_URL="$(aws ssm get-parameter --name "$PARAM_PATH/AWS_SQS_QUEUE_URL" --with-decryption --query 'Parameter.Value' --output text)"
export AWS_S3_BUCKET="$(aws ssm get-parameter --name "$PARAM_PATH/AWS_S3_BUCKET" --with-decryption --query 'Parameter.Value' --output text)"
export JOB_API_BASE_URL="$(aws ssm get-parameter --name "$PARAM_PATH/JOB_API_BASE_URL" --with-decryption --query 'Parameter.Value' --output text)"
export WORKER_TOKEN="$(aws ssm get-parameter --name "$PARAM_PATH/WORKER_TOKEN" --with-decryption --query 'Parameter.Value' --output text)"
export OPENAI_API_KEY="$(aws ssm get-parameter --name "$PARAM_PATH/OPENAI_API_KEY" --with-decryption --query 'Parameter.Value' --output text)"
export GEMINI_API_KEY="$(aws ssm get-parameter --name "$PARAM_PATH/GEMINI_API_KEY" --with-decryption --query 'Parameter.Value' --output text)"
export ECR_AWS_ACCOUNT_ID="$(aws ssm get-parameter --name "$PARAM_PATH/ECR_AWS_ACCOUNT_ID" --with-decryption --query 'Parameter.Value' --output text)"
export ECR_REPOSITORY="$(aws ssm get-parameter --name "$PARAM_PATH/ECR_REPOSITORY" --with-decryption --query 'Parameter.Value' --output text)"
export ECR_IMAGE_TAG="$(aws ssm get-parameter --name "$PARAM_PATH/ECR_IMAGE_TAG" --with-decryption --query 'Parameter.Value' --output text)"
# === ECR 로그인 ===
aws ecr get-login-password --region "$AWS_REGION" \
| docker login --username AWS --password-stdin "${ECR_AWS_ACCOUNT_ID}.dkr.ecr.${AWS_REGION}.amazonaws.com"
# === 이미지 pull ===
IMAGE="${ECR_AWS_ACCOUNT_ID}.dkr.ecr.${AWS_REGION}.amazonaws.com/${ECR_REPOSITORY}:${ECR_IMAGE_TAG}"
docker pull "$IMAGE"
# === 컨테이너 실행 ===
docker run -d --restart=always --gpus all \
-e AWS_REGION \
-e AWS_SQS_QUEUE_URL \
-e AWS_S3_BUCKET \
-e JOB_API_BASE_URL \
-e WORKER_TOKEN \
-e OPENAI_API_KEY \
-e GEMINI_API_KEY \
"$IMAGE"
새로 Spot 인스턴스를 만들때마다 .env를 넣어줄 수 없으니... SSM으로 애플리케이션 설정 값을 주입해주었다.
SSM이란 애플리케이션 설정값을 키–값 형태로 저장하는 관리형 서비스이다.
Launch Template
Launch Template란?
Amazon EC2의 Launch Template는 EC2 인스턴스를 만들 때 필요한 설정을 미리 고정해 둔 템플릿이다.
Launch Template에 들어가는 것들
- AMI ID
- 인스턴스 타입
- 서브넷 / 보안 그룹
- IAM Role
- User Data 스크립트
- 태그 (Role=worker-gpu 등)
한 번 정해두면 API 서버는 세부 옵션을 몰라도 된다.

이렇게 만들어둔다.
'프로젝트 > 원스어폰타임' 카테고리의 다른 글
| 온프레미스 -> AWS 마이그레이션 시작 (1) (1) | 2026.01.10 |
|---|