まだプログラマーですが何か?

プログラマーネタ中心。たまに作成したウェブサービス関連の話も https://twitter.com/dotnsf

2016/02

StrongLoop の API プラットフォームにおける利用状況や接続先データソースをグラフィカルな UI で管理するツールが StrongLoop Arc です:
https://strongloop.com/node-js/arc/


StrongLoop (や LoopBack )の slc コマンドを導入済みの環境であればすぐに使うことができます。試しに LoopBack で API を作った時の環境を使って、StrongLoop Arc を利用してみます。LoopBack を導入する手順についてはこちらを参照ください:
CentOS に StrongLoop をインストールする


上記手順を行い、"myapp2" という名前で LoopBack アプリケーションが作られていると仮定します。この環境を使って、StrongLoop Arc を利用するには、アプリケーションフォルダに移動して、"slc arc" コマンドを実行します:
# cd myapp2
# slc arc

するとこんな感じで StrongLoop Arc が起動します。以下の例では 41241 番ポートで起動していますが、このポート番号は動的に変わるようです:

2016021810


ではこの環境にウェブブラウザでアクセスしてみましょう。なお、StrongLoop Arc はリモートアクセスを許可していないので、この StrongLoop 環境を導入したマシンそのものにログインしてからウェブブラウザで http://localhost:XXXXX/ にアクセスする必要があります:
2016021801


StrongLoop Arc を使うにはアカウントが必要です。まだお持ちでない場合は画面右上のハンバーガーメニューから "register" を選んで登録を行います:
2016021802


登録するユーザーのメールアドレスを指定して "Submit":
2016021803


指定アドレスに StrongLoop からメールが届いたら、メール内のリンクをクリック:
2016021804


で、ユーザー名やパスワードやら、残りの項目を登録します:
2016021805


最後に同意チェックを入れて Submit !これでアカウント登録完了です:
2016021806


改めてこの画面から登録ユーザー名(またはメールアドレス)とパスワードを指定してログインします:
2016021807


ログイン直後の画面がこちらです。コマンドラインの slc コマンドで行っていたような作業を GUI で行えるようになります:
2016021808


試しにデータソースを管理してみましょう。"Composer" をクリックすると、現在(LoopBack を使った時に登録した)データソースが確認でき、またここから新規にデータソースを定義することもできるようです:
2016021809


プロセスマネージャーを登録してトレーシング・・・ などもやってみたかったのですが、何故か上手く行かなかったのでそちらについてはまた別途、かな。


(2016/Dec/02 追記 このエントリ内で紹介しているページが消えてしまったため、具体的なサーバーを確認することができなくなってしまいました)


このブログでもたびたび紹介している Loopback などを提供している StrongLoop 社の技術者向けブログを見
ていて気付いたことがあります。

気付いたのはこのブログエントリ、LoopBack から MySQL データベースに接続するための手順を紹介しているガイドです:
Getting Started with the MySQL Connector for LoopBack

(2016/Dec/02 追記 ↑このリンク先ページが消えてしまいました)



ページの中ごろに差し掛かったあたりで・・・ ん!?
2016021801
 (↑実際にはモザイク無し)


LoopBack の MySQL コネクターを使ったデータソースを記述する手順を紹介している箇所で、StrongLoop がデモ用に公開している MySQL サーバーの接続情報が載っているではありませんか!?

まあデモ用なので、このユーザーでできる権限は問い合わせ(つまり SELECT)程度です。とはいえ、そんな MySQL が公開されているとな!

早速 Bluemix 上に phpMyAdmin 環境を構築してみました(笑)。その手順についてはこちらを参照ください:
Bluemix で phpMyAdmin を動かす


1つ注意点として、StrongLoop のデモ用 MySQL はバージョンが低いらしく(僕が確認した限りでは 5.1.73)、最新 phpMyAdmin の推奨環境に合わないためか、そのままだと「バージョンアップしろ」という内容のエラーが表示されました:
2016021800


