S3 업로드를 감지하여 EC2에서 자동 텍스처 변환
한 줄 요약
S3에 파일이 업로드되면 Lambda가 감지하여 EC2를 시작하고, Docker 컨테이너 안에서 3,561개 타일의 텍스처를 JPEG → KTX2로 변환한 뒤 자동 종료한다. 32 vCPU 인스턴스에서 8 worker 병렬 처리로 약 10분에 완료, 100% 변환 성공률을 달성했다.
왜 Lambda에서 직접 변환하지 않는가
AWS Lambda는 서버리스 함수로, 이벤트에 반응하여 코드를 실행하기에 적합하다. 그러나 텍스처 변환 작업에는 두 가지 근본적 제약이 있다:
시간 제한: Lambda의 최대 실행 시간은 15분이다. 3,561개의 b3dm 파일(2.6GB)을 변환하는 데는 이 시간으로 부족하다.
메모리 제한: Lambda의 최대 메모리는 10GB다. 대용량 3D 모델 파일을 메모리에 올려서 변환하기에는 제약이 크다.
대안 검토: 왜 EC2인가
Lambda에서 직접 변환이 불가능하다면, 별도의 컴퓨팅 리소스가 필요하다. AWS에서 이 역할을 할 수 있는 서비스 세 가지를 검토했다.
| 서비스 | 장점 | 단점 | 우리 상황에서의 판단 |
|---|---|---|---|
| AWS Batch | 다중 인스턴스 자동 오케스트레이션 | 초기 설정 복잡, Job Queue/Compute Environment 구성 필요 | DevOps 인력이 없는 소규모 팀에서 설정·유지 부담이 과도 |
| ECS Fargate | 서버리스, 인스턴스 관리 불필요 | 동일 사양 대비 EC2보다 비용 20~30% 높음 | 변환 빈도가 월 수십 회 수준이라 비용 차이가 누적됨 |
| EC2 | 비용 최저, 사양 자유 선택 | 인스턴스 시작/종료를 직접 관리해야 함 | Lambda가 트리거하므로 관리 부담이 거의 없음 |
EC2를 선택한 핵심 근거는 "높은 사양을 짧게 돌리는 것이 낮은 사양을 오래 돌리는 것보다 총 비용이 적다"는 전략이다. 32 vCPU 인스턴스를 사용하면 CPU 코어 수만큼 병렬 처리가 가능하여, 3,561개 타일을 약 10분 만에 변환할 수 있었다.
다만, 변환 대상이 10만 개 이상으로 증가하면 단일 인스턴스의 한계에 부딪힌다. 그 시점에서는 AWS Batch로의 전환이 필요하다.
Docker 환경 구성
텍스처 변환 파이프라인은 여러 도구의 조합으로 동작한다:
- gltf-transform CLI (v4): glTF/glb 파일의 텍스처를 KTX2로 변환하는 Node.js 기반 도구
- KTX-Software (4.4.2): Khronos Group이 관리하는 공식 KTX2 인코더/디코더. 네이티브 바이너리
이 도구들의 실행 환경 의존성을 Docker 컨테이너로 패키징했다.
베이스 이미지: node:20-slim (~200MB)
+ gltf-transform CLI v4 (npm)
+ KTX-Software 4.4.2 (네이티브 바이너리)node:20-slim을 선택한 이유: 세 가지 변형을 검토했다.
| 이미지 | 크기 | 특징 | 판단 |
|---|---|---|---|
| node:20 | ~900MB | 전체 Debian OS | 불필요한 패키지 포함, 크기 과대 |
| node:20-slim | ~200MB | 최소 Debian | glibc 호환 유지 + 크기 최소화 |
| node:20-alpine | ~130MB | Alpine Linux | musl libc 사용, KTX-Software 네이티브 바이너리 호환 문제 |
KTX-Software의 네이티브 바이너리가 glibc에 의존하므로, Alpine의 musl libc와 호환성 문제가 발생할 수 있었다. slim은 glibc 호환성을 유지하면서 크기를 최소화하는 균형점이었다.
아키텍처 구조
업로드 담당자: zip 파일을 S3에 업로드 (기존 워크플로우 변경 없음)
↓
S3: 업로드 이벤트 발생
↓
Lambda: S3 이벤트 감지 → EC2 인스턴스 시작 (트리거 역할만)
↓
EC2 (32 vCPU):
1. S3에서 zip 다운로드
2. 압축 해제
3. Docker 컨테이너 내에서 JPEG → KTX2 변환 (8 worker 병렬)
4. 변환 결과를 S3에 업로드
5. 자동 종료
설계 원칙 세 가지:
Lambda는 트리거만 한다: Lambda의 코드는 EC2 인스턴스를 시작하는 API 호출 한 줄이다. 실행 시간은 수 초이므로 비용이 거의 발생하지 않는다.
업로드 담당자의 워크플로우는 변경되지 않는다: 담당자는 이전과 동일하게 zip 파일을 S3에 업로드하기만 하면 된다.
EC2는 작업 완료 후 자동 종료된다: 유휴 상태에서 비용이 발생하지 않는다. 사실상 서버리스에 가까운 비용 구조다.
파이프라인 처리 결과
| 지표 | 값 |
|---|---|
| 변환 대상 | 3,561개 b3dm 파일 (2.6GB) |
| 인스턴스 사양 | 32 vCPU |
| 병렬 워커 수 | 8 |
| 총 변환 시간 | 약 10분 |
| 변환 성공률 | 100% |
비용 구조:
- Lambda: 월 수백 회 트리거 수준에서 프리 티어 범위 내
- EC2: 변환 1회당 고사양 인스턴스 10분 사용 비용만 발생
- S3: 저장 및 전송 비용
더 큰 규모에서는?
현재: 3,561개 타일, 단일 EC2(32 vCPU), 약 10분.
만약 타일이 10만 개 이상이라면:
- EC2 단일 인스턴스 → AWS Batch (다중 인스턴스 오케스트레이션)
- S3 이벤트 폭주 → SQS 큐 중간 배치 (Lambda 동시 실행 1,000개 제한 우회)
- 비용 구조 전환 → 스팟 인스턴스 (최대 90% 절감, 중단 허용 배치 작업에 적합)
이 확장 경로를 미리 인식하고 있되, 현재 규모에서는 단일 EC2가 가장 단순하고 비용 효율적인 선택이다.
이 경험에서 추출한 원칙
-
"서버리스의 한계"가 곧 "서버가 필요한 이유"다. Lambda의 시간·메모리 제한은 설계상 의도된 것이다. Lambda를 억지로 쓰는 것보다, Lambda는 트리거로만 쓰고 실제 작업은 적합한 도구(EC2)에 맡기는 것이 올바른 아키텍처다.
-
고사양 × 짧은 시간이 저사양 × 긴 시간보다 싸다. 클라우드 비용은 (인스턴스 단가 × 실행 시간)이다. CPU 병렬 처리가 가능한 작업에서는 코어 수를 늘리는 것이 거의 선형적으로 시간을 줄여준다.
-
"환경을 코드로 관리한다"가 Docker의 본질이다. Dockerfile 하나에 모든 환경 설정이 선언적으로 정의된다. 서버가 바뀌어도 동일한 환경이 재현된다.
S3 업로드 이벤트를 Lambda로 트리거하는 것은 AWS 콘솔에서 10분이면 설정할 수 있다. 반복 작업을 자동화할 기회가 있다면 시도해 보라.
S3 업로드를 감지하여 EC2에서 자동 텍스처 변환
데이터 파이프라인