はじめに
AWS Sumerianは、他のAWSサービスと連携したAR/VRのコンテンツを作成することができ、そこが大きな魅力の一つでもあります。
そこで今回は、SumerianとLambdaを直接連携させる形で簡易的なEC2インスタンスのモニタリング機能(+停止機能)を実現するVRアプリケーションを作成してみます。
必要なもの
- AWSアカウント
- ウェブブラウザ(開発および実行環境として。ChromeやFirefoxなど)
構築要素
- EC2インスタンス: 監視/制御の対象となるEC2インスタンス
- Lambda関数: EC2インスタンスの情報取得やシャットダウンを実行する関数
- Cognito IDプール: Sumerianに一時認証情報を払い出すためのIDプール
- IAMロール: Lambda関数やSumerianのスクリプトでAWSのAPIリクエストを実行するためのロール
- Sumerianシーン: 簡易EC2モニタ本体(ウェブブラウザからアクセスするVRアプリケーション)
Step 1: EC2インスタンスの作成
AWSマネジメントコンソールでEC2の管理画面に移り、任意のEC2インスタンスを1つ作成します。
Step 2: Lambda関数の作成
関数1: EC2インスタンスの情報取得処理
-
Lambdaの管理画面に移り、以下の内容でLambda関数を作成します。SumerianのスクリプトはJavaScriptで実装するので、今回はLambda関数もJavaScriptで実装することにしました。
- オプション: 一から作成
- 関数名: GetEc2Info
- ランタイム: Node.js 12.x
- 実行ロール: 基本的な Lambda アクセス権限で新しいロールを作成(デフォルトのまま)
-
関数のコードを以下の内容で書き換えます。
index.js'use strict'; const AWS = require('aws-sdk'); AWS.config.update({region: 'ap-northeast-1'}); const ec2 = new AWS.EC2({apiVersion: '2016-11-15'}); exports.handler = (event, context, callback) => { ec2.describeInstances(event, function(err, data) { if (err) { callback(err, null); } else { callback(null, data); } }); };
-
IAMの管理画面に移り、Lambda関数の作成時に作られたIAMロール GetEc2Info-role-ランダム文字列 を選択します。
-
EC2インスタンスの情報参照リクエスト(ec2:describeInstances)を実行できるよう、このロールにAWS管理ポリシー AmazonEC2ReadOnlyAccess をアタッチします。
-
Lambdaの管理画面に戻り、関数 GetEc2Info で以下のテストイベントを作成し、テストを実行します。関数の実行が成功すると 詳細 にEC2インスタンスの情報が表示されます。
{ "InstanceIds": ["<EC2のインスタンスID>"] }
関数2: EC2インスタンスの停止処理
-
Lambdaの管理画面に移り、以下の内容でLambda関数を作成します。
- オプション: 一から作成
- 関数名: StopEc2Instance
- ランタイム: Node.js 12.x
- 実行ロール: 基本的な Lambda アクセス権限で新しいロールを作成(デフォルトのまま)
-
関数のコードを以下の内容で書き換えます。
index.js'use strict'; const AWS = require('aws-sdk'); AWS.config.update({region: 'ap-northeast-1'}); const ec2 = new AWS.EC2({apiVersion: '2016-11-15'}); exports.handler = (event, context, callback) => { ec2.stopInstances(event, function (err, data) { if (err) { callback(err, null); } else { callback(null, data); } }); };
-
IAMの管理画面に移り、Lambda関数の作成時に作られたIAMロール StopEc2Instance-role-ランダム文字列 を選択します。
-
EC2インスタンスの停止リクエスト(ec2:stopInstances)を実行できるよう、このロールにAWS管理ポリシー AmazonEC2FullAccess をアタッチします。
-
Lambdaの管理画面に戻り、関数 StopEc2Instance で以下のテストイベント(GetEc2Infoで作成したテストイベントと同じ)を作成し、テストを実行します。関数の実行が成功するとEC2インスタンスが停止します。
Step 3: Cognito IDプールの作成
-
Cognitoの管理画面に移り、IDプールの管理 で以下の内容で新しいIDプールを作成します。
- IDプール名: SumerianEC2Monitor
- 認証されていないIDに対してアクセスを有効にする: チェックを入れる
-
Identify the IAM roles to use with your new identity pool の画面ではそのまま 許可 をクリックします。
-
Edit identity pool を選択してプールの詳細を確認します。
-
あとで使用するために、 AWS 認証情報の取得 に表示されている Identity pool ID の値を書き留めます。
-
IAMの管理画面に移り、CognitoのIDプール作成時に作られたIAMロール Cognito_SumerianEC2MonitorUnauth_Role を選択します。
-
Lambda関数の呼び出しリクエスト(lambda:InvokeFunction)を実行できるよう、このロールにAWS管理ポリシー AWSLambdaRole をアタッチします。
Step 4: Sumerianシーンの作成
Sumerianシーンの作成
-
Sumerianの管理画面(ダッシュボード)に移り、Create new scene をクリックし、以下の内容でシーンを作成します。
- 名前: EC2Monitor
-
右側のペインの AWSの設定 コンポーネントで、 Cognito Identity Pool ID の入力欄に書き留めていた Identity pool ID の値を入力します。
HTMLエンティティ(情報表示パネル用)の作成
EC2インスタンスの情報を表示するための2DのHTMLドキュメントを配置し、EC2インスタンスの情報を表示する処理を実装します。
-
画面上部の +Create Entity をクリックし、2D Shapesの HTMLを選択します。
-
右側のペインの HTML Entity コンポーネントで、分かりやすい名前に変更します。
- Name: Panel
-
Transform コンポーネントで位置を調整します。あとで作成するBoxエンティティの上に配置されるようにYの値を変更します。
- Translation(Y): 1.5
-
HTML コンポーネントで Open in Editor をクリックします。
-
Sumerianのテキストエディタが表示されます。HTMLファイルを以下の内容に置き換え、Save をクリックします。
<style> .left{ text-align:left;} .right{ text-align:right;} .center{ text-align:center;} .box{ background:rgba(192,192,192,0.2); color:#FFF; float:left; width:400px; height:160px; margin:10px; padding:15px; font-family:Arial, Helvetica, sans-serif; font-size:18px; font-weight:normal; } .fourcorners{ -moz-border-radius: 15px; -webkit-border-radius: 15px; -khtml-border-radius: 15px; border-radius: 15px; } </style> <section class="box fourcorners"> <p>Instance ID: <span id='InstanceId'></span></p> <p>Instance Type: <span id='InstanceType'></span></p> <p>Instance State: <span id='InstanceState'></span></p> <p>Launch Time: <span id='LaunchTime'></span></p> </section>
Boxエンティティの作成
EC2インスタンスに相当する立方体を配置し、EC2インスタンスの状態や情報を立方体(色)とパネル(文字)に反映する処理を実装します。
-
Sumerianのシーン画面に戻り、画面上部の +Create Entity をクリックし、Box(立方体)を選択します。
-
右側のペインの Box コンポーネントで、分かりやすい名前に変更します。
- Name: Instance
-
Transform コンポーネントで向きを調整します。Y軸を45度回転させてみます。
- Rotation(Y): 45
-
右側のペインの一番下にある +Add Component をクリックし、State Machine を選択します。
-
ペインに追加された State Machine コンポーネントの中にある + をクリックします。
-
画面中央の下側に表示されているペインで、Show current state for: で Instance が選択されていることを確認し、このエンティティの状態とアクションを追加していきます。
- 状態1:初期状態
- Name: Init
- 追加するアクション1: AWS SDK Ready
- 追加するアクション2: Transition
- 状態2:EC2インスタンスの情報取得処理実行
- Name: Execute
- 追加するアクション: Execute Script
- 状態3:ループ処理のウェイト状態
- Name: Wait
- 追加するアクション: Wait
- Waitの設定(Time): 15(秒)
- 状態1:初期状態
-
GetInfo の状態を選択し、右側のペインに追加されている Execute Script アクションの設定で、鉛筆マークのアイコンをクリックします。
-
Sumerianのテキストエディタが表示されます。スクリプトの中の enter() の実装を以下の内容で書き換えます。 <EC2のインスタンスID> は作成したEC2インスタンスのものに置換します。
function enter(args, ctx) { const pendingColor = [1.00, 1.00, 0.00, 1]; // #ffff00 (yellow) const runningColor = [1.00, 0.65, 0.00, 1]; // #ffa500 (orange) const shuttingDownColor = [0.73, 0.33, 0.83, 1]; // #ba55d3 (mediumorchid) const terminatedColor = [0.29, 0.00, 0.51, 1]; // #4b0082 (indigo) const stoppingColor = [0.00, 0.75, 1.00, 1]; // #00bfff (deepskyblue) const stoppedColor = [0.80, 0.80, 0.80, 1]; // #cccccc (light gray) var lambdaParams = { FunctionName: args.lambdaGetEc2Info, InvocationType: 'RequestResponse', Payload: JSON.stringify({ InstanceIds: ["<EC2のインスタンスID>"] }) } ctx.worldData.lambda.invoke(lambdaParams, function(err, data) { if (err) { console.log("Lambda invoke error: " + err); ctx.transitions.failure(); } else { try { var payload = JSON.parse(data.Payload); } catch (err) { console.log("parse error: " + err); ctx.transitions.failure(); } // Boxエンティティの表示色を更新 var instance = payload.Reservations[0].Instances[0]; var color = null; switch (instance.State.Code) { case 0: // pending color = pendingColor; break; case 16: // running color = runningColor; break; case 32: // shutting-down color = shuttingDownColor; break; case 48: // terminated color = terminatedColor; break; case 64: // stopping color = stoppingColor; break; case 80: // stopped color = stoppedColor; break; default: console.log("unknown code: " + instance.State.Code); ctx.transitions.failure(); } ctx.entity.setDiffuse(color); // パネル上の表示内容の更新 var spanElement = null; spanElement = document.getElementById('InstanceId'); spanElement.innerHTML = instance.InstanceId; spanElement = document.getElementById('InstanceType'); spanElement.innerHTML = instance.InstanceType; spanElement = document.getElementById('InstanceState'); spanElement.innerHTML = instance.State.Name; spanElement = document.getElementById('LaunchTime'); spanElement.innerHTML = instance.LaunchTime; ctx.transitions.success(); } }); }
-
同様に、スクリプトの中の parameter の値を以下の内容で書き換えます。 <AWSアカウントID> は使用しているAWSアカウントのものに置換します。
var parameters = [ {type: 'string', key: 'lambdaGetEc2Info', 'default': 'arn:aws:lambda:ap-northeast-1:<AWSアカウントID>:function:GetEc2Info', description: 'Lambda Function GetEc2Info'}, ];
HTMLエンティティ(停止ボタン用)の作成
EC2インスタンスを停止(シャットダウン)するためのボタンとして2DのHTMLドキュメントを配置し、クリックされたときの処理を実装します。
-
画面上部の +Create Entity をクリックし、2D Shapesの HTMLを選択します。
-
右側のペインの HTML Entity コンポーネントで、分かりやすい名前に変更します。
- Name: StopButton
-
Transform コンポーネントで位置を調整します。
- Translation(Y): -1.5
- Translation(Z): 1.5
-
HTML コンポーネントで Open in Editor をクリックします。
-
Sumerianのテキストエディタが表示されます。HTMLファイルを以下の内容に置き換え、Save をクリックします。
<style> .left{ text-align:left;} .right{ text-align:right;} .center{ text-align:center;} .btn-circle-3d { display: inline-block; text-decoration: none; background: #cc0000; color: #FFF; width: 80px; height: 80px; line-height: 45px; border-radius: 50%; text-align: center; font-weight: bold; overflow: hidden; box-shadow: 0px 2px 2px rgba(0, 0, 0, 0.29); border-bottom: solid 3px #bd6565; transition: .4s; } .btn-circle-3d:active { -webkit-transform: translateY(2px); transform: translateY(2px); box-shadow: 0 0 1px rgba(0, 0, 0, 0.15); border-bottom: none; } </style> <section class="btn-circle-3d"> <p>Stop</p> </section>
-
右側のペインの一番下にある +Add Component をクリックし、State Machine を選択します。
-
ペインに追加された State Machine コンポーネントの中にある + をクリックします。
-
画面中央の下側に表示されているペインで、Show current state for: で StopButton が選択されていることを確認し、このエンティティの状態とアクションを追加していきます。
- 状態1:初期状態
- Name: Init
- 追加するアクション1: AWS SDK Ready
- 追加するアクション2: Transition
- 状態2:ユーザーの操作待ち
- Name: WaitForAction
- 追加するアクション: Mouse Up / Touch end
- 状態3:EC2インスタンスの停止処理実行
- Name: Execute
- 追加するアクション: Execute Script
- 状態4:ループ処理のウェイト状態(連続操作を避けるための便宜的な仕組みとして設けました)
- Name: Wait
- 追加するアクション: Wait
- Waitの設定(Time): 60(秒)
- 状態1:初期状態
-
GetInfo の状態を選択し、右側のペインに追加されている Execute Script アクションの設定で、鉛筆マークのアイコンをクリックします。
-
Sumerianのテキストエディタが表示されます。スクリプトの中の enter() の実装を以下の内容で書き換えます。 <EC2のインスタンスID> は作成したEC2インスタンスのものに置換します。
function enter(args, ctx) { console.log('enter()') var lambdaParams = { FunctionName: args.lambdaStopEc2Instance, InvocationType: 'RequestResponse', Payload: JSON.stringify({ InstanceIds: ["<EC2のインスタンスID>"] }) } ctx.worldData.lambda.invoke(lambdaParams, function(err, data) { if (err) { console.log("Lambda invoke error: " + err); ctx.transitions.failure(); } else { ctx.transitions.success(); } }); }
-
同様に、スクリプトの中の parameter の値を以下の内容で書き換えます。 <AWSアカウントID> は使用しているAWSアカウントのものに置換します。
var parameters = [ {type: 'string', key: 'lambdaStopEc2Instance', 'default': 'arn:aws:lambda:ap-northeast-1:<AWSアカウントID>:function:StopEc2Instance', description: 'Lambda Function StopEc2Instance'}, ];
テスト実行
- 左側のペインで EC2Monitor を選択します。
- 右側のペインで EC2Monitor のコンポーネントにある View Scene をクリックします。
- ウェブブラウザのタブが開き、作成したシーンが実行されます。
実行イメージ
作成したシーンを公開すると、PCやスマートフォンのブラウザからアクセスできるようになります。
これはiPhone(Safari)からアクセスしたものです。
Sumerian+Lambdaで簡易EC2モニタを作ってみた pic.twitter.com/jlj0Dws2Lt
— marudai (@marudai86882629) December 26, 2019
アプリケーションの開始直後に(PCの)AWSマネジメントコンソールからEC2インスタンスを起動しています。EC2インスタンスの状態に合わせてパネル上の Instance State の値とBoxエンティティの表示色が変わっています。
起動が完了したあと、アプリケーション上の Stop ボタンをタップしてシャットダウンしています。
まとめ
Lambda関数や連携するAWSサービスを変えると様々なことを実現できます。エンターブライズでの利用も含め、AR/VRアプリケーションの用途が拡がるのではないでしょうか。
今回は初めて自分のユースケースでSumerianを使ったアプリケーションを作ってみましたが、日本語の情報が比較的少なめなので情報収集に少し苦労しました。困ったときは、Sumerian Slack チャネル で検索や質問をしてみるとヒントが得られるかもしれません。また、今回の記事もSumerianとAWSサービスの連携で困ったときの参考になれば幸いです。
参考にしたもの
https://docs.aws.amazon.com/ja_jp/sumerian/latest/userguide/sumerian-guide.pdf
https://github.com/TankAndDozer/AmazonSumerian-DynamoDB-Lambda