Kitsu連携によるNAS不要のフラメンコレンダリング(2026)

Kitsu連携によるNAS不要のフラメンコレンダリング(2026)
🧠
Kitsuにレンダーコンテキストとファイルを担わせることで、共有ストレージなしにFlamencoを実行できます。

Flamencoを使いたいけれど、NASは買いたくないのですね。

ソロアーティストやマイクロなアニメーションスタジオであれば、それは完全に合理的な判断です。共有ストレージは高価になりがちで、メンテナンスの手間も増え、レンダーファームを実際に回してみないと気づかない問題を、たとえ解決できたとしてもそれが本当に必要だったかは別問題だからです。

Flamencoは従来型のスタジオ構成を前提にしています:共有ファイル、共有パス、即時アクセス。NASがない状況では、その前提を回避するのが難しくなります。Flamencoには制作環境(production context)の概念がないため、「どのショットをレンダリングしたいのか」「どのバージョンが承認されているのか」「ジョブファイルがどこにあるのか」を理解できません。そして、その情報がないと、NASレス環境では安全に動作できないのです。

そこで登場するのがKitsuです。

Kitsuは、Flamencoが持っていない情報をすでに知っています。タスク、ショット、バージョン、承認状況です。Kitsuを非同期のネットワークストレージとして扱えば、必要なタイミングでデータをFlamencoマネージャーへ移し、レンダリングし、共有ストレージに依存せずに済みます。

ただし、Flamencoはこのワークフローを標準でサポートしていません。動かすには、Kitsuからコンテキストとファイルを取得し、それらをローカルに段階的に配置し、いつ・どのようにレンダリングを実行するかを制御するカスタムのFlamencoジョブタイプを作る必要があります。この記事では、そのための具体的な作り方を紹介します。

💡
動く例を探していますか?

このガイドで紹介している例の統合について、完全なソースコードはGitHubで確認できます。

🔗 https://github.com/cgwire/blog-tutorials/tree/main/flamenco-kitsu-render-farm

全体アーキテクチャ

私たちの構成は、シンプルな考え方に基づいています。Flamencoがレンダリングを行い、Kitsuが真実(正)を提供する、ということです。

Kitsu
  ↑↓ (REST API)
Custom Flamenco Job Type
  ├── Pre-task Python (fetch task data & files)
  ├── Blender render tasks (Flamenco-managed)
  └── Post-task Python (upload renders back to Kitsu)
Flamenco Manager
  ↓
Flamenco Worker(s)

Flamencoは意図どおりに動きます。マネージャーが作業をスケジューリングし、ワーカーがBlenderタスクを実行します。変わるのは、ジョブの定義方法です。Flamencoを共有フォルダへ向けて、すべてのマシンが同じファイルを見られることに賭けるのではなく、制作データを理解しKitsuと通信できるカスタムのFlamencoジョブタイプを導入します。

Kitsuはファームの外側にあり、REST APIを通じてすべてを公開します。ショット、タスク、バージョン、ファイルの場所です。レンダージョブが開始されると(手動でも自動でも)、カスタムのジョブタイプがKitsuへ問い合わせて、何をレンダリングすべきかを正確に特定します。たとえば「ショット020の最新の承認済みライティングバージョンをください」と尋ねるかもしれません。Kitsuが答え、その答えがレンダージョブになります。

Flamenco側では、マネージャーはKitsuをポーリングせず、制作の状態を追跡もしません。単に、渡されたジョブ定義を実行するだけです。カスタムジョブタイプは、小さなPythonのプリタスクを使ってKitsuからメタデータとファイルを取得し、ジョブフォルダにローカルで配置してから、Flamencoが効率よく管理できる標準のBlenderレンダータスクへ引き渡します。

レンダリングが完了すると、ポストタスクのPythonステップが結果をKitsuへ送り返し、レンダリングされたフレームをアップロードしたり、新しいバージョンを作ったり、タスクのステータスを更新したりします。作業員(ワーカー)が共有ストレージを必要としたり、同じファイルシステムへ恒久的にアクセスする必要は、一切ありません。各ワーカーが必要なものを取り出してローカルでレンダリングし、結果を非同期にプッシュします。


1. 新しいジョブタイプを作成する

