Escalonamento
Escalone o Nodaro self-hosted para além de um contêiner: separe a API, os workers e o orquestrador e ajuste a concorrência, o Redis e o armazenamento.
Escalonar um Nodaro self-hosted significa rodar os processos dele em mais de um contêiner. A configuração padrão do Compose roda tudo em um único contêiner: a API, os workers, o orquestrador e o servidor web. Isso atende bem até cerca de 5 usuários ativos. Para mais usuários, rode os workers em contêineres próprios. Eles compartilham um Redis, um banco de dados e um bucket de armazenamento, e só se coordenam pelas filas do Redis.
Os processos
O script de inicialização da imagem, /app/start.sh, inicia estes processos lado a lado, a partir de /app/backend:
| Comando | O que faz | Carga |
|---|---|---|
node dist/server.js | A API HTTP | Pouca CPU, memória moderada |
node dist/worker.js | O worker de mídia: um job por nó, chama os provedores de modelos | Espera pela rede, executa muitos jobs ao mesmo tempo |
node dist/render-worker.js | O renderizador: composições, em um Chrome headless | Limitado pela CPU: 1 ou 2 por máquina |
node dist/orchestrator.js | O orquestrador de workflows: executa o grafo de cada workflow | Espera pela rede, pouca CPU |
node dist/pipeline-worker.js | O pipeline História → vídeo (Story → Video), que só roda no Nodaro Cloud | Encerra na hora nas edições self-hosted |
O script de inicialização também faz quatro coisas que um contêiner que roda um único processo não faz:
- Ele roda o servidor web na porta
3000, na frente da API. - Ele aplica as migrações na stack incluída.
- Ele gera o
INTERNAL_ORCHESTRATOR_SECRETquando a variável não está definida. - Ele gera a chave de criptografia na stack incluída.
Leia o script na imagem antes de dividi-lo.
Uma divisão típica
- 1 contêiner da API, rodando
server.js. - Vários contêineres de worker de mídia.
VIDEO_WORKER_CONCURRENCY=50, o padrão, serve para cada um. - 1 ou 2 contêineres de worker de renderização, cada um na própria máquina.
- 1 contêiner do orquestrador.
Os contêineres nunca se comunicam diretamente. Todos usam o mesmo Redis, o mesmo Supabase e o mesmo armazenamento, e o Redis é o único ponto de coordenação.
O que todos os contêineres precisam compartilhar
| Variável | Por que precisa ser igual |
|---|---|
INTERNAL_ORCHESTRATOR_SECRET | O orquestrador se autentica na API com ela. O script de inicialização gera uma nova em cada contêiner quando ela não está definida, então defina-a explicitamente, com o mesmo valor em todos os lugares. |
NODARO_ENCRYPTION_KEY | As chaves de provedor e as credenciais armazenadas são criptografadas com ela. Outra chave não consegue lê-las. |
RUNTIME_ENV | Dá nome à instalação. Todos os contêineres de uma mesma instalação precisam usar o mesmo valor. |
EDITION, SUPABASE_URL, SUPABASE_SERVICE_ROLE_KEY, REDIS_URL e as variáveis R2_* | A mesma edição, o mesmo banco de dados, as mesmas filas e o mesmo armazenamento em todos os contêineres. |
Concorrência
| Variável | Padrão | O que ela limita |
|---|---|---|
MAX_CONCURRENT_NODES_PER_EXECUTION | 6, no máximo 20 | Os nós que uma execução de workflow pode executar ao mesmo tempo. O teto de nós em paralelo para todo o servidor. |
VIDEO_WORKER_CONCURRENCY | 50 | Jobs ao mesmo tempo em um worker de mídia |
ORCHESTRATOR_CONCURRENCY | 20 | Jobs de workflow ao mesmo tempo em um orquestrador |
RENDER_WORKER_CONCURRENCY | 2, no máximo 10 | Renderizações ao mesmo tempo em um worker de renderização. Cada uma é um Chrome headless. |
REMOTION_CONCURRENCY | 2 para cenas 3D; metade dos núcleos da CPU para as outras renderizações | Abas do navegador por renderização. Mantenha o valor baixo quando vários jobs 3D são executados ao mesmo tempo: cada aba WebGL acrescenta threads e conta para o limite de processos do contêiner. |
FFMPEG_CONCURRENCY | 4, no máximo 32 | Processos do ffmpeg ao mesmo tempo, somando todos os nós de edição de vídeo e de áudio |
O arquivo do Compose não repassa essas variáveis do .env. Adicione-as em environment: de cada serviço que precisa delas.
Cada nó de uma execução pode levar até 90 minutos, e uma execução inteira, até 120 minutos. Um nó que declara o próprio orçamento de tempo recebe esse orçamento, e o limite da execução cresce na mesma medida. O Aplicar EDL (Apply EDL) é um nó assim: o orçamento dele depende da edição que ele renderiza.
Alta disponibilidade do Redis
As filas de jobs aceitam o modo cluster do Redis. Defina REDIS_URL com um endpoint de cluster ou uma URL do Sentinel.
Além das filas, a API mantém pequenos caches compartilhados no Redis, para que vários contêineres da API não repitam, cada um, o mesmo trabalho lento junto aos provedores. Hoje, isso é o catálogo de avatares e vozes da HeyGen, de cerca de 4 MB. Um contêiner o atualiza sob um bloqueio (lock) a cada HEYGEN_CATALOG_REFRESH_HOURS horas, 24 por padrão, e os outros adotam a nova cópia em cerca de meio minuto. Tudo ali é cache: quando o Redis não pode ser acessado, cada contêiner usa a própria memória, e uma entrada perdida é preenchida de novo a partir do provedor na inicialização seguinte.
Duas instalações, um banco de dados
Você pode apontar uma segunda instalação, como uma cópia de staging, para o mesmo projeto do Supabase, com um Redis próprio. Nesse caso, dê a cada instalação um RUNTIME_ENV diferente. No Railway, RAILWAY_ENVIRONMENT_NAME já faz isso.
Cada execução registra o nome da instalação cujo orquestrador a assumiu, e a limpeza de cada instalação só verifica as próprias execuções. Sem nomes distintos, cada instalação procura no próprio Redis os jobs da outra, não os encontra e marca execuções saudáveis como falhas com Execution orphaned. As execuções que começaram antes de as instalações registrarem os nomes não têm nome; a instalação chamada production cuida delas.
Ciclo de vida do armazenamento
O Nodaro nunca apaga sozinho a mídia armazenada: ele só faz referência aos arquivos pela chave deles. Para expirar a mídia antiga, adicione uma regra de ciclo de vida ao seu bucket, por exemplo depois de 90 dias. Inclua o prefixo video-analysis-tmp/ na regra: ele guarda arquivos temporários de análise, e as instalações self-hosted não têm um job de limpeza para eles.
Parar contêineres no Railway
No Railway, RAILWAY_DEPLOYMENT_DRAINING_SECONDS define o tempo entre o sinal de parada e a parada forçada de um contêiner substituído. O worker de mídia drena durante esse tempo menos 5 segundos, para que uma chamada longa a um modelo consiga terminar e ser salva antes de o job dela passar para o novo contêiner. Sem a variável, o worker drena por 25 segundos.
Perguntas frequentes
Páginas relacionadas
Arquitetura
Configuração
Requisitos
Banco de dados
Última atualização
Atualização
Atualize o Nodaro self-hosted com uma imagem mais nova, fixe uma tag de versão, faça um backup antes de uma versão major e reverta restaurando esse backup.
Backup e restauração
Faça o backup do Nodaro self-hosted com um comando, restaure com outro e faça downgrade com segurança. Veja o que o backup guarda e como proteger a chave.