ブログ

BLOG

Blenderのレンダリングサーバーを作ろう

2020年4月12日
Blenderのレンダリングサーバーを作ろう

こんにちはyuです。前回に引き続きBlenderネタです。

Blenderでレンダリングさせるサービスを作りたいと思ったとき、Blenderをサーバーに置いて外部からリクエストを送り、レンダリング結果を取得するといった運用が考えられます。

予めスクリプトなりBlenderファイルなりを作っておいて、モデルデータをS3経由でサーバーに取得させてblenderに食わせる..といったことができたら便利ですよね。

ただしいざサーバー上でやってみようとするとうまく動かないのです。GUIはいらないから、Blender -bでバックグラウンド実行でうまくいくはず..と思いきや、

"Unable to open a display” by the rendering on the background (Eevee)

といったエラーが出てしまいます。これはBlenderがDisplayを必要としているからです。

Blenderはbackgroundであろうがなかろうが、常にDisplayを必要とします。なのでサーバーもGUI環境が構築されているか、sshの接続元でGUIを起動し、fowargingしないと起動することができません。

ただ、

「レンダリングサーバーなんだからGUIを起動しておくなんて無駄でしょ。というかBlenderを起動している間常にssh接続でGUIをフォワーディングしていないといけないの??」

と思うかもしれません。
確かにX11をインストールし、GUIを構築しないといけませんが、仮想ディスプレイを作ることにより擬似的にHeadless環境を構築することができます。この場合、ssh接続の際にGUIをfowardingする必要はありません。

今回はその具体的なやり方について解説します。

事前準備

お使いのクラウドのインスタンスにssh接続します。また今回Ubuntu18で構築しました。CentOSなどで構築したい方はx11のインストール周りは別途調査する必要があります。

今回はX11をインストールしGUIを起動しますが、ローカル環境へのフォワーディングは行いません。なので、sshするときに-Xオプションは必要ありません。

X11のインストール

下記のコマンドでX環境をインストールします

 apt update -y
 apt install xorg xserver-xorg-core -y

このとき、/etc/X11/xorg.confという設定ファイルが作成されるので、確認しましょう。

仮想ディスプレイを設定

仮想ディスプレイを作るには、/etc/X11/xorg.confを設定する必要があります。

今回はnvidia-xconfigが便利なので、下記のコマンドで設定します。

nvidia-xconfig -a --allow-empty-initial-configuration --use-display-device=None --virtual=1920x1200 --busid nvidia-xconfig --query-gpu-info | grep "PCI BusID" | sed -e "s/PCI BusID : //"
sed -i "47,58d" /etc/X11/xorg.conf && sed -i "4,9d" /etc/X11/xorg.conf

書き換えたあとのxorg.confは下記のようになります。

Section "Files"
EndSection
Section "InputDevice"
    # generated from default
    Identifier     "Mouse0"
    Driver         "mouse"
    Option         "Protocol" "auto"
    Option         "Device" "/dev/psaux"
    Option         "Emulate3Buttons" "no"
    Option         "ZAxisMapping" "4 5"
EndSection
Section "InputDevice"
    # generated from default
    Identifier     "Keyboard0"
    Driver         "kbd"
EndSection
Section "Monitor"
    Identifier     "Monitor0"
    VendorName     "Unknown"
    ModelName      "Unknown"
    HorizSync       28.0 - 33.0
    VertRefresh     43.0 - 72.0
    Option         "DPMS"
EndSection
Section "Device"
    Identifier     "Device0"
    Driver         "nvidia"
    VendorName     "NVIDIA Corporation"
    BoardName      "Tesla T4"
    BusID          "PCI:0:30:0"
EndSection

X11を起動

最後にX11をバックグラウンドで起動してGUIをDisplay番号0番に設定します。下記のコマンドを実行します。

X :0 &

設定似誤りが無ければ、問題なく起動するでしょう。

Blenderをインストール

curlwgetでBlenderをダウンロードし、tarコマンドで解答します。

curl -OL https://download.blender.org/release/Blender2.82/blender-2.82-linux64.tar.xz
                tar xf blender-2.82-linux64.tar.xz

これで、Blenderを起動する全ての準備が整いました。

レンダリングしてみよう

Blenderを実行するときは環境変数DISPLAYにXが起動しているDisplay番号を指定する必要があります。.bashrc.zshrc

export DISPLAY=:0

と書いておけばシェル起動時に設定されますし、コマンド実行時だけ指定したければコマンドの手前にDISPLAY=:0をかけば反映されます。

手前に書いておいてBlenderをバックグラウンドで起動し、レンダリング結果を保存するというところまでしてみましょう。

下記のコマンドを実行します。

DISPLAY=:0 /blender-2.82-linux64/blender -b -f 1

私の環境では、下記の通り実行されました。

Blender 2.82 (sub 7) (hash 77d23b0bd76f built 2020-02-12 17:14:50)
found bundled python: /blender-2.82-linux64/2.82/python
Fra:1 Mem:88.27M (0.00M, Peak 88.55M) | Time:00:01.84 | Syncing Cube
Fra:1 Mem:88.59M (0.00M, Peak 88.88M) | Time:00:02.25 | Syncing Light
Fra:1 Mem:88.59M (0.00M, Peak 88.88M) | Time:00:02.25 | Syncing Camera
Fra:1 Mem:88.59M (0.00M, Peak 88.88M) | Time:00:02.31 | Rendering 1 / 64 samples
Fra:1 Mem:88.60M (0.00M, Peak 88.88M) | Time:00:02.53 | Rendering 26 / 64 samples
Fra:1 Mem:88.60M (0.00M, Peak 88.88M) | Time:00:02.56 | Rendering 51 / 64 samples
Fra:1 Mem:88.60M (0.00M, Peak 88.88M) | Time:00:02.58 | Rendering 64 / 64 samples
Fra:1 Mem:48.33M (0.00M, Peak 88.88M) | Time:00:02.63 | Sce: Scene Ve:0 Fa:0 La:0
Saved: '/tmp/0001.png'
 Time: 00:03.14 (Saving: 00:00.51)

