AWS
Paas
kintone
awsIoT
AWSLambda

PaaSの制限を補完するためのAWS

IoTのセンサーデータを蓄積したい。でもPaaSに設けられた制限が。

 IoT。その言葉は技術者界隈だけでなく、一般にもすっかり知れ渡るようになりました。
 あらゆるものに通信機能を組み込み、人力を借りずにセンサーで取り込んだデータを送信する。それは技術の一つの到達点です。

 ですが、IoTの機械が発信するデータがきちんと生かされているか。と問われると首をかしげざるをえません。それは今の人間と技術の関わり合いの中では、膨大なデータを分析するのは結局人間だからです。

 データをいくら貯めようとそこに意味を与えるのは人間。ビッグデータが脚光を浴びようと、人工知能の脅威が叫ばれようと、やはり人の目による最終的な確認はまだ必要です。人の目にデータを一覧で表示するためには、さまざまな操作が欠かせません。例えばデータの抽出や並び替え。そのデータを加工し、通知し、ビジネスロジックを加え、組織の複数人が情報を共有する。それらの操作を通すことによって、ようやく人間が判断できるのです。

 そうした操作を簡単に構築する仕組みとして、よくPaaSが話題に挙がっています。海外ではSalesForce.comが良く知られています。我が国ではサイボウズ社によるkintoneがシェアを大きく伸ばしています。それらPaaSはAPIも準備されており、IoT機器から発信されたデータがAPIの仕様に従っていれば、簡単に取り込むことができます。

 ですが、一つ問題があります。それはAPIのリクエスト回数の制限です。回数の制限は、いくらPaaS側の容量が潤沢でも、IoTからのデータ量がわずかでも避けられません。たとえばkintoneは1アプリにつき10000回/日とされています(※1)。SalesForce.comは契約エディションの種類によっては受けられるリクエストの数が変わります(※2)。kintoneの場合でいえば一日あたり10000件のリクエストしか受けられません(※3)。一日は秒に換算すると86400秒です。それはつまり8.64秒に一回のリクエストであれば、ぎりぎりリクエストの上限内に収まるということです。

 ただしそれは、IoT機器が一台だけの話です。複数台の端末が1アプリに対してリクエストを行うとなると、許容される時間幅はさらに長くなっていきます。たとえば10台のIoT機器が1アプリに対してリクエストを投げるとすれば、10000回÷10台、つまり1日1台当たり1000回しかリクエストを投げられません。一日の秒数に換算すれば、86.4秒に一回、つまり1.5分に一回です。これではセンサーデータとして有意な変化を見逃しかねません。

 アプリを複数に分け、端末ごと、時間ごとに切り替えることでリクエスト制限を回避する。そんな方法も可能です。ですが、それらのデータを結合させる手間が発生します。リクエスト制限は、kintoneを運営するサイボウズ社にとってみれば、安定した運営を担保するために必要です。では、どうすればよいでしょうか。

 ※1  https://kintone.cybozu.co.jp/jp/price/
 ※2  https://developer.salesforce.com/docs/atlas.ja-jp.208.0.salesforce_app_limits_cheatsheet.meta/salesforce_app_limits_cheatsheet/salesforce_app_limits_platform_api.htm
 ※3 超過しても直ちに使えなくなることはありませんが、警告が出ます。運用の見直しが必要です。https://faq.cybozu.info/alphascope/cybozu/web/kintone/Detail.aspx?id=1874

AWS Lambdaのスクリプトで有意なデータだけを判別します。

 IoTからのデータを直接PaaSに投げず、間にワンクッションを置けばよいのです。

 そしてここに、AWS Lambdaの活躍の場の一つがあります。AWS Lambdaとはハブサービスです。AWSのさまざまなサービスの中でアプリとアプリをつなぐサービス。例えばあるサービスからデータや指示がAWS Lambdaに投げられたとします。AWS Lambdaはそれをトリガーとし、内部で設定したLambda関数で処理を行います。そしてそれを次のサービスにデータや処理指示として渡します。

 このプロセスをIoTに適用することで、IoTからの膨大なデータをAWS Lambdaが処理し、有意なデータかそうでないかを判断できるのです。その判断をIoT機器に任せてもよいのですが、IoT機器は処理能力に限界があります。例えばセンサーをRaspberry Piにつなげた場合は、Raspberry Piの中でPythonで制御できます。そこで条件に応じたフィルタを掛けることも可能です。が、ここではIoTの機器側はセンサーとデータ送信の役割に徹してもらうこととして話を進めます。一方のPaaS側には上に書いたようにリクエスト制限があります。つまり、AWS Lambdaがその緩衝役を担えば、有意なデータだけをPaaSに蓄積できるのです。

 ハブサービスはほかにも多数あります。Microsoft Flowや、IFTTT、Zapier、MyThingsなどが有名です。ですが、残念ながらトリガーの受け取りから発動までの間隔が長く設定されているものがほとんどです。現時点でも最短でも5分間隔と考えて頂いてよいです(※4)。AWS Lambdaはその点、1分ごとに設定できます。また、AWS IoTを挟めば、さらにIoTの膨大なリクエストをさばくことができます。一秒間に9000リクエストまでが許されます(※5)。なので、普通はIoTをAWS Lambdaの間にAWS IoTも挟みます。

 ※4 https://flow.microsoft.com/ja-jp/pricing/
    https://zapier.com/app/billing/plans 
    https://mythings-developers.yahoo.co.jp/document/api_url

 ※5 https://docs.aws.amazon.com/general/latest/gr/aws_service_limits.html#limits_iot

