enebularのクラウド実行環境からDynamoDBを利用してみる

# はじめに

残念ながら、enebularが提供するクラウド実行環境だけではデータの保存をすることができません。
本記事では、データの保存先としてAWSのDynamoDBを利用する方法を紹介します。

以下に本記事で作成するシステムの構成を示します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/380472/6232a7b5-512d-12a8-6aff-73da9184e8d3.png)

DynamoDBが動作するAWS環境は、ユーザー自身で準備頂く必要があります。
ユーザーはそのAWS環境でDynamoDBのテーブル作成と、DynamoDBにアクセス可能なユーザーを予め作成頂き、アクセスキーとシークレットキーを準備します。

クラウド実行環境で動作するNode-REDフローに、そのアクセスキーとシークレットアクセスキーをセットすることでDynamoDBへのアクセスを可能にします。

# AWS環境準備

ユーザーのAWS環境にDynamoDBのテーブルおよびDynamoDBにアクセス可能なユーザーの追加を行います。

## DynamoDBのテーブル作成

AWSのコンソールにログインし、サービスの一覧からDynamoDBを選択し、ダッシュボード画面を表示します(右上のメニューから利用したいリージョンも選択してください)。

サイドメニューの`テーブル`をクリックし、表示された画面で`テーブルの作成`ボタンを押してください。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/380472/0b282c24-f58e-9f57-b295-33e7970a68c5.png)

テーブルの詳細情報を入力してください。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/380472/0bb6b69d-62f1-ffeb-4533-0bda3bd42205.png)

この記事では`sample-table`というテーブルを作成し、パーティションキーは`id`(データの種類を格納)、ソートキーは`ts`(登録時刻を格納)としました。
テーブル設定は`デフォルト設定`にします。

`テーブルの作成`ボタンを押すとテーブルが作成されます。

作成されたテーブルは、以下のように一覧に表示されます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/380472/c027c6b9-5974-753b-4b41-4b3bf70fd6ff.png)

## DynamoDBにアクセスするユーザー作成

次に、DynamoDBにアクセスするユーザーを作成します。
AWSのサービスの一覧からIAMを選択し、ダッシュボードを表示します。

サイドメニューの`ユーザー`をクリックし、ユーザーの一覧画面上部の`ユーザーを追加`ボタンをクリックします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/380472/9def1f32-3a57-8beb-d9a0-8cd5a1859a01.png)

ユーザー名は`sample-dynamodb-user`としました。
このユーザーは、プログラムから利用するユーザーであるため、`アクセスキー - プログラムによるアクセス`にのみチェックを入れ、`次のステップ:アクセス権限`ボタンを押します。

以下の画面が表示されるため、`既存のポリシーを直接アタッチ`を選択し、ポリシーのフィルタに`dynamodb`と入力し、`AmazonDynamoDBFullAccess`にチェックを入れます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/380472/7e1047ac-059b-b334-a845-977e77349bff.png)

`次のステップ:タグ`ボタンをクリックします。
必要に応じタグを入力し、`次のステップ:確認`ボタンをクリックしてください。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/380472/0c06a669-6b7a-2ec9-48fb-f4c72b6a8bf6.png)

設定を確認し、問題なければ`ユーザーの作成`ボタンをクリックしてください。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/380472/ba84bbbd-6dab-7a9d-ed13-3ff64856712d.png)

以下の画面が表示されたら、`.csvのダウンロード`ボタンをクリックしてください。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/380472/66ef03e9-3a41-0648-c7d5-dabcd2ddaadb.png)

アクセスキーとシークレットアクセスキーを含むファイルがダウンロードされました。アクセスキーとシークレットアクセスキーは、のちほどNode-REDフローに組み込んで利用します。
これでユーザーの作成は完了です。

# Node-REDフローの作成

enebularで、DynamoDBにアクセスするNode-REDのサンプルフローを作成します。

今回は、以下を実施するサンプルを作成します。
* DynamoDBに以下のデータを書き込みます
    * id: AまたはB
    * ts: 現在時刻(ミリ秒数)
    * value: 0〜100のランダムな値
