AWSのEC2上でblenderを動作させてNice DCVを使ってリモート接続する方法

はじめに

blenderなどの3Dアプリケーションを利用する場合、そこそこのマシンスペックが必要になります。
私は、Mac Book(ノートPC)を愛用しているのですが、少々厳しい(デュアルコアだし、GPUも無い)です。
そこで、AWS上のEC2でblenderを動作させ、Nice DCVで接続することにします。

blenderの公式サイトを参考にすると、2022年10月現在のオススメスペックは以下の通りです。

  • 64-bit eight core CPU
  • 32 GB RAM
  • 2560×1440 display
  • Three button mouse or pen+tablet
  • Graphics card with 8 GB RAM

参考:www.blender.org

上記を満たすには、g4dn.2xlarge (8コア、32GbyteのRAM) 程度がちょうど良さそうですね。

設定手順

今回は、手元のmacから接続します。EC2インスタンスは、LinuxWindowsが選択できますが、Linux側を選択します。
(ここは、慣れている方で良いと思います。)

ざっくりとは、以下の手順です。

  1. blenderとNice DCV設定済みのAMIからEC2インスタンスを起動
  2. ユーザ作成 (Nice DCVで接続した時のログイン用兼作業用)
  3. Nice DCVの設定ファイルの書き換え
  4. クライアントのインストール(既にインストール済みでなければ)

EC2インスタンスの起動

  • AMIは、「Nimble Studio Linux Workstation AMI」を選択します。
    • このAMIには、BlenderとNice DCV(サーバ)が最初からインストール済みとなります。
    • スポットインスタンスを選択する場合は、高度なオプションから「スポットインスタンスをリクエスト」にチェックを入れます。
      • ただし、スポットは割り当てられないこともあるので注意。
  • EC2インスタンスでは、Nice DCVで利用する8443ポートを空けておきます。
  • EC2のIAMRoleを設定します。
    • 以下の通り、Nice DCVのライセンスを取得できる様に設定します。
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": "s3:GetObject",
            "Resource": "arn:aws:s3:::dcv-license.ap-northeast-1/*"
        }
    ]
}

ユーザの作成

起動したEC2インスタンスにログインします(以下、SSHでログインしている前提)。
centosなので、amazon linuxのデフォルトユーザec2-userではなく、centosユーザでのログインとなります。


(1)ユーザを作成します。

sudo adduser [USER_NAME]

(2)パスワードを変更します。

sudo passwd [USER_NAME]

(3)wheelグループへ追加します。

sudo usermod -aG wheel [USER_NAME]

Nice DCVの設定

nice DCVの設定で、作成したユーザでのログインを許可します。
まずは、permissionファイルの作成します。
作成するpermissionファイル( /etc/dcv/permissions.conf としました)の内容は、以下の通りです。

[permissions]
[USER_NAME] allow builtin
%owner% allow builtin

※[USER_NAME]は、上で作成したユーザ名に置き換えてください。

次に、そのファイルを、設定ファイル (/etc/dcv/dcv.conf)で指定します。

[session-management/automatic-console-session]
permissions-file='/etc/dcv/permissions.conf'

後は、dcvserverを再起動して、上の設定を読み込みます。

sudo systemctl restart dcvserver

Nice DCVクライアントのインストール

以下から、ご自分の端末に合うアプリをインストールしてください。
docs.aws.amazon.com

後は、Nice DCVクライアントを起動して、接続します。
ちなみに、グローバルIPを調べたい場合は、以下が便利です。

curl ifconfig.io

SSHのProxyCommandを利用してEC2インスタンスを自動で起動する設定

毎回、EC2インスタンスの起動・終了を実施するのは面倒です。
そこで、SSHのProxyCommandとSSMエージェントを利用し、sshコマンドを実行すると、自動でEC2が起動する様にします。
また、cron コマンドを利用して、一定時間操作がない場合は、自動で終了する様に設定します。

EC2インスタンスのIAM Roleの設定

  • AWSにて、あらかじめ用意されている「AmazonSSMManagedInstanceCore」を追加します。
    • これで、SSMが利用できる様になります。
    • 注:SSMのエージェントは、上で選択したAMIでは、既にインストール済みのため、特段することはないですが、もしSSMエージェントがない場合は、追加します。

IAMユーザのプロファイルのポリシー

接続にいくユーザの権限として、以下を付与します。
インスタンスの起動や終了、ssm周りの権限を付与しています。
とりあえず、グローバルIPで絞っています。ころころ変わって面倒な場合は、MFA認証を必須にするなどでも良いと思います。

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "VisualEditor0",
            "Effect": "Allow",
            "Action": "ec2:StartInstances",
            "Resource": [
                "arn:aws:ec2:ap-northeast-1:[AWS アカウントのID]:instance/[EC2インスタンスのID]",
            ],
            "Condition": {
                "IpAddress": {
                    "aws:SourceIp": "[接続元のIPアドレス]"  #ここは必要に応じて
                }
            }
        },
        {
            "Sid": "VisualEditor1",
            "Effect": "Allow",
            "Action": "ec2:DescribeInstances",
            "Resource": "*",
            "Condition": {
                "IpAddress": {
                    "aws:SourceIp": "[接続元のIPアドレス]"
                }
            }
        },
        {
            "Sid": "VisualEditor2",
            "Effect": "Allow",
            "Action": "ec2:StopInstances",
            "Resource": [
                "arn:aws:ec2:ap-northeast-1:[AWSアカウントのID]:instance/[EC2インスタンスのID]",
            ]
        },
        {
            "Sid": "VisualEditor3",
            "Effect": "Allow",
            "Action": "ssm:StartSession",
            "Resource": [
                "arn:aws:ec2:ap-northeast-1:[AWSアカウントのID]:instance/[EC2インスタンスのID]",
                "arn:aws:ssm:ap-northeast-1::document/AWS-StartSSHSession"
            ]
        },
        {
            "Effect": "Allow",
            "Action": [
                "ssm:TerminateSession",
                "ssm:ResumeSession"
            ],
            "Resource": [
                "arn:aws:ssm:*:*:session/${aws:username}-*"
            ]
        }
    ]
}

