1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

UL Systems (ウルシステムズ)Advent Calendar 2019

Day 22

Sumerian+Lambdaで簡易EC2モニタを作ってみた

Last updated at Posted at 2019-12-27

はじめに

AWS Sumerianは、他のAWSサービスと連携したAR/VRのコンテンツを作成することができ、そこが大きな魅力の一つでもあります。
そこで今回は、SumerianとLambdaを直接連携させる形で簡易的なEC2インスタンスのモニタリング機能(+停止機能)を実現するVRアプリケーションを作成してみます。

sumerian-image.png

必要なもの

  • AWSアカウント
  • ウェブブラウザ(開発および実行環境として。ChromeやFirefoxなど)

構築要素

  • EC2インスタンス: 監視/制御の対象となるEC2インスタンス
  • Lambda関数: EC2インスタンスの情報取得やシャットダウンを実行する関数
  • Cognito IDプール: Sumerianに一時認証情報を払い出すためのIDプール
  • IAMロール: Lambda関数やSumerianのスクリプトでAWSのAPIリクエストを実行するためのロール
  • Sumerianシーン: 簡易EC2モニタ本体(ウェブブラウザからアクセスするVRアプリケーション)

sumerian-lambda-arch.png

Step 1: EC2インスタンスの作成

AWSマネジメントコンソールでEC2の管理画面に移り、任意のEC2インスタンスを1つ作成します。

Step 2: Lambda関数の作成

関数1: EC2インスタンスの情報取得処理

  1. Lambdaの管理画面に移り、以下の内容でLambda関数を作成します。SumerianのスクリプトはJavaScriptで実装するので、今回はLambda関数もJavaScriptで実装することにしました。

    • オプション: 一から作成
    • 関数名: GetEc2Info
    • ランタイム: Node.js 12.x
    • 実行ロール: 基本的な Lambda アクセス権限で新しいロールを作成(デフォルトのまま)
  2. 関数のコードを以下の内容で書き換えます。

    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);
            }
        });
    };
    
  3. IAMの管理画面に移り、Lambda関数の作成時に作られたIAMロール GetEc2Info-role-ランダム文字列 を選択します。

  4. EC2インスタンスの情報参照リクエスト(ec2:describeInstances)を実行できるよう、このロールにAWS管理ポリシー AmazonEC2ReadOnlyAccess をアタッチします。

  5. Lambdaの管理画面に戻り、関数 GetEc2Info で以下のテストイベントを作成し、テストを実行します。関数の実行が成功すると 詳細 にEC2インスタンスの情報が表示されます。

    {
      "InstanceIds": ["<EC2のインスタンスID>"]
    }
    

関数2: EC2インスタンスの停止処理

  1. Lambdaの管理画面に移り、以下の内容でLambda関数を作成します。

    • オプション: 一から作成
    • 関数名: StopEc2Instance
    • ランタイム: Node.js 12.x
    • 実行ロール: 基本的な Lambda アクセス権限で新しいロールを作成(デフォルトのまま)
  2. 関数のコードを以下の内容で書き換えます。

    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);
            }
        });
    };
    
  3. IAMの管理画面に移り、Lambda関数の作成時に作られたIAMロール StopEc2Instance-role-ランダム文字列 を選択します。

  4. EC2インスタンスの停止リクエスト(ec2:stopInstances)を実行できるよう、このロールにAWS管理ポリシー AmazonEC2FullAccess をアタッチします。

  5. Lambdaの管理画面に戻り、関数 StopEc2Instance で以下のテストイベント(GetEc2Infoで作成したテストイベントと同じ)を作成し、テストを実行します。関数の実行が成功するとEC2インスタンスが停止します。

