Kubernetes上にBlenderのレンダリングファームを作る

# これは何？

レンダリングファームはとはたくさんの計算機を並べてそれをうまく使って早く3DCGをレンダリングするというものです。
これを使うことにより、3DCGアニメーションなどレンダリングに時間のかかる作業を、高速に実行することができます。

今回使用するのはオープンソースの3D統合開発環境である[Blender](http://www.blender.org/)です。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/100375/ad6bde3e-ae0b-d664-438c-0ac33f2c7832.png)


Blenderには元から分散レンダリングの仕組みが搭載されているので、たくさんのマシンをセットアップし、Blenderを起動しておけば、レンダリングファームを作ることができますが、この記事ではKubernetesを使って、簡単にBlenderのレンダリングファームを作る方法を紹介します。

# blenderのレンダリングクラスタ

2.79で利用できるadd-onであるNetwork Rendererを使います。
(最近リリースされた2.8ではどうやるかわからず・・😭）

構成は下図のようになっており、client（普通に使っているBlenderアプリケーション）からジョブをmasterに投入し、masterがjobをslaveに振り分けるようです。
結果（画像）は最終的にはclientに返却されます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/100375/de4f3a66-a50a-6acb-f4b6-0217bf9a7098.png)

# BlenderのDockerイメージ

<table>
<tr>
  <td colspan="2"><a href="https://github.com/rndevfx/docker-blender-render-cluster">rndevfx/docker-blender-render-cluster</a></td>
</tr>
<tr>
  <td><img src="https://github.com/rndevfx/docker-blender-render-cluster/raw/master/readme/images/blenderDocker.png"></td>
  <td>A docker based multi machine render setup. Contribute to rndevfx/docker-blender-render-cluster development by creating an account on GitHub.</td>
</tr>
</table>

ちょうどdockerを使ってレンダリングファームを作っている人がいたので、これをそのまま使わせてもらいます。

# Kubernetes環境の用意

今回は[minikube](https://github.com/kubernetes/minikube)を使います。minikubeはローカルにVMを作ってKubernetesクラスタを構築するものなので、これで作ったクラスタではBlenderのレンダリングは全く早くなりませんが、同じ方法を使うことで様々なKubernetes環境でこの手法が使えるため、動作の確認という意味では手軽で良い方法だと考えました。

minikubeのセットアップは公式ページなどに詳しく書かれているので割愛します。

# マニフェストファイル一覧

## masterのマニフェスト

```yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  labels:
    run: blender
  name: blender
spec:
  replicas: 1
  selector:
    matchLabels:
      run: blender
  template:
    metadata:
      labels:
        run: blender
    spec:
      containers:
      - image: d3v0x/blender-render-cluster:2.79
        name: blender
        env:
        - name: RENDER_MODE
          value: "MASTER"
```
# MasterのService
`minikube tunnel`でアクセスするために必要です。

```yaml
apiVersion: v1
kind: Service
metadata:
  labels:
    run: blender
  name: blender
spec:
  type: LoadBalancer
  ports:
  - port: 8000
    protocol: TCP
    targetPort: 8000
  selector:
    run: blender
```

## Slaveのマニフェスト

replicasを増やすだけで簡単にslaveが増やせます。

```yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  labels:
    run: blender-slave
  name: blender-slave
spec:
  replicas: 2
  selector:
    matchLabels:
      run: blender-slave
  template:
    metadata:
      labels:
        run: blender-slave
    spec:
      containers:
      - image: d3v0x/blender-render-cluster:2.79
        name: blender
        env:
        - name: MASTER_PORT_8000_TCP_ADDR
          value: "blender"
```

# 注意

手元で利用しているBlender(client)のバージョンとKubernetesで動かしているBlender(master,slave)のバージョンが同じである必要があります。

# Blenderのmasterへのアクセス
ここまで紹介したmanifestをminikube上にデプロイしたら、ブラウザから動作を確認してみます。

```console
$ minikube tunnel
```

を実行して、ServiceのIPに簡単にアクセスできるようにします。　(以前書いた [minikube tunnelを試してみた
](https://qiita.com/inajob/items/4025a1d1aa83721c453d)が参考になると思います。)

これで、ServiceのIPでブラウザからBlenderのmasterにアクセスすることができます。
まずServiceのIPアドレスを調べます。

```console
$ kubectl get svc
NAME         TYPE           CLUSTER-IP      EXTERNAL-IP     PORT(S)          AGE
blender      LoadBalancer   10.109.59.222   10.109.59.222   8000:31702/TCP   9m40s
kubernetes   ClusterIP      10.96.0.1       <none>          443/TCP          12m
```

この例だとブラウザで `http://10.109.59.222:8000`にアクセスすれば良いわけです。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/100375/7ccac104-0141-85d9-7136-d777e4ba5709.png)

こんなふうにWeb UIが出てくれば成功です。
よくみると「Slaves」の項目に2つのSlaveが登録されていることも確認できます。

# Blenderのclientの設定

次にBlenderのclientからmasterへの設定を行います。

UserPreference -> Add-ons から　Network Rendererを有効化します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/100375/a1a3ae57-c19f-e750-b1d5-64f7c57321cf.png)

そうするとNetwork Renderを選ぶことができます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/100375/b21dea77-4ddf-a262-920f-0773b00a4eb6.png)

Propaties -> Renderパネルに下図のようにmasterのIPアドレス（さっきブラウザで確認したもの）を指定します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/100375/3aa84084-f1e7-23be-1381-bdbbe4bc892d.png)

# 動作の様子

まず一度ファイルを保存する必要があります。リモートでのレンダリングは簡単にいうとファイルをそれぞれ転送して、部分的にレンダリングさせているだけなので、実施前には必ずファイルの保存が必要です。未保存のデータはレンダリングされません。

Propaties -> Renderパネルにある「Send job」をクリックすると、masterにレンダリングjobを発行できます。

しばらくするとWebUIにjobがあらわれます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/100375/655647b1-8ea1-2c34-836d-80af041da41d.png)

Not Finishedとなっていることから、今はまだ実行中ということがわかります。

これがFinishedとなったのちに、Propaties -> Renderパネルにある「Get Image」をクリックするとその結果をクライアント側で取得できます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/100375/9e509d62-d416-5545-2df0-1dcad103e19b.png)


# 感想

3Dのレンダリングパイプラインはjobを細切れにしてレンダリングして返すだけなので、一時的なデータしか持たないためPVなどを使わずとも、Kubernetesで扱うことができそうだと感じました。
もっと大きなデータやアニメーションを扱う場合はレンダリング結果が巨大になるのでNFSなどに格納するのが良いかもしれません。

最新のBlenderでのネットワークレンダリングが試せなかったのが残念。（知っている方、教えてください）