SSH ProxyCommandの設定

インスタンス起動のスクリプトの準備

まずは、インスタンスを起動するためのスクリプトを用意します。
ここでは、IAMユーザのプロファイルを「blender」としています。ここは、前の章のポリシーが付いていれば何でも良いです。
渡されたIDのインスタンスが「stopped」状態であれば、起動する様にしています。
(10秒待っているのは、wait_until_running関数だけだと、初回の接続に失敗することがあったためです。)

import time
import boto3
import argparse

class Agent():
    def __init__(self,host):
        self._TARGET_INSTANCE_ID = host
        self._PROFILE_NAME = "blender"
        self._REGION_NAME = "ap-northeast-1"

        self._session = boto3.Session(profile_name=self._PROFILE_NAME)
        self._ec2 = self._session.resource("ec2",region_name=self._REGION_NAME)
        
    def start_instance(self):
        target_instance = self._ec2.Instance(self._TARGET_INSTANCE_ID)
        status = target_instance.state["Name"]

        if status == "running":
            return True
        elif status == "stopped":
            target_instance.start()
            target_instance.wait_until_running()
            time.sleep(10)
            target_instance.reload()
            return target_instance.state["Name"] == "running"
        else:
            return False

    def stop_instance(self):
        target_instance = self._ec2.Instance(self._TARGET_INSTANCE_ID)
        target_instance.stop()
        target_instance.wait_until_stopped()
        target_instance.reload()
        return target_instance.state["Name"] == "stopped"

def setup_arg_parser():
    parser = argparse.ArgumentParser(description="start ec2")
    parser.add_argument("--host",help="EC2 instance ID",required=True)
    return parser
        
def main():
    parser = setup_arg_parser()
    args = parser.parse_args()
    agent = Agent(host=args.host)
    
    if agent.start_instance():
        print("EC2 instance is running")
    else:
        agent.stop_instance()
        print("Failed to start the instance")

if __name__ == "__main__":
    main()

上のpythonスクリプトは、setup_blender.pyとして保存しました。

次に、bashコマンドで、ラップしておきます。
pythonの環境をvirtualenvで設定しているので、環境を呼び出してから、pythonファイルを実行しているだけです。

#!/bin/bash

HOST=$1
source [virtualenvを設定したパス]/bin/activate
python [pythonファイルを置いたパス]/setup_blender.py --host $HOST
.ssh/configの設定
HOST blender_ec2
	   HostName [EC2インスタンスのID]
           User centos
	   Port 22
	   IdentityFile ~/.ssh/[SSHのキー※インスタンス作成時に設定したもの]
	   ProxyCommand sh -c "[パス]/setup.sh %h;  aws ssm start-session --profile blender --region ap-northeast-1 --target %h --document-name AWS-StartSSHSession --parameters 'portNumber=%p'"

blenderと言うIAMユーザのプロファイルを作っています。

上記を全て設定すると、以下を実行するだけで、EC2インスタンスの起動を自動で実施してくれます。

ssh blender_ec2

一定時間操作がない場合、cronによりEC2インスタンスを自動で終了する

以下の、シャットダウンスクリプトを、cronで、5分置きに実行する様にしました。

動きとしては、5分して、ローカルからのsshセッションが1本も無ければ・・・
まずは、フラグを立て(/tmpにファイルを作成し)ます。
再び5分して、フラグが立った状態で、なおsshセッションが1本も無ければインスタンスを落とします。
なので、少なくとも5分は待ってくれて、最長でも10分以内に落としてくれる動きを想定しています。

#/bin/bash

SESSION_COUNT=`w -h | grep localhost | wc -l`
THERE_IS_NO_SESSION=/tmp/there_is_no_session

if [ $SESSION_COUNT -gt 0 ] 
then 
 rm -rf $THERE_IS_NO_SESSION
elif [ -e $THERE_IS_NO_SESSION ]
then
 rm -rf $THERE_IS_NO_SESSION
 sudo shutdown -h now
else
 touch $THERE_IS_NO_SESSION
fi

これを、cronに設定します。

 */5 * * * * /usr/local/bin/shutdown.sh

(/usr/local/bin/にshutdown.shとして置いています。)

おわりに

以上で、mac bookからでも快適にblenderが利用できる様になりました。
(やはりMac bookの1kgを切る重さは捨てられません)