このエラーを(無理やり)回避するには、phpMyAdmin 側のエラーチェック部分を変更する必要があります。phpMyAdmin の libraries/common.inc.php ファイルの下記該当部分を全てコメントアウトして保存します:
   :
:

# if (PMA_MYSQL_INT_VERSION < $cfg['MysqlMinVersion']['internal']) { # PMA_fatalError( # __('You should upgrade to %s %s or later.'), # array('MySQL', $cfg['MysqlMinVersion']['human']) # ); # }

:

その上で phpMyAdmin 一式を(Bluemix 環境に)デプロイして phpMyAdmin にアクセスし、上記ブログ内で公開されている ID とパスワードでログインすると、デモデータベースの中身にアクセスできました:
2016021802


定義や内容を変更することはできないのですが、動作確認用のサンプルデータベースとして MySQL データベースサーバーが必要な場合に重宝しそうです。


(参考)
http://stackoverflow.com/questions/26222244/phpmyadmin-error-you-should-upgrade-to-mysql-5-5-0-or-later


IBM Bluemix からも提供されている NodeRED データフローエディタの中にある TCP インプットノードを使ってみました:
2016021600


この TCP インプットノードはホスト名とポート番号を指定して、TCP サーバーソケットに直結してデータを受信する、というノードです。

実際に NodeRED で使ってみました。データが受信できることを確認したかったので、以下のような TCP インプットノード1つに、デバッグアウトプットノード1つをつなぐ、というシンプルなデータフローを作ります:
2016021601


TCP インプットノードをダブルクリックして属性を指定します。上から順にポート番号(下図では 12345)とホスト名を指定します。なおここで指定するホスト名のサーバーには、NodeRED サーバーから繋がる必要があります(つまりプライベートネットワーク上にある場合は正しくポートフォワードされている必要があり、加えてファイアウォールで指定ポートに穴を開けておく必要もあります)。また受け取るメッセージの形式を単体文字列に指定しています。この状態で NodeRED 上でデプロイします:
2016021602



一方の TCP サーバー側のアプリケーションを用意します。今回はこんなシンプルな Java アプリを、NodeRED のホストとして指定したサーバー上に作ってみました。(デフォルトで)12345 番ポートでクライアントを待ち受けて、接続があればメッセージを返す、というものです。待受ポート番号は起動時に指定することもできるようにしていますが、今回は使いません:
//. myTcpServer.java
import java.io.*;
import java.net.*;

public class myTcpServer{
  public static void main( String[] argv ) throws Exception{
    int port = 12345; //. デフォルトで待ち受けるポート番号
    try{
      //. 実行時のパラメータで待受ポート番号を変更できるようにする
      if( argv.length > 0 ){
        try{
          port = Integer.parseInt( argv[0] );
        }catch( Exception e ){
        }
      }

      //. サーバーソケット作成
      ServerSocket server = new ServerSocket( port );

      while( true ){
        Socket socket = server.accept(); //. 接続してくるクライアントを待つ

        //. 接続してきたら、メッセージを返す
        OutputStream output = socket.getOutputStream();
        String message = "Connected with port: " + port;
        byte[] out = message.getBytes( "UTF-8" );
        output.write( out );

        //. クライアントを破棄(ループで接続待ち状態へ戻る)
        socket.close();
      }
    }catch( Exception e ){
      e.printStackTrace();
    }
  }
}


これをコンパイルして、実行します:
# javac myTcpServer.java
# java myTcpServer

すると NodeRED 側のリクエストがこのサーバーアプリに繋がり、定期的にメッセージが返されることが分かります:
2016021603


デバッグメッセージをよく見ると、10秒おきにメッセージが送られてくることが分かります。これが TCP インプットノードの仕様なんですかね:
2016021604
ともあれ、TCP インプットノードを使って、TCP ポート直結でデータを受け取ることができる、ということが確認できました。