レンダリング結果が/tmp/0001.pngに保存されました!

まとめ

Blenderでssh接続し、X環境とBlenderをインストールしてレンダリング結果を取得するというところまで行いました。ここまで出来れば、デスクトップで構築したスクリプトやBlenderファイルをアップロードしてwebサーバーを構築し、適当な入力を受けてレンダリングを行うことは簡単にできると思います。

Appendix

「細かいことは後で読むからまず動くものを見たい!」という方のために、AWSのCloudFormation templateを用意しました。これでスタックを作れば上記の手順で構築したEC2インスタンスがお使いのawsに構築され、簡単に動作確認ができます。{your server name}だけインスタンスにつけたい名前にご変更ください。

AWSTemplateFormatVersion: "2010-09-09"
Description: Provision EC2
Parameters:
  KeyName:
    Description: The EC2 Key Pair to allow SSH access to the instance
    Type: "AWS::EC2::KeyPair::KeyName"
Resources:
  VPC:
    Type: AWS::EC2::VPC
    Properties:
      CidrBlock: 10.0.0.0/16
      Tags:
        - Key: Name
          Value: vpc-cf
  IGW:
    Type: AWS::EC2::InternetGateway
    Properties:
      Tags:
        - Key: Name
          Value: igw-cf
  AttachGateway:
    Type: AWS::EC2::VPCGatewayAttachment
    Properties:
      VpcId: !Ref VPC
      InternetGatewayId: !Ref IGW
  PubSub:
    Type: AWS::EC2::Subnet
    Properties:
      AvailabilityZone: ap-northeast-1a
      VpcId: !Ref VPC
      CidrBlock: 10.0.1.0/24
      Tags:
        - Key: Name
          Value: pub-sub-a-cf
  PubSubRT:
    Type: AWS::EC2::RouteTable
    Properties:
      VpcId: !Ref VPC
      Tags:
        - Key: Name
          Value: pub-sub-a-rt-cf
  PubSubToInternet:
    Type: AWS::EC2::Route
    Properties:
      RouteTableId: !Ref PubSubRT
      DestinationCidrBlock: 0.0.0.0/0
      GatewayId: !Ref IGW
  AssoPubSubRT:
    Type: AWS::EC2::SubnetRouteTableAssociation
    Properties:
      SubnetId: !Ref PubSub
      RouteTableId: !Ref PubSubRT
  MyInstanceIAMRole:
    Type: "AWS::IAM::Role"
    Properties:
      AssumeRolePolicyDocument:
        Version: "2012-10-17"
        Statement:
          -
            Effect: "Allow"
            Principal:
              Service:
                - "ec2.amazonaws.com"
            Action:
              - "sts:AssumeRole"
      Path: "/"
      Policies:
        -
          PolicyName: "root"
          PolicyDocument:
            Version: "2012-10-17"
            Statement:
              -
                Sid: "1"
                Effect: "Allow"
                Action: "cloudwatch:PutMetricData"
                Resource: "*"
  MyInstanceIAMInstanceProfile:
    Type: "AWS::IAM::InstanceProfile"
    Properties:
      Path: "/"
      Roles:
        - Ref: MyInstanceIAMRole
    DependsOn: MyInstanceIAMRole
  EC2:
    Type: AWS::EC2::Instance
    Properties:
      ImageId: ami-039d0c16bef5e0155
      KeyName: !Ref KeyName
      InstanceType: g4dn.2xlarge
      BlockDeviceMappings:
      -
        DeviceName: /dev/sda1
        Ebs:
          VolumeSize: 200
          VolumeType: gp2
      NetworkInterfaces:
        - AssociatePublicIpAddress: "true"
          DeviceIndex: "0"
          SubnetId: !Ref PubSub
          GroupSet:
            - !Ref EC2SG
      UserData:
          Fn::Base64: |
              #!/bin/bash -xe
              exec > >(tee /var/log/user-data.log|logger -t user-data -s 2>/dev/console) 2>&1
                apt update -y
                apt install xorg xserver-xorg-core -y
                nvidia-xconfig -a --allow-empty-initial-configuration --use-display-device=None --virtual=1920x1200 --busid nvidia-xconfig --query-gpu-info | grep "PCI BusID" | sed -e "s/PCI BusID : //"
                sed -i "47,58d" /etc/X11/xorg.conf && sed -i "4,9d" /etc/X11/xorg.conf
                X :0 &
                curl -OL https://download.blender.org/release/Blender2.82/blender-2.82-linux64.tar.xz
                tar xf blender-2.82-linux64.tar.xz
      IamInstanceProfile: !Ref MyInstanceIAMInstanceProfile
      Tags:
        - Key: Name
          Value: {your server name}
  EC2SG:
    Type: AWS::EC2::SecurityGroup
    Properties:
      GroupName: ec2-sg-cf
      GroupDescription: Allow SSH and HTTP access only MyIP
      VpcId: !Ref VPC
      SecurityGroupIngress:
        # SSH
        - IpProtocol: tcp
          FromPort: 22
          ToPort: 22
          CidrIp: "0.0.0.0/0"
Outputs:
  EC2PublicIP:
    Value: !GetAtt EC2.PublicIp
    Description: Public IP of EC2 instanc