* 上記書き込みが終わった後に直近1時間の間に書き込まれたデータを検索し、データ数をカウントします
* 結果はクラウド実行環境のログに出力します

## 作成するフローの全体像

今回作成するフローの全体像を先に説明します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/380472/d25ebd88-23f1-8fb6-9bbc-895ee23960ff.png)

1. 起点は`LCDP`ノードです
1. `ダミーデータ作成`ではidがAまたはB、tsが現在時刻、valueにランダムな値を含むデータを作成します
1. `DynamoDB PutItem`では、DynamoDBに上記データを書き込みます
1. 成功すると、`Aの検索条件`で、直近1時間に書き込まれたidがAのデータを検索する条件を作成します
1. `DynamoDB Query`で、上記条件を実行し、検索結果を取得します
1. 成功したら`Aの検索結果`で検索結果のデータ件数をログに出力し、Bの検索を実施します(Aと同様です)

## lcdp-toolkitノードのインポート

[enebular](https://enebular.com/app/)にログインし、プロジェクトの作成を行います。
プロジェクトが作成できたら、クラウド実行環境を利用するために必要となる`lcdp-toolkit`ノードをインポートします。
enebular上部の`Discover Assets`をクリックし、検索欄に`lcdp-toolkit`を入力し、名称を選択すると以下のプライベートノードが見つかります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/380472/29d4f50c-17c7-0295-8864-55ccf7490d0b.png)

カードをクリックし、上部の`インポート`ボタンを押し、インポート先のプロジェクトを選択したら、そのプロジェクトへのインポートが実施されます。

## フローの作成

次に、フローの作成を行います。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/380472/4bf91440-d6af-c158-dd16-83713c0030df.png)

フローを作成すると以下の画面が表示されるので、右の`編集`ボタンを押します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/380472/61101d93-85bd-6890-9ceb-b6c95832c8d5.png)

フローエディタが起動します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/380472/9d9ff386-06ca-faba-d069-d7a95050ad40.png)

## DynamoDBにアクセスするためのノードのインストール

右上のハンバーガーメニューから`パレットの管理`を選択します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/380472/61184471-4557-92d3-892a-bfea18ffd42e.png)

`ノードを追加`タブを選択し、`ノードを検索`欄に`dynamodb`と入力すると候補が表示されます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/380472/d8503e77-9cca-7cd7-8eda-43233c632d27.png)

今回は、比較的更新時期が新しい`node-red-contrib-aws`を利用することにしました。
`ノードを追加`ボタンを押し、インストールを行います。
インストールが成功すると、左側のパレットに複数のAWSの関連ノードが追加されます。この中から`AWS DynamoDB`ノードをドラッグ&ドロップでキャンバスに表示し、ダブルクリックします。

プロパティ編集画面が表示されます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/380472/46592738-9e9b-5b3a-9cda-187469def992.png)

AWS欄の右の鉛筆アイコンをクリックし、AWSのアクセス情報を入力します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/380472/4ebcf5a5-5b98-3e13-619c-01a27b81fa43.png)

以下の情報を入力してください。
* Region: 利用するDynamoDBのリージョン
* Access Id: AWSに追加したユーザーのアクセスキー(ダウンロードしたｃｓｖファイルを参照)
* Secret Key: AWSに追加したユーザーのシークレットアクセスキー(ダウンロードしたｃｓｖファイルを参照)

## DynamoDBへの書き込みと読み込み

DynamoDBのプロパティのOperationをクリックするとDynamoDBに対して実施したい処理を選択できます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/380472/27e8988e-3c4d-39a8-c20c-2c3d9f689f35.png)
今回、書き込みには、`PutItem`、読み込みには`Query`を利用します。

### PutItem

`ダミーデータ作成`はfunctionノードです。
以下の処理を実行することで`DynamoDB PutItem`に入力するmsgを作成します。