Flamencoのジョブタイプは、ジョブが実際の作業にどう変換されるかを定義します。「これをレンダリングしたい」という要求から、ファーム全体に対してFlamencoがスケジューリングする具体的なタスクへと変換する層です。概念的には、ジョブタイプは必要な情報と、その情報をタスクへ組み立てる方法を宣言します。

最も単純な形では、ジョブタイプはラベルと設定のセットを説明し、その設定を受け取ってジョブを組み立てる関数を提供します。コード上では、次のようなイメージです:

const JOB_TYPE = {
  label: "Kitsu Render",
  settings: [
    // { key: "message", type: "string", required: true },
    // { key: "sleep_duration_seconds", type: "int32", default: 1 },
  ],
};

function compileJob(job) { const settings = job.settings; }

このコードは、カスタムのFlamencoジョブタイプの骨組みを定義します。JOB_TYPEオブジェクトは、Flamenco上でのジョブの見え方を宣言します。つまり、人が読めるラベルと、ジョブ作成時に想定される設定です。

これらの設定は、型付きの入力として機能し、バリデーションはFlamencoが担当します。この例では、必須の文字列と、デフォルト値を持つ任意の整数です。

compileJob関数は、ジョブを実行可能なタスクへ変換する場所です。送信されたジョブを受け取り、解決された設定を読み取り、通常はそれらを使ってレンダ、プリタスク、ポストタスクのステップを生成します。現状のままではまだ何も処理していませんが、制作ロジックが置かれる入口を確立しています。

実際の制作環境では、汎用のメッセージの代わりに、KitsuのタスクID、ショット名、希望する出力先、あるいは使用するべきBlenderバージョンなどを渡します。

このロジックがどこに置かれるかは重要です。カスタムのFlamencoジョブタイプは、ワーカーではなくFlamenco Manager上で動作します。ディスク上では、たとえば次のようにマネージャープログラムの隣に配置されます:

$ flamenco
└── flamenco-manager
└── scripts/
└── kitsu-render.js

実務では、スタジオはこれらのジョブタイプスクリプトをパイプラインのコードベースの一部として扱います。バージョン管理下に置かれ、時間とともに進化し、Flamencoのアップデートと一緒にデプロイされます。そうすれば、ファーム上のすべてのワーカーマシンを再デプロイや再設定せずに、ジョブの組み立て方法やKitsuへの問い合わせ方法を変更できます。

カスタムのジョブタイプからコマンドとして呼び出されるワーカースクリプトは、flamenco-workerプログラムの隣に配置します:

$ flamenco
└── flamenco-worker
└── kitsu-render.py

2. タスクを追加する

compileJobの内部では、ジョブを構成するタスクを明示的に定義します。ここが、「このショットをレンダリングしてほしい」という高レベルな要求が、Flamencoがワーカーへ引き渡せる具体的でスケジュール可能な作業へ変換される場所です。

下の例は、最もシンプルなタスクです。echoタスクをFlamencoのタスク作成APIで作成し、カテゴリを与えたうえで、単一のコマンドを割り当てます。このコマンドは、解決されたジョブ設定をタスクへ渡し、タスクが実行されるとメッセージを表示します。最後に、そのタスクをジョブへ追加して、マネージャーがスケジュールできるようにします。

const echoTask = author.Task("echo", "misc");
echoTask.addCommand(
author.Command("echo", {
message: settings.message,
}),
);
job.addTask(echoTask);

このタスク自体は有用なことはしませんが、重要なのはこのパターンです。同じ仕組みを使ってPythonスクリプトを実行したり、レンダリングのためにBlenderをバックグラウンドモードで起動したり、タスクが完了とみなされる前にバリデーションチェックを行ったりできます。各タスクは原子的で再起動可能な設計になっており、つまり、ワーカーがクラッシュしたり、午前3時にレンダが失敗したとしても、Flamencoはジョブ全体を崩さずにそのタスクだけを再試行できます。この信頼性こそが、このアプローチが夜通しで何百ものショットを回す際にスケールする理由です。

それでは、チュートリアルの本題に入りましょう。Kitsuからアセットをダウンロードし、Blenderでレンダリングし、結果をKitsuへ再アップロードするタスクをコード化します。


