R RenderComp
remotion lambda video-rendering typescript worker-pool

Remotionワーカープールのサイジング設計

執筆: RenderComp チーム 編集方針

Remotionがフレームを計算する単位は、映像ファイルではなくJSXコンポーネントの返り値です。同じinputPropsと同じフレーム番号を渡せば、どの実行環境でも同一のビットマップが生成されます。この決定論的な性質が、フレームを分割して複数のLambdaワーカーに並行して投げる根拠になっています。

問題が複雑になるのは、複数のジョブが同時にキューに積まれる運用フェーズです。ワーカーの同時起動数がAWS Lambdaの同時実行上限を超えると、後続ジョブが503エラーで失敗します。framesPerLambdaの値とアカウントの同時実行数設定の関係を把握することが、安定したキュー運用の前提になります。


framesPerLambda が決めるもの

renderMediaOnLambdaに渡すframesPerLambdaは、1台のLambdaワーカーが担当するフレーム数を指定します。合計フレーム数をこの値で割った数が、そのジョブで起動するワーカーの台数です。

Remotion 4.0.331以降、framesPerLambdaの下限は5です。それ以前のバージョンでは下限は4でした。指定値が下限を下回ってもサイレントにクランプされるため、フレーム数が少ない短尺動画でも最低5フレームが1台のワーカーに割り当てられます。

import { renderMediaOnLambda } from "@remotion/lambda/client";

const { renderId, bucketName } = await renderMediaOnLambda({
  region: "ap-northeast-1",
  functionName: "remotion-render-4-0-mem2048mb-disk2048mb-120sec",
  serveUrl: SERVE_URL,
  composition: "MyVideo",
  inputProps: { title: "Hello World" },
  codec: "h264",
  // 300フレームの場合、起動するワーカーは300÷20=15台
  framesPerLambda: 20,
});

framesPerLambdaを小さくするとワーカー数が増えて並列度が上がります。各ワーカーの起動にはコールドスタートのオーバーヘッドがあるため、値を下げれば全体の書き出し時間が短くなるわけではありません。ワーカー数を増やすほどLambdaの同時実行枠を消費するという点も同様に重要です。


ワーカー数の計算と同時ジョブ数の見積もり

AWS Lambdaの実行環境1インスタンスは、秒間最大10リクエストを処理できます。アカウント全体の呼び出し上限は「同時実行数の設定値×10」です。同時実行数を1,000に設定した環境では、秒間10,000リクエストまで呼び出せます。

複数のジョブが同時にLambdaを呼び出す場合、各ジョブのワーカー数の合計がこの上限に収まるかどうかを事前に計算します。

// Remotion 4.0.331以降の下限
const FRAMES_PER_LAMBDA_MIN = 5;

function estimateWorkerCount(
  totalFrames: number,
  framesPerLambda: number
): number {
  const clamped = Math.max(framesPerLambda, FRAMES_PER_LAMBDA_MIN);
  return Math.ceil(totalFrames / clamped);
}

// オーケストレーターLambdaの1台分を加算して上限を計算
function maxConcurrentJobs(
  concurrencyLimit: number,
  totalFrames: number,
  framesPerLambda: number
): number {
  const workersPerJob = estimateWorkerCount(totalFrames, framesPerLambda);
  return Math.floor(concurrencyLimit / (workersPerJob + 1));
}

たとえば30fps・150秒の動画(4,500フレーム)をframesPerLambda: 30で書き出すと、1ジョブあたりのワーカーは150台です。同時実行数1,000の環境ではmaxConcurrentJobs(1000, 4500, 30)は6を返します。7本目以降のジョブは、いずれかが完了して枠が空くまで待機させる必要があります。


キューとスロットル

並列ジョブ数をプログラムで制御するには、Amazon SQSとLambdaを組み合わせる構成が実用的です。EventSourceのmaxConcurrencyを設定すると、アカウント全体の同時実行数とは別にキュー消費側の並行数を絞れます。

