확장
셀프 호스팅 Nodaro를 컨테이너 하나 이상으로 확장하세요. API, 미디어 워커, 렌더링 워커, 오케스트레이터를 분리하고 동시 실행, Redis, 스토리지를 조정합니다.
셀프 호스팅 Nodaro를 확장한다는 것은 그 프로세스를 여러 컨테이너에서 실행한다는 뜻입니다. 기본 Compose 구성은 API, 워커, 오케스트레이터, 웹 서버를 모두 컨테이너 하나에서 실행합니다. 활성 사용자 약 5명까지는 이것으로 충분합니다. 그보다 많으면 워커를 별도 컨테이너에서 실행하세요. 워커들은 Redis 하나, 데이터베이스 하나, 스토리지 버킷 하나를 공유하며, Redis의 큐로만 조정합니다.
프로세스
이미지의 시작 스크립트 /app/start.sh는 /app/backend에서 다음 프로세스를 나란히 시작합니다.
| 명령 | 역할 | 부하 |
|---|---|---|
node dist/server.js | HTTP API | 낮은 CPU, 보통 수준의 메모리 |
node dist/worker.js | 미디어 워커: 노드마다 작업 하나를 맡아 모델 공급자를 호출합니다. | 네트워크를 기다리며, 많은 작업을 동시에 실행합니다. |
node dist/render-worker.js | 렌더러: 헤드리스 Chrome에서 컴포지션을 렌더링합니다. | CPU에 따라 제한됩니다. 머신당 1~2개. |
node dist/orchestrator.js | 워크플로 오케스트레이터: 각 워크플로의 그래프를 실행합니다. | 네트워크를 기다리며, CPU 사용량이 낮습니다. |
node dist/pipeline-worker.js | Nodaro Cloud에서만 실행되는 스토리 → 동영상 파이프라인 | 셀프 호스팅 에디션에서는 바로 종료됩니다. |
시작 스크립트는 단일 프로세스를 실행하는 컨테이너가 하지 않는 일 네 가지도 합니다.
- API 앞에서 포트
3000의 웹 서버를 실행합니다. - 번들된 스택에서 마이그레이션을 적용합니다.
INTERNAL_ORCHESTRATOR_SECRET이 설정되지 않았으면 생성합니다.- 번들된 스택에서 암호화 키를 생성합니다.
분리하기 전에 이미지 안의 스크립트를 읽어 보세요.
일반적인 분리 구성
- API 컨테이너 1개:
server.js를 실행합니다. - 미디어 워커 컨테이너 여러 개: 각각 기본값인
VIDEO_WORKER_CONCURRENCY=50으로 충분합니다. - 렌더링 워커 컨테이너 1~2개: 각각 별도 머신에서 실행합니다.
- 오케스트레이터 컨테이너 1개.
컨테이너끼리는 절대 직접 통신하지 않습니다. 모두 같은 Redis, Supabase, 스토리지를 쓰며, Redis가 유일한 조정 지점입니다.
모든 컨테이너가 공유해야 하는 것
| 변수 | 같아야 하는 이유 |
|---|---|
INTERNAL_ORCHESTRATOR_SECRET | 오케스트레이터가 이 값으로 API에 인증합니다. 설정되지 않았으면 시작 스크립트가 컨테이너마다 새 값을 생성하므로, 모든 곳에 같은 값을 명시적으로 설정하세요. |
NODARO_ENCRYPTION_KEY | 저장된 공급자 키와 자격 증명이 이 키로 암호화됩니다. 다른 키로는 읽을 수 없습니다. |
RUNTIME_ENV | 설치의 이름입니다. 한 설치의 모든 컨테이너가 같은 값을 써야 합니다. |
EDITION, SUPABASE_URL, SUPABASE_SERVICE_ROLE_KEY, REDIS_URL, R2_* 변수 | 모든 컨테이너에서 에디션, 데이터베이스, 큐, 스토리지가 같아야 합니다. |
동시 실행
| 변수 | 기본값 | 제한하는 것 |
|---|---|---|
MAX_CONCURRENT_NODES_PER_EXECUTION | 6, 최대 20 | 워크플로 실행 하나가 동시에 실행할 수 있는 노드 수입니다. 서버 전체의 병렬 노드 상한입니다. |
VIDEO_WORKER_CONCURRENCY | 50 | 미디어 워커 하나가 동시에 처리하는 작업 수 |
ORCHESTRATOR_CONCURRENCY | 20 | 오케스트레이터 하나가 동시에 처리하는 워크플로 작업 수 |
RENDER_WORKER_CONCURRENCY | 2, 최대 10 | 렌더링 워커 하나가 동시에 처리하는 렌더링 수입니다. 각 렌더링이 헤드리스 Chrome 하나입니다. |
REMOTION_CONCURRENCY | 3D 장면은 2, 다른 렌더링은 CPU 코어 수의 절반 | 렌더링 하나당 브라우저 탭 수입니다. 3D 작업 여러 개가 실행될 때는 낮게 유지하세요. WebGL 탭마다 스레드가 늘어나고 컨테이너의 프로세스 한도에 포함됩니다. |
FFMPEG_CONCURRENCY | 4, 최대 32 | 모든 동영상 및 오디오 편집 노드에서 동시에 실행되는 ffmpeg 프로세스 수 |
Compose 파일은 .env에서 이 변수들을 전달하지 않습니다. 필요한 각 서비스의 environment: 아래에 추가하세요.
실행의 각 노드는 최대 90분, 실행 전체는 최대 120분까지 걸릴 수 있습니다. 자체 시간 예산을 선언한 노드는 대신 그 예산을 받고, 실행의 한도도 같은 만큼 늘어납니다. EDL 적용이 그런 노드입니다. 예산은 렌더링하는 편집 내용에 따라 달라집니다.
Redis 고가용성
작업 큐는 Redis 클러스터 모드를 지원합니다. REDIS_URL을 클러스터 엔드포인트나 Sentinel URL로 설정하세요.
API는 큐 외에도 Redis에 작은 공유 캐시를 둡니다. 그래서 여러 API 컨테이너가 같은 느린 공급자 작업을 각자 반복하지 않습니다. 현재는 약 4MB 크기의 HeyGen 아바타 및 보이스 카탈로그가 여기에 해당합니다. 컨테이너 하나가 잠금을 걸고 HEYGEN_CATALOG_REFRESH_HOURS마다(기본값 24) 이를 새로 고치며, 다른 컨테이너는 30초쯤 안에 새 사본을 받아 씁니다. 여기에 있는 것은 모두 캐시입니다. Redis에 연결할 수 없으면 각 컨테이너가 자체 메모리를 쓰고, 잃어버린 항목은 다음 부팅 때 공급자에서 다시 채워집니다.
설치 두 개, 데이터베이스 하나
스테이징 사본 같은 두 번째 설치를 같은 Supabase 프로젝트에 연결하고 별도의 Redis를 쓰게 할 수 있습니다. 이때 설치마다 다른 RUNTIME_ENV를 지정하세요. Railway에서는 RAILWAY_ENVIRONMENT_NAME이 이미 이 역할을 합니다.
각 실행에는 그 실행을 가져간 오케스트레이터가 속한 설치의 이름이 기록되며, 각 설치의 정리 작업은 자기 실행만 확인합니다. 이름이 구분되지 않으면 각 설치가 자기 Redis에서 다른 설치의 작업을 찾다가 찾지 못하고, 정상인 실행을 Execution orphaned로 실패 처리합니다. 설치가 이름을 기록하기 전에 시작된 실행에는 이름이 없으며, 이름이 production인 설치가 이를 처리합니다.
스토리지 수명 주기
Nodaro는 저장된 미디어를 스스로 삭제하지 않습니다. 키로 파일을 참조할 뿐입니다. 오래된 미디어가 만료되게 하려면 버킷에 수명 주기 규칙을 추가하세요. 예를 들어 90일 후입니다. 규칙에 video-analysis-tmp/ 접두사를 포함하세요. 이 접두사에는 임시 분석 파일이 있으며, 셀프 호스팅 설치에는 이를 정리하는 작업이 없습니다.
Railway에서 컨테이너 중지
Railway에서는 RAILWAY_DEPLOYMENT_DRAINING_SECONDS가 교체되는 컨테이너의 중지 신호와 강제 중지 사이의 시간을 정합니다. 미디어 워커는 그 시간에서 5초를 뺀 만큼 남은 작업을 마무리하므로, 긴 모델 호출도 작업이 새 컨테이너로 옮겨지기 전에 끝나고 저장될 수 있습니다. 설정하지 않으면 워커는 25초 동안 마무리합니다.
자주 묻는 질문
Nodaro 컨테이너 하나로 사용자 몇 명까지 지원할 수 있나요?
기본 단일 컨테이너 구성은 활성 사용자 약 5명까지 충분합니다. 그보다 많으면 미디어 워커, 렌더링 워커, 오케스트레이터를 별도 컨테이너에서 실행하세요.
Nodaro 컨테이너끼리는 어떻게 통신하나요?
직접 통신하지 않습니다. 모든 컨테이너가 같은 Redis, 데이터베이스, 스토리지에 연결하며, Redis의 작업 큐가 유일한 조정 지점입니다.
실행이 “Execution orphaned”로 표시되는 이유는 무엇인가요?
두 설치가 데이터베이스 하나를 공유하면서 서로 다른 Redis를 쓰고, 각자의 이름이 구분되지 않았을 가능성이 높습니다. 설치마다 고유한 RUNTIME_ENV 값을 지정하고, 한 설치의 모든 컨테이너에는 같은 값을 쓰세요.
Nodaro가 버킷의 오래된 미디어를 삭제하나요?
아니요. Nodaro는 저장된 미디어를 참조할 뿐 스스로 삭제하지 않습니다. 오래된 파일이 만료되도록 버킷에 수명 주기 규칙을 추가하고, 그 규칙에 video-analysis-tmp/ 접두사를 포함하세요.
관련 문서
아키텍처
구성
요구 사항
데이터베이스
최종 업데이트