3. サブコマンド1:Kitsuからアセットをダウンロードする

Kitsu主導のジョブで最初に行う本格的なタスクは、必要な正確なデータをKitsuから取り出し、ワーカー上にクリーンなローカルワークスペースを用意することです。Blenderが起動される前に、ワーカーは「どのタスクをレンダリングするのか」と「ジョブファイルがどこにあるのか」を把握している必要があります。

ロジックをJavaScriptで書く代わりに、よりシンプルなgazur Python SDKを使ってkitsu-renderスクリプトを作成し、それをJavaScriptから呼び出します。ワーカー環境にPythonがインストールされていない場合は、Pythonスクリプトからバイナリ実行ファイルを作ることを検討してください。

function compileJob(job) {
const settings = job.settings;

const task = author.Task("kitsu-render", "misc");

task.addCommand( author.Command("exec", { exe: "python3", args: "kitsu-render.py" }), );

job.addTask(task); }

PythonスクリプトはKitsu APIへ認証し、TODOレンダリングタスクを探し、レンダリングするための.blendプロジェクトを含む関連するプレビュー・ファイルをダウンロードします。

import os
import gazu

gazu.set_host("http://localhost/api") user = gazu.log_in("admin@example.com", "mysecretpassword")

projects = gazu.project.all_projects() project = projects0

tasks = gazu.task.all_tasks_for_project(project)

rendering = gazu.task.get_task_type_by_name("Rendering") todo = gazu.task.get_task_status_by_name("todo")

render_tasks = t for t in tasks if t"task_type_id" == rendering"id" and t"task_status_id" == todo"id"

for task in render_tasks: files = gazu.files.get_all_preview_files_for_task(task) if not files: continue

latest = files[-1]
if latest["extension"] == "blend":
    task_to_render = task
    latest_blend = latest
    break

if task_to_render is None: raise RuntimeError("No render task with a .blend preview found")

target_path = os.path.join( "/tmp", latest_blend"original_name" + "." + latest_blend"extension" )

gazu.files.download_preview_file(latest_blend, target_path)

このステップが、NASレスのワークフローを成立させます。各ワーカーは、制作ツリー全体をマウントしたり同期したりするのではなく、実行中の特定タスクに必要なファイルだけを取り出します。ダウンロードに失敗した場合も、人の介入なしにFlamencoがタスクを自動的に再試行できます。


4. サブコマンド2:Blenderでレンダリングする

レンダリングするblendファイルがワーカー上にローカルで段階配置できたら、bpyライブラリでプログラム的にレンダリングできます:

bpy.ops.wm.open_mainfile(filepath=target_path)

output_path = os.path.join( "/tmp", latest_blend"name" + ".mp4" )

bpy.context.scene.render.image_settings.file_format = "FFMPEG" bpy.context.scene.render.ffmpeg.format = "MPEG4" bpy.context.scene.render.ffmpeg.codec = "H264" bpy.context.scene.render.ffmpeg.constant_rate_factor = "HIGH" bpy.context.scene.render.ffmpeg.gopsize = 12 bpy.context.scene.render.ffmpeg.audio_codec = "AAC" bpy.context.scene.render.filepath = output_path

bpy.ops.render.render(animation=True)

より高度なパイプラインでは、Flamencoのネイティブな「blender-render」コマンドを活用し、フレーム範囲を小さな作業単位に自動で分割して、利用可能なワーカーへ分散できます。マシンが脱落したり、フレームが失敗したりしても、そのフレームだけが再試行されるため、ショット全体を最初からやり直したり、並列性を扱うための独自キューのロジックを作ったりする必要がありません。

しかし、この例をシンプルに保つため、ワーカー1台で動画全体をレンダリングします。


5. サブコマンド3:結果をKitsuへアップロードする

ジョブの最後のステップは、レンダリング後のサブコマンドでレンダリング結果をKitsuへ戻すことです。この時点で、ワーカーは自分のフレーム範囲のレンダリングをローカルで完了しており、ファームの責務は計算からパブリッシングへ移ります。ここで、レンダリングされた出力が制作の他の部分から見えるようになります。

下の例は、生成された動画ファイルを元のタスクに対するアタッチメントとしてKitsuへアップロードする、最小限のPython命令を示しています。