MQTT を使う必要はなく、ホスト名(IPアドレス)とTCP ポートから直結してデータを受け取ることもできる、というサンプルでした。ただ現実問題としてインターネットに公開されたホストからわざわざ TCP ポート直結のファイアウォールを空けてまでデータを受け取る必要があるか、と言われると、そこはやはり MQTT の方がいいのかなあ、とは思っています。まあどうしても MQTT が使えないようなケースに限った利用ケースかもしれません。


IBM Bluemix を通じて DBaaS で提供されている NoSQL データベース Cloudant 。特に Bluemix の IoT Foundation などと親和性が高く、NodeRED などを使ってセンサーデバイスから送られてくる非構造化データを非常に簡単に格納することができるなど、SQL データベースが苦手とするようなケースでのデータストレージとして便利に使えます:
2016021201


ところで、データを Cloudant に格納する所までは(特に NodeRED を使うなどすれば)コーディングなどをほぼ意識することなく出来てしまうのですが、では実際に集まったデータを外部から利用するにはどうすればいいでしょうか? まあ「プログラミング」といえばプログラミングを行うのですが、SQL やら JDBC やらとは違う方法で各データレコードにアクセスする必要があり、その辺りをまとめてみます。

まずは今回扱う Cloudant 上のデータを用意します。実際にはどんなものでもいいのですが、ここでは以下の様なものとします。具体的には IBM Bluemix と NodeRED を使い、IBM IoT Foundation の MQTT ブローカー経由でラズベリーパイから送信されたデバイスデータを Cloudant 内のデータベースに格納したものを使います。この辺りの具体的な手順はこちらを参照ください:
Bluemix の Node-RED サービスで IoT アプリを作る(2/2)

2016021103


上記リンク先で解説した方法で集めたデータは "iotdata" という名前のデータベース内に格納されているものとします:
2016021101


個別のデータの中身はこのような感じです。"_id", "_rev" というデフォルトのキー値に加えて、ラズベリーパイから取り出した "myName", "cputemp(CPU温度)", "cpuload(CPU負荷率)", "sine(サインカーブの値)" という4つのキー値が各レコードに含まれて JSON 形式で格納されています:
2016021102


この iotdata データベースから目的のデータを取り出す API とその使い方を紹介していきます。その前に API 実行のために必要な情報を Bluemix 画面から取り出しておきます。Bluemix でこの Cloudant サービスをバインドしているランタイムプロジェクトから「環境変数」を参照して、この Cloudant サービスにアクセスするための credentials 情報を調べておきます。具体的には username, password, host, port, url の情報が API 実行時に必要になるため、メモするなどして控えておくようにします:
2016021104


(1) _id 値を指定してドキュメントを取り出す

NoSQL データベースではもっともシンプルな API です。目的のドキュメントを特定する _id 値を指定して、目的のドキュメントを取り出す、というものです。これは環境変数の url 値と、_id 値を指定して、以下の様な API を GET リクエストで実行します:
(https で始まる "url" の値)/(データベース名)/(_id の値)

例えば url の値が https://NNNN:PPPP@xx.xx.xx.xx/ であったとして、データベース名が iotdata、_id 値が 3bfeaf10db8b47a1021dc51397773a99 であれば以下の URL に GET アクセスすることになります:
https://NNNN:PPPP@xx.xx.xx.xx/iotdata/3bfeaf10db8b47a1021dc51397773a99

この URL をウェブブラウザのアドレス欄にそのまま打ち込んでアクセスすれば目的のドキュメントデータが JSON オブジェクトとして得られるはずです:
2016021105

↑目的のドキュメントが得られるはずです。ここまでは簡単。


(2) 特定のキーの値で検索してドキュメントを取り出す

_id 値がわかっていればドキュメントを取り出すことができることは分かりました。でも実際には _id 値がわかっているデータにアクセスする、という使い方はほとんどなく、_id 値ではない値に対して何らかの条件を与えて検索して、その結果のドキュメントのデータを取得する、というケースがほとんどのはずです。 この「検索」を NoSQL データベースではどのように行うか、というのが問題になります。