IoTからのリクエストをAWS IoTとAWS Lambdaを通してkintoneに書き込む。

  ここでは、実際にAWS IoTから疑似的にリクエストを発信し、それをkintoneに取り込む例をご紹介します。

  シナリオとしては、AWS IoTのMQTTクライアントを通してリクエストを発信し、それをAWS Lambda上のLambda関数を通してkintoneにPOSTします。その中で、二つのフィルタ方法をご紹介します。

  下流にあたるkintoneの設定から順にさかのぼって説明を行います。

・kintoneのアプリを設定します。

kintone1.png

  ここではiot_akvabit_testというアプリを作りました。フィールドは四つ用意しています。

  (なお、kintoneのアカウントは別途ご用意ください。試用環境もしくはスタンダードプランのアカウントが必要です。)

   ・日時フィールド ・・・フィールドコードは「日時」
   ・端末番号フィールド・・フィールドコードは「端末」
   ・センサー値・・・・・・フィールドコードは「センサー値」
   ・センサー値2・・・・・フィールドコードは「センサー値2」
kintone2.png

  忘れてはいけないのがトークンです。必要な権限を設定します。ここではレコード閲覧、追加、編集としておきます。
kintone3.png

  あと、今後の処理で必要なので、トークン以外にも以下の値をメモしておきます。

   サブドメインの名称 https://******.cybozu.com*****の部分です。
   アプリ番号 https://******.cybozu.com/k/12/ の12にあたる数字です。
    (この二つは環境によって変わります。)

・続いてAWS Lambda関数を設定します。

   Lambda関数の作成については、 https://docs.aws.amazon.com/ja_jp/lambda/latest/dg/getting-started.html
   をご覧の上、作成してください。

  その前に、kintoneの認証情報を、Lambda環境に書いておきます。環境変数に書き込むことで、Lambda関数内にkintoneの認証に関する値を書く必要がなくなります。
aws_lambda3.png

  続いてLambda関数を作成した結果がこちらです。
aws_lambda2.png

  ここではトリガーにAWS IoTを指定していますが、当初はこの部分は空白でよいです。AWS IoTでルールを設定した後、あらためてここを設定します。

・AWS Lambda関数を作成します。

ここではnode.jsを使いました。node.jsについては、別途インストール方法と使用方法を検索の上、ご利用ください。今回はWindows 64ビット版を利用しています。

  以下にLambda関数を貼り付けます。

index.js
var aws = require('aws-sdk');
var request = require('request');

/* kintone用のパラメータ*/

var DOMAIN = process.env.kintone_domain + '.cybozu.com';        //kintoneドメイン(lambda環境変数に記載)
var APP_ID = process.env.kintone_app_id;                        //アプリID(lambda環境変数に記載)
var BASE_URL = "https://" + DOMAIN + '/k/v1/';                  //kintoneUrl
var APITOKEN =  process.env.kintone_api_token;                  //kintoneAPIトークン(lambda環境変数に記載)
var headers = {'X-Cybozu-API-Token': APITOKEN};

exports.handler = function(event, context) {
   var sensoreddt = event.sensoreddt;
   var sensorid = event.sensorid;
   var sensorvalue = event.sensorvalue;
   var sensorvalue2 = event.sensorvalue2;
   var body_post = {
       app: APP_ID,
       record: {
           日時: {
               value: sensoreddt
           },
           端末: {
               value: sensorid
           },
           センサー値: {
               value: sensorvalue
           },
           センサー値2: {
               value: sensorvalue2
           }
       }
   };

   var options_post_sensor_data = {
       url: BASE_URL + 'record.json',
       method: 'POST',
       headers: headers,
       'Content-Type': 'application/json',
       json: body_post
   }

   //レコードを取得
   request(options_post_sensor_data, function (error, response, body) {
       context.done(null, {text: response});
   })
};

  ここでLambad環境変数を5,6,8行目で呼び出していることに注意してください。

  もう一つ、19-29行目では、kintoneの設定したアプリの対象項目のフィールドコードを設定しています。

  このコードでは、IoTからJSONデータを受け取り、kintoneにAPIリクエストを投げています。つまり、この段階でkintoneに対して任意の条件を設定できるのです。

  上記のコードを仮にindex.jsと名付けて任意のフォルダーに保存します。