```
msg.TableName = "sample-table";
const TYPES = ["A", "B"];
const value = Math.random() * 100;
msg.Item = {};
msg.Item.id = { S: TYPES[Math.floor(Math.random()*TYPES.length)]};
msg.Item.ts = { N: String(Date.now())};
msg.Item.value = { N: String(value)};
return msg;
```
上記処理にて作成されるmsgは以下となります。

### Query

`Aの検索条件`および`Bの検索条件`はfunctionノードです。
以下の処理を実行することで`DynamoDB Query`に入力するmsgを作成します。

```
msg.TableName = "sample-table";
const from = Date.now() - 3600 * 1000;
const to = Date.now();
msg.KeyConditionExpression = "id = :id and ts BETWEEN :from AND :to";
msg.ExpressionAttributeValues = {
        ":id": { "S": "A"},
        ":from": { "N": String(from) },
        ":to"  : { "N": String(to) }
    };
return msg;
```

実行した結果の例は以下です。
```
[
    {
        "ts":{"N":"1671555043805"},
        "value":{"N":"42.92672704782301"},
        "id":{"S":"A"}
    },
    {
        "ts":{"N":"1671557874922"},
        "value":{"N":"12.97791476304242"},
        "id":{"S":"A"}
    }
]
```

ここで紹介するフローはenebularのDiscover Assetsに公開(下記リンク参照)しましたのでご利用ください。
[sample-dynamodb](https://enebular.com/discover/flow/32d6c8f0-5470-4bae-8047-b958bebe9bf8)

# クラウド実行環境で実行

作成したフローをクラウド実行環境にデプロイし、動作させます。
今回は、20分に一回、スケジュール実行するようにしました。

## クラウド実行環境の作成

enebularのサイドメニューの`クラウド実行環境`をクリックします。
クラウド実行環境の一覧画面が表示されるので、右下の`+`ボタンをクリックします。
作成する実行環境の名称を入力(今回は`sample-dynamodb-ee`としました)し、`OK`ボタンをクリックするとクラウド実行環境が作成されます。

クラウド実行環境が作成されたら、上部の`設定`タブをクリックし、スケジュール実行の設定を行います。
設定画面上部の`設定を編集する`リンクをクリックすると、設定を編集できるようになります。

スケジュールトリガーをONにし、毎時0分、20分、40分に実行されるように設定を行います。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/380472/40920b6f-b751-1ed6-9208-374859b4e98a.png)

設定が完了したら、右下の`保存`ボタンをクリックします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/380472/4e619ff4-901e-4f95-f71a-091075a0bbdb.png)

## クラウド実行環境へのデプロイ

作成したNode-REDフローをクラウド実行環境にデプロイします。
作成したフローの概要画面の右にある`デプロイ`ボタンをクリックします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/380472/3d38cbd4-85d9-9cdf-52e3-5e16746a4a3f.png)

以下のダイアログが表示されるので、`クラウド実行環境を選択`ボタンをクリックします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/380472/51c5c350-23a7-6f20-833a-8288448512b9.png)

デプロイ対象を選択し、`デプロイ`ボタンをクリックするとクラウド実行環境へのデプロイが開始します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/380472/aef50f24-80a5-7d38-a6a6-90504d5be368.png)

## 実行結果の確認

クラウド実行環境の`ログ`タブを選択sうることで実行ログを確認できます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/380472/3a11986b-b36e-362c-2d4c-ac628cef4daa.png)

ログから、毎時、0分、20分、40分に実行されていることが分かるかと思います。
また、登録成功やA、B各々の直近1時間以内の検索結果数も確認できます。

# まとめ

enebularは、データを保存する機能を提供していないため、データの保存を行いたい場合、外部のデータストアサービスを組み合わせて実現する必要があります。
データストアサービスは、種類に応じ多様な特徴を持っているため、実現したいアプリに応じてマッチするデータストアサービスの検討が必要です。
もし、作成したいアプリのデータストアとしてDynamoDBがマッチする場合には、本サンプルが開発の参考になれば幸いです。