例えば「CPU 温度(cputemp)が 42 度より大きいレコードのみ取り出して、_id 値でソートする」という検索をするにはどうすればいいでしょう? SQL データベースであれば深く考えずに
select * from iotdata where cputemp > 42 order by _id asc
みたいに実行すればいいだけなのですが、NoSQL データベースでは(当たり前ですが) SQL は使えません。NoSQL データベースで検索するには検索条件を JSON で与えて API をポストする、という必要があります。そしてその前に検索するキー値(今回の例であれば cputemp)のインデックスを作成されている必要があります。念のため現在作成されているインデックスの一覧を確認してみます:
(https で始まる "url" の値)/(データベース名)/_index

上述の条件であれば、以下の URL に GET アクセスすることになります:
https://NNNN:PPPP@xx.xx.xx.xx/iotdata/_index


上記の URL にウェブブラウザでアクセス(GET リクエスト)すると、指定したデータベースに現在作成されているインデックスの一覧が JSON フォーマットで確認することができます:
2016021202

↑デフォルトでは "_id" キーを対象とした "type":"special" なインデックスだけが作成されていることがわかります。


では cputemp キーで検索できるよう、cputemp にインデックスを作成します。新規にインデックスを作成するには、同じ URL に、以下のような JSON データを POST します:
{
  "index": {
    "fields": ["cputemp"]
  },
  "name" : "cputemp-index",
  "type" : "json"
}

↑フィールドに cputemp を指定し、インデックス名を cputemp-index にしています(インデックス名は適当で構いません)。

ウェブブラウザでは POST ができないため、別途ツールを使って実行してください。以下は curl を使った場合のコマンドライン例です(実行結果を緑にしています):
$ curl -X POST https://NNNN:PPPP@xx.xx.xx.xx/iotdata/_index -d '{"index":{"fields":["cputemp"]},"name":"cputemp-index","type":"json"}'

{"result":"created","id":"_design/c233b8dd41b208ef1d9c8370f7ff3906857b0089","name":"cputemp-index"}

↑cputemp-index インデックスの作成が成功しました。この状態で再度上記のインデックス一覧確認 URL にアクセスすると、作成された cputemp-index が追加されて、以下の様な結果になります:
2016021201


cputemp のインデックスが作成されたので、このインデックスを使って CPU 温度でデータの検索を行います。例えば上記で紹介した「CPU 温度が 42 度より上なデータを検索」してみましょう。

この検索を行うための指定は以下の JSON になります:
{
  "selector": {
    "cputemp": {
      "$gt": 42
    }
  }
}

↑"cputemp"が42より大きい("$gt" は "Greater Than"の意)の条件を selector に指定しています。

この JSON データを本文として、以下の URL に POST することで検索を実行します:
(https で始まる "url" の値)/(データベース名)/_find

curl を使う場合は青のような入力になります。成功すると緑のような結果が返ってきます。cputemp が 42 より大きなものだけを取り出すことができました:
$ curl -X POST https://NNNN:PPPP@xx.xx.xx.xx/iotdata/_find -d '{"selector":{"cputemp":{"$gt":42}}}'

{"docs":[
{"_id":"*****","_rev":"xxxxx","myName":"myPi","cputemp":42.24,"cpuload":0,"sine":-0.32},
{"_id":"*****","_rev":"xxxxx","myName":"myPi","cputemp":42.24,"cpuload":0,"sine":-1},
   :
: {"_id":"*****","_rev":"xxxxx","myName":"myPi","cputemp":46.54,"cpuload":0.45,"sine":-0.75} ]}

クエリーには更に条件を加えることもできます。例えばこれは cputemp が 42 より大きなものを最初の10個だけ、_id と _rev と cputemp のフィールドだけ抜き出す、という検索をする場合の指示です:
{
  "selector": {
    "cputemp": {
      "$gt": 42
    }
  },
  "fields": ["_id","_rev","cputemp"],
  "limit": 10
}