・続けて圧縮してzipファイルにします。

  
   圧縮元のフォルダー構成を以下に示します。

 node_modules
    ├→request ←※6
    └→safe-buffer ←※7
 index.js

  上記をZipファイルにします。圧縮したファイル名はなんでもよいですが、仮にindex.zipとします。Windowsに標準でついている圧縮機能でZip圧縮して構いません。

  ※6 node.jsのインストールフォルダー の C:\Program Files\nodejs\node_modules\npm\node_modules\request フォルダー内を全てコピーして配置します。
  ※7 node.jsのインストールフォルダー の C:\Program Files\nodejs\node_modules\npm\node_modules\safe-buffer フォルダー内を全てコピーして配置します。

・AWS IoTにモノを登録します。

  AWS IoTのコンソール上で、対象のモノを登録します。さらにそのモノに対して証明書を適用させます。もう一つ、ポリシーを設定し使用できるアカウントの権限に紐づけます。  一連の流れについては   https://docs.aws.amazon.com/ja_jp/iot/latest/developerguide/what-is-aws-iot.html
 をご覧ください。とくに証明書については、上記URLの指示通りに操作していただければ、正常に作成・登録できると思います。

  モノの結果を以下に貼り付けます。
aws_iot1.png
aws_iot2.png

  最後にルールを設定します。ルールを設定することで、IoTからのリクエストのうち、どのデータをAWS Lambdaに投げるかを指定できます。ここではいったんルールについては、全てのデータを通すように設定します。

  また、動作のアクションに先ほどAWS Lambda関数に設定したakvabit_iot_sampleを設定します。
aws_iot5.png

  その結果のIoTの動作ルールは下図のとおりです。
aws_iot4.png

・AWS Lambda関数の設定でトリガーをAWS IoTに設定します。

  すると、このようにIoT機器と関数が紐づいています。あとは実行するだけです。
aws_lambda4.png

・テスト実行してみましょう。

   AWS IoTのMQTTクライアントを選びます。(左下のテストを選択します)
aws_mqtt1.png

  トピックへサブスクリプションに iotbutton/+ と入力し、その右側のボタン「トピックのサブスクライブ」をクリックします。
aws_mqtt2.png

  つづいてトピックの発行画面に移ります。以下のように、黒い部分(JSONリクエストデータ)に以下のように入力します。

    {
    "sensoreddt": "2018-03-01T01:10:00",
     "sensorid": 1,
    "sensorvalue":2350,
    "sensorvalue2":310
    }

さらに、その上のテキストボックス内に
iotbutton/0
と入力します。

  入力結果は下図の通りです。あとは「トピックを発行」ボタンを押すだけです。
aws_mqtt3.png

  すると、下図のようにトピックが発行されます。これによって、IoT機器を持たずに、疑似的にリクエストデータが発行できます。
aws_mqtt4.png

  kintoneを確認すると、以下のようにデータが登録されています。
kintone4.png

・ルールの設定

  先ほど、ルールの設定は全てのデータを無条件にリクエストとして投げると書きました。

  ここでは以下のようにルールを設定してみます。
aws_iot6.png

  ここで条件に sensorvalue > 2340 と指定しました。

  条件の sensorvalueに注目してください。先ほど、MQTTクライアントで以下のJSONデータを投げました。この三つ目のキーがsensorvalueです。つまりこの条件はsensorvalueの値が 2340よりも大きい場合のみ、AWS Lambdaにデータを投げるということなのです。

   {
    "sensoreddt": "2018-03-01T01:10:00",
     "sensorid": 1,
    "sensorvalue":2350,
    "sensorvalue2":310
   }

  なので、条件を sensorvalue > 2350 とした場合は、上のJSONデータはkintoneまで届きません。

  ここで、条件式を設定することによって、IoTからのリクエストをフィルタできます。AWS LambdaのLambda関数でもnode.jsの文法にのっとって条件式を加えることでより詳細なフィルタが行えます。ですが、AWS IoTでも簡単なフィルタがかけられるのです。

まとめ

  PaaSにはリクエストを受け付けられる数に上限があります。それはIoTの運用には耐えられないレベルです。そのため、IoT機器からのデータのうち条件に基づいたフィルタをかけることで、PaaS側にリクエストされるデータの回数を軽減できます。

  フィルタはAWS IoTとAWS Lambdaのどちらかでも設定できます。AWS IoTでは値の比較いよってフィルタが可能です。AWS Lambda内のLambda関数では、node.jsの文法に則った条件式が書けるので、より複雑な条件でのフィルタが可能です。

  高頻度データであっても、その二つでフィルタをかけることで、有意なデータだけをPaaSで扱うことができます。