result = gazu.task.publish_preview(
task_to_render,
todo,
comment="rendered",
preview_file_path=output_path,
)

実際の制作パイプラインでは、このステップは通常、単にファイルをアップロードするだけではありません。Kitsuで新しいバージョンを作成し、タスクのステータスをDoneのような状態へ更新し、レビューや通知のワークフローをトリガーして、監督者が新しい出力の準備ができたことを把握できるようにします。このロジックはFlamencoタスク内で動くPythonなので、レンダーファーム自体を触らずに、制作ニーズが変わったときに簡単に進化させられます。


6. ワークフローを起動する

カスタムジョブタイプが用意できたら、Flamencoマネージャーへジョブリクエストを送信することでワークフローがトリガーされます。開発中は、マネージャーのREST APIを直接呼び出すことで手動で行うことがよくあります。これにより、ジョブのコンパイルが動作すること、設定が正しく配線されていること、そして自動化を積み重ねる前にタスクが期待どおり挙動することを素早く検証できます。

下の例では、kitsu-renderタイプのジョブをマネージャーへ送信します。追跡や帰属のための基本メタデータに加えて、リクエストには優先度の値と、通常はKitsuの制作IDのような制作固有の入力を含むはずの空のsettingsオブジェクトが含まれています。ジョブが受理されると、マネージャーはカスタムジョブタイプを呼び出し、タスクをコンパイルして、利用可能なワーカー全体へスケジュールします。

curl -X 'POST' 
'http://172.17.0.1:8080/api/v3/jobs'
-H 'accept: application/json'
-H 'Content-Type: application/json'
-d '{ "metadata": { "project": "kitsu", "user.email": "basunako@gmail.com", "user.name": "kitsu" }, "name": "Kitsu Render", "priority": 50, "settings": {}, "submitter_platform": "linux", "type": "kitsu-render" }'

マネージャーがジョブリクエストを受け取り、それをワーカーに割り当てたことが確認できます:

この手動トリガーは主に開発用のツールです。ジョブロジックを反復し、想定外のケースをテストし、制作側の人や制作ツールを巻き込まずにジョブを再実行できます。

本番では、スタジオは常にこのステップを自動化します。小さなサービス(多くの場合cronジョブや軽量なWebhookリスナー)は定期的にKitsuへ問い合わせ、「ちょうど承認されたショット」や「パブリッシュされたもの」のようにレンダリングできる状態のタスクを探します。見つかったら、同じAPI呼び出しを使ってFlamencoマネージャーへ対応するジョブを送信します。

これができると、Flamencoは人がボタンを押すのを待つのではなく、Kitsuの変化に自動的に反応し、ファームを制作の状態と同期したまま保つ、制作を理解したレンダーバックエンドになります。


結論

この記事であなたが作り上げたのは、小規模スタジオにおけるレンダリングの考え方としては根本的に異なるアプローチです。

Kitsuからコンテキストとデータを引き出し、作業をローカルに段階配置し、Flamencoのネイティブなスケジューラでレンダリングし、結果を非同期に送り返すために、カスタムのFlamencoジョブタイプを使ったことで、信頼性やスケールを犠牲にせずに共有ストレージの必要をなくせました。

各パーツの責務は明確です。Kitsuが制作上の正しさを定義し、Flamencoが作業の実行方法を決め、そしてカスタムジョブタイプが両者を同期させる“接着剤”になります。この分離があるからこそ、このシステムは堅牢で、デバッグ可能で、制作パイプラインが成長しても適応できます。

このパターンを理解することは重要です。ソロのアーティストやマイクロスタジオの現実に合ったレンダリング基盤を構築できるからです。

ですが、ここで終わりにしないでください。この記事用のサンプルGitHubリポジトリをクローンすることで、今日からレンダリングを始めましょう!

📽️
アニメーションの制作プロセスについてさらに学ぶには Discordコミュニティへの参加をご検討ください!私たちは、ベストプラクティスを共有する1,000人以上の専門家とつながっており、ときには対面イベントも企画しています。ぜひようこそお迎えしたいです! 😊

この記事はいかがでしたか?

ニュースレターを購読して、さらなる考察、チュートリアル、業界ニュースをお受け取りください。