これで検索すると検索結果には _id と _rev と cputemp だけが含まれたデータが最初の 10 件分だけ返されます。インデックスとこの検索条件を工夫することで色々なクエリーを実現することができるようになります。
$ curl -X POST https://NNNN:PPPP@xx.xx.xx.xx/iotdata/_find -d '{"selector":{"cputemp":{"$gt":42}},"fields":["_id","_rev","cputemp"],"limit":10}'

{"docs":[
{"_id":"*****","_rev":"xxxxx","cputemp":42.24},
{"_id":"*****","_rev":"xxxxx","cputemp":42.24},
   :
(10件分だけ)
   :
{"_id":"*****","_rev":"xxxxx","cputemp":46.54}
]}


ただ SQL データベースのように「インデックスさえ作れば SQL のような検索が自由にできる」と考えるべきではありません。インデックスを作らないと検索できないともいえるわけで、データリソース(データベースサイズ)への影響を考える必要も考えながら、最小限のインデックスを作るべきです。また SQL のように全文検索を得意としているわけではないので、場合によっては検索エンジンや SQL データベースと組み合わせて使う必要も出てきます。

その一方で分散データベースの得意分野といえるスケーリングや高可用性を活かした使い方ができるので、適材適所的な考えでシステムを構築していく中で、上記のような得意分野を活かした使い方をさせることで便利に使っていけると思っています。


なお、クエリーについてのより詳しい情報も含めた Cloudant の API に関してはこちらのリファレンスを参照ください:
IBM Cloudant Documentation - API Reference

IBM Bluemix からも提供されているスケーラブルな NoSQL データベースである Cloudant 。Bluemix から提供されているのはいわゆる "DBaaS(DataBase as a Service)" と呼ばれている形態です。データベースの管理や多重化、スケーリング対応などは全てクラウドベンダーである IBM が管理しており、利用者は単にデータを読み書きするだけのデータベースとして提供しています。

(特にクラウド環境下で使う場合であれば)これはこれで便利なデータベースだと思うのですが、中にはこの Cloudant をオンプレミス環境で構築したい/使いたい!という猛者な方もいらっしゃるかもしれません。そんな貴方に Cloudant Data Layer Local Edition(通称 "Cloudant Local") をご紹介します:

Cloudant Data Layer Local Edition
cloudantlocal


Cloudant Local はその名の通りで、Cloudant をローカル(オンプレミス)環境に構築するためのミドルウェアです。Cloudant のソフトウェア部分だけを単体で提供するもので、対応環境(X86_64 の Debian 6.0.10 / Ubuntu 12.04.4 / RHEL 6.5 / CentOS 6.5 / Oracle Linux 6.5、または zSystems の RHEL 6.5)の OS 上にミドルウェアとしてインストールして利用します。

なお、現時点で Cloudant Local は無料で提供されているものではありません。IBM ソフトウェア製品として購入する必要があります。興味のある方や詳しく知りたい方はこちらからお問い合わせください:
https://cloudant.com/cloudant-local-info/


さてインストールガイドを見ると、推奨環境としてこのようなアーキテクチャ図が紹介されています。要はロードバランサーに1ノード以上、そしてデータベースとして3ノード以上、計4ノードを最小構成環境にしろ、ということらしいです。Cloudant は分散させることでスケール対応させたり、可用性を上げたりしているわけなので、そういった意味での推奨環境なのでしょう:
cloudant_local_architecture_ports


ただ、まあそのような環境を個人で用意するのは結構ハードル高いです(1ノードあたりのスペックもそこそこ高い)。というわけで自分はデータベースに1ノード(つまり分散なし)、そしてそこにロードバランサーは使わない、という1インスタンス構成で作ってみました。以下、この珍しい環境の導入手順の紹介です。

まずは入手した Cloudant Local の導入モジュールを展開します。2016/Feb/13 時点での Cloudant Local の最新バージョンは 1.0.0.4 ですが、自分が入手したバージョンは 1.0.0.3 で、ファイル名は CLO_DLLED_1.0.0.3_LNX8664_RH,COS_.tar.gz でした。以下このファイルを使う前提で記載します:
# tar xzvf CLO_DLLED_1.0.0.3_LNX8664_RH,COS_.tar.gz