import { SqsEventSource } from "aws-cdk-lib/aws-lambda-event-sources";

// レンダーワーカーがキューを消費する際の同時実行数上限
renderFunction.addEventSource(
  new SqsEventSource(renderQueue, {
    batchSize: 1,        // 1呼び出し=1ジョブ
    maxConcurrency: 50,  // このキューの消費は最大50並列まで
  })
);

batchSize: 1にすると、1回のLambda呼び出しで1ジョブだけ処理します。レンダーは長時間実行になりやすく、バッチサイズを大きくするとタイムアウトのリスクが高まります。

ジョブをキューに投入する側は、レンダー完了を待たずにすぐ返ります。renderIdをデータベースに記録しておき、後のポーリングで完了を確認します。

import { SendMessageCommand, SQSClient } from "@aws-sdk/client-sqs";

const sqs = new SQSClient({ region: "ap-northeast-1" });

async function enqueueRenderJob(
  renderId: string,
  renderParams: Record<string, unknown>
): Promise<void> {
  await sqs.send(
    new SendMessageCommand({
      QueueUrl: RENDER_QUEUE_URL,
      MessageBody: JSON.stringify({ renderId, ...renderParams }),
    })
  );
}

進捗ポーリング

renderMediaOnLambdaはレンダーの完了を待たずに返ります。getRenderProgressを定期的に呼び出して完了を検知します。

import { getRenderProgress } from "@remotion/lambda/client";

async function pollUntilDone(
  renderId: string,
  bucketName: string,
  region: string,
  functionName: string
): Promise<string> {
  while (true) {
    const progress = await getRenderProgress({
      renderId,
      bucketName,
      functionName,
      region,
    });

    if (progress.done) {
      return progress.outputFile!;
    }

    if (progress.fatalErrorEncountered) {
      throw new Error("Render failed");
    }

    // overallProgress は 0〜1 の小数
    console.log(`進捗: ${Math.round(progress.overallProgress * 100)}%`);

    await new Promise<void>((resolve) => setTimeout(resolve, 3_000));
  }
}

ポーリング間隔3秒は実用的な基準です。並列ジョブが多い場合、getRenderProgressの呼び出し自体もLambda実行数に加算されます。ジョブ数が数十を超える環境では、間隔を長めに設定するか、SNSトピックへのWebhookに切り替えることを検討します。


framesPerLambda の選択指針

framesPerLambdaの値は、1本あたりの書き出し速度と同時処理可能なジョブ数のトレードオフを決めます。短尺・大量同時投入の用途ではワーカー数を絞って同時実行枠を複数ジョブで共有し、長尺・少量の用途では1本あたりの並列度を上げます。

// 用途別の framesPerLambda 設定例
const presets = {
  // 30fps × 15秒 = 450フレーム → ワーカー9台
  shortClip: { framesPerLambda: 50 },
  // 30fps × 120秒 = 3600フレーム → ワーカー240台
  longForm: { framesPerLambda: 15 },
} as const;

下限の5を下回る値を渡してもクランプされるため、設定ミスで想定外のワーカー数が起動することはありません。実際の起動台数はgetRenderProgressの返り値で確認できるため、本番前に1ジョブだけ走らせてconcurrencyLimitの見積もりと照合しておくと、後の調整コストを減らせます。

販売中

1,000以上のRemotionテンプレートを一括入手

買い切り(一括払い)・サブスクなし・生涯アップデート無料。TypeScript製。

料金プランを見る →

無料50本

テンプレート50本を ZIP で受け取る

メールアドレスを入れると、50本の ZIP のリンクをすぐにお送りします。

50本の ZIP をメールで受け取ります。あわせて RenderComp テンプレートの更新と、有料ライブラリを含む製品のご案内を受け取ることに同意します(メールは数通・ワンクリック解除)。 プライバシーポリシー(英語)

メールを使わずに受け取ることもできます。GitHub のリポジトリは公開のままで、登録も不要です。 リポジトリを開く