Step 3: Cognito IDプールの作成

  1. Cognitoの管理画面に移り、IDプールの管理 で以下の内容で新しいIDプールを作成します。

    • IDプール名: SumerianEC2Monitor
    • 認証されていないIDに対してアクセスを有効にする: チェックを入れる
  2. Identify the IAM roles to use with your new identity pool の画面ではそのまま 許可 をクリックします。

  3. Edit identity pool を選択してプールの詳細を確認します。

  4. あとで使用するために、 AWS 認証情報の取得 に表示されている Identity pool ID の値を書き留めます。

  5. IAMの管理画面に移り、CognitoのIDプール作成時に作られたIAMロール Cognito_SumerianEC2MonitorUnauth_Role を選択します。

  6. Lambda関数の呼び出しリクエスト(lambda:InvokeFunction)を実行できるよう、このロールにAWS管理ポリシー AWSLambdaRole をアタッチします。

Step 4: Sumerianシーンの作成

Sumerianシーンの作成

  1. Sumerianの管理画面(ダッシュボード)に移り、Create new scene をクリックし、以下の内容でシーンを作成します。

    • 名前: EC2Monitor
  2. 右側のペインの AWSの設定 コンポーネントで、 Cognito Identity Pool ID の入力欄に書き留めていた Identity pool ID の値を入力します。

HTMLエンティティ(情報表示パネル用)の作成

EC2インスタンスの情報を表示するための2DのHTMLドキュメントを配置し、EC2インスタンスの情報を表示する処理を実装します。

  1. 画面上部の +Create Entity をクリックし、2D Shapesの HTMLを選択します。

  2. 右側のペインの HTML Entity コンポーネントで、分かりやすい名前に変更します。

    • Name: Panel
  3. Transform コンポーネントで位置を調整します。あとで作成するBoxエンティティの上に配置されるようにYの値を変更します。

    • Translation(Y): 1.5
  4. HTML コンポーネントで Open in Editor をクリックします。

  5. 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インスタンスの状態や情報を立方体(色)とパネル(文字)に反映する処理を実装します。

  1. Sumerianのシーン画面に戻り、画面上部の +Create Entity をクリックし、Box(立方体)を選択します。

  2. 右側のペインの Box コンポーネントで、分かりやすい名前に変更します。

    • Name: Instance
  3. Transform コンポーネントで向きを調整します。Y軸を45度回転させてみます。

    • Rotation(Y): 45
  4. 右側のペインの一番下にある +Add Component をクリックし、State Machine を選択します。

  5. ペインに追加された State Machine コンポーネントの中にある をクリックします。

  6. 画面中央の下側に表示されているペインで、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(秒)
  7. 状態の間を以下のようにつなげます。
    InstanceBehavior.png

  8. GetInfo の状態を選択し、右側のペインに追加されている Execute Script アクションの設定で、鉛筆マークのアイコンをクリックします。

  9. 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();
            }
        });
    }
    
  10. 同様に、スクリプトの中の 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ドキュメントを配置し、クリックされたときの処理を実装します。

  1. 画面上部の +Create Entity をクリックし、2D Shapesの HTMLを選択します。

  2. 右側のペインの HTML Entity コンポーネントで、分かりやすい名前に変更します。

    • Name: StopButton
  3. Transform コンポーネントで位置を調整します。

    • Translation(Y): -1.5
    • Translation(Z): 1.5
  4. HTML コンポーネントで Open in Editor をクリックします。

  5. 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>
    
  6. 右側のペインの一番下にある +Add Component をクリックし、State Machine を選択します。

  7. ペインに追加された State Machine コンポーネントの中にある をクリックします。

  8. 画面中央の下側に表示されているペインで、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(秒)
  9. 状態の間を以下のようにつなげます。
    StopButtonBehavior.png

  10. GetInfo の状態を選択し、右側のペインに追加されている Execute Script アクションの設定で、鉛筆マークのアイコンをクリックします。

  11. 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();
            }
        });
    }
    
  12. 同様に、スクリプトの中の 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'},	
    ];
    

テスト実行

  1. 左側のペインで EC2Monitor を選択します。
  2. 右側のペインで EC2Monitor のコンポーネントにある View Scene をクリックします。
  3. ウェブブラウザのタブが開き、作成したシーンが実行されます。

実行イメージ

作成したシーンを公開すると、PCやスマートフォンのブラウザからアクセスできるようになります。
これはiPhone(Safari)からアクセスしたものです。

アプリケーションの開始直後に(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

1
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?