展開後のフォルダに quiet_install.sh というサイレントインストール用のシェルスクリプトがあります。これを使って導入するのですが、シングルノードでの1インスタンス構成で導入するには以下のコマンドを実行します:
# ./quiet_install.sh -a -s -E

3つのオプションの意味を解説します。-a は利用条件の Agreement 、-s はシングルノード構成でのインストール指示、そして -E は EPEL リポジトリを有効にするスイッチです。これでしばらく待つとデフォルト条件で Cloudant Local がインストールされます。


次に Cloudant Local の構成を行います。構成は ~/Cloudant/repo/configure.sh コマンドを実行するだけですが、その前に構成条件(の足りない部分)を ~/Cloudant/repo/configure.ini ファイルで指定しておく必要があります。具体的には _ADMIN_PASSWORD(管理パスワード)や、_CLOUDANT_PASSWORD(システムに作成される cloudant ユーザーのパスワード)、_HAPROXY_PASSWORD(プロクシーのパスワード)、_JMXREMOTE_PASSWORD(リモートJMXのパスワード)、_METRICS_PASSWORD(METRICSのパスワード)といった各種パスワードをこのファイル内に指定します(デフォルトでは全て空になっていて、何かを設定しないと configure.sh は正しく動きません)。
  :
  :
_ADMIN_PASSWORD=*****
  :
_CLOUDANT_PASSWORD=*****
  :
_HAPROXY_PASSWORD=*****
  :
_JMXREMOTE_PASSWORD=*****
  :
_METRICS_PASSWORD=*****
  :
  :

加えて、同ファイル内の DB ノードとロードバランサーの情報をシングルノード構成用に書き直します。具体的にはこのような感じにします(はコメント):
  :
  :
_DB_NODE_CNT=1               # データベースノードは1個
_DB_NODE_ARRAY=( localhost ) # データベースノードのホスト名配列
_DB_IP_ARRAY=( 127.0.0.1 )   # データベースノードのIPアドレス配列
  :
  :
_LB_CNT=0                    # ロードバランサーの数
_LB_ARRAY=                   # ロードバランサーのホスト名配列(指定しない)
  :
  :

データベースノードを1つに指定し、従ってロードバランサーは不要なので使っていないことを指定(_LB_CNT=0)しています。

この状態で ~/Cloudant/repo/configure.sh ファイルを実行して構成を行います:
# ~/Cloudant/repo/configure.sh

ここまでの処理が正しく完了すれば Cloudant Local は正しく導入/構成され、このマシン上に(インストール手順の中で)導入された Nginx を使ったウェブコンソールごと稼働しているはずです。ウェブブラウザを使って、以下の URL にアクセスしてみます:
http://(このマシンのIPアドレス)/dashboard.html

Cloudant Local が正しく導入され、構成も行われていれば以下の様なログイン画面が表示されるので、configure.ini 内で _ADMIN_USER_ID (デフォルトのままであれば admin )と _ADMIN_PASSWORD で指定したユーザー名とパスワードを指定してログインします:
2016021201


正しくログインが完了すると、Bluemix ではお馴染みの Cloudant のダッシュボード画面が表示されます。アドレスもオンプレミス環境のものになっていて、ローカル環境で導入できて動いています!:
2016021200


データの中身は実質的に何もない状態ですが、この時点で OS 含めて 5GB くらい使っています。なので 5GB プラス自分が格納するデータサイズくらいのディスクがあれば一応動かせる、ということになりますね。


Cloudant Local は本来はロードバランサーごと複数台構成にすることでスケーラブルな NoSQL データベースになりますが、利用目的が限られていればこんな1台構成で環境を構築することも可能、でした。オンプレミス環境であれば容量や読み書きの API ごとの課金を心配することもないので、構成や使いみちを自由に決めたい(またはまだ決めたくない)場合の NoSQL データベースの選択肢になりえます。

このページのトップヘ