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

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

タグ:bluemix

Node-RED を使うことで IoT データの収集や Web API の実装などが非常に簡単に実現できます。このブログでも何度か紹介していますし、公開されている外部モジュールを使って更にカスタム機能を追加することも可能です。

今回紹介するのは HTTP in ノードに認証機能を追加する node-red-contrib-httpauth ノードです。これを使うと Node-RED に標準装備されている HTTP in ノード(HTTP リクエストノード)に Basic 認証や Digest 認証を簡単に追加することができるようになります:
2018082900


実際に使う場合は、Node-RED の画面右上のメニューから「パレットの管理(Manage Pallette)」を選びます:
2018082901


設定ダイアログが表示されたら、"Palette" の "Install" タブで "httpauth" と検索します。すると node-red-contrib-httpauth ノードが見つかるので、"install" ボタンをクリックしてノードを追加します:
2018082902


インストールが成功すると以下のような表示になります。ここから実際にノードが使えるようになります:
2018082903


この時点でパレットにも "http auth" というノードが追加されていることが確認できます:
2018082904


実際に使う場合、http in ノードの直後に http auth ノードを配置します。この例では http in ノードの直後に http auth ノードを配置し、その後ろに(いつも使っているような)template ノードや function ノードを配置して、最後に http response ノードで HTTP リクエスト可能な API を作りました:
2018082905


template ノードの中身はシンプルにしています(認証が成功するとこの文字列が表示される、というテストです):
2018082906


そして http auth ノードに認証内容を設定します。この例では Basic 認証でレルム文字列は MyRealm 、そしてユーザー名 : user1 &パスワード : pass1 を設定しました。この状態でデプロイします:
2018082907


デプロイ後にウェブブラウザでこの API にアクセスすると、先程設定した http auth が機能し、指定した内容の認証が行われます。具体的にはユーザー名とパスワードを問い合わせるダイアログが表示され、先程指定した内容が入力されないと先へ進めません:
2018082908


上記で設定した内容(ユーザー名 : user1、パスワード : pass1)が正しく入力されると HTTP リクエストが正しく実行され、設定していた文字列が表示されます:
2018082909


この http auth ノードを使うことで、Node-RED で作成する API や Web ページに簡単に Basic 認証をかけることができそうです。


 

IBM Cloud から提供されているサーバーレス環境である Cloud Functions は、オープンソースの Apache OpenWhisk をベースとした FaaS(Functions as a Service)となっており、数あるサーバーレス環境の中でもよりオープンなものとなっています:
2018082301


IBM Cloud の Cloud Functions の場合、専用のダッシュボードが用意されており、手元に開発環境を用意することなく、ここから(ブラウザから)簡易的なアクションを記述/保存/実行することもできるようになっています:
2018082302
 ↑Node.js でアクションを記述している様子


Cloud Functions はこのブラウザからアクションを記述して実行することができる、というメリットがあるのですが、個人的にはここで記述できるアクションはかなり限定的なものだと思っています。理由はここで編集できるものはアクションとして実行する関数の中身だけであって、関連する設定ファイルを変更したりすることはできません。Node.js では npm という強力な外部モジュール連携の仕組みがあり、npm を使って標準の Node.js では提供されていない機能を呼び出して利用することができます。が、この仕組を使うには npm コマンドであらかじめ利用する機能を導入しておくか、 package.json と呼ばれる設定ファイルを変更して利用を宣言しておく必要があります。実行する関数の記述を変更するだけでは対応しきれない仕組みがあり、この機能に関してはこのウェブブラウザからのアクション記述だけでは使えないのでした。


前置きが長くなりましたが、ここからが本エントリの本番です。この外部パッケージを使ったアクションの記述は CLI(Command Line Interface)を使うことで実現できます。その手順を紹介します。

まず利用したい外部パッケージを明示した package.json を用意します。この例では "request" という HTTP クライアントパッケージ(のバージョン 2.88.0)を利用することと、エントリーポイントとなる JavaScript ファイル名(main.js)を指定しています:
{
  "name": "requestAction",
  "version": "0.0.1",
  "main": "main.js",
  "dependencies": {
    "request": "^2.88.0"
  }
}

次にエントリーポイントとなる JavaScript ファイル main.js を以下の内容で用意します。この中で request パッケージを require して使っている点に注目してください:
// main.js
async function myAction( params ){
  try{
    const result = await getHtml( params );
    return result;
  }catch( err ){
    return err;
  }
}

function getHtml( params ){
  return new Promise( function( resolve, reject ){
    if( params.url ){
      const request = require( 'request' );

      var options = {
        method: 'GET',
        url: params.url,
        encoding: null
      };
      request( options, function( err, res, buf ){
        if( err ){
          reject( { status: false, error: err } );
        }else{
          var html = buf.toString( 'utf-8' );
          resolve( { status: true, html: html } );
        }
      });
    }else{
      reject( { status: false, error: 'parameter url is needed.' } );
    }
  });
}

exports.main = myAction;

このアクションでは実行時に url パラメータを指定します(未指定の場合はエラー)。指定された url の HTML コンテンツを request パッケージを使って取得し、その結果を return します。 なお request() は非同期に実行される関数のため、HTML コンテンツを取得する部分だけを関数化(getHTML())し、この関数は Promise オブジェクトを(実行結果が得られたら)返すようにしています。 なお、このアクションは Node.js V8 で実行することを想定しているので、getHTML() 関数を実行する際に async/await を使って非同期に呼ぶようにしています。

この2つのファイル(package.json と main.js)でアクションに必要な設定と内容が用意できました。なお、同じファイルをこちらに用意しておいたので、興味ある方は参照ください:
https://github.com/dotnsf/requestAction


ではこのアクションを実際に IBM Cloud Functions にデプロイして実行するまでの手順を紹介します。今回用意したスクリプトはブラウザからデプロイするのではなく、 CLI (ibmcloud コマンド)を使ってデプロイする必要があります。というわけで、まだ CLI の導入ができていない場合で最初に CLI をデプロイする必要があります。

【ibmcloud コマンド CLI のインストール&セットアップ】
Windows や MacOS、Linux 向けの ibmcloud コマンドは以下のページからインストールできます:
https://console.bluemix.net/docs/cli/reference/ibmcloud/download_cli.html

Windows はダウンロードしてインストール、MacOS や Linux であれば以下のコマンドを実行してインストールします:
$ curl -sL http://ibm.biz/idt-installer | bash

ibmcloud コマンドが導入できたら IBM Cloud Functions を使うためのプラグインをあわせて導入/更新しておきます:
$ ibmcloud plugin install cloud-functions -r Bluemix

また、このあとのコマンドを実行する際に IBM Cloud にログインしている必要があるため、この段階で ibmcloud コマンドによる IBM Cloud へのログインとターゲットの設定を済ませておきます:
$ ibmcloud login

$ ibmcloud target --cf


【Node.js および npm のインストール】
ブラウザからアクションを記述する場合はクライアントに Node.js をインストールする必要はなかったのですが、今回の CLI を使った手順ではローカルで npm コマンドを実行することになります。つまり Node.js や npm がインストールされた環境が必要です。まだ導入していない場合は、公式ページなどから(V8 以上の Node.js の)ダウンロード&インストールを済ませておいてください:
http://nodejs.org/


【ライブラリのインストール、zip ファイル化、デプロイ】
まず package.json の内容にしたがってパッケージ(今回のケースでは request パッケージ)とその依存ライブラリをインストールします:
$ npm install

次にこのディレクトリ内の全ファイルを zip ファイルにまとめます。IBM Cloud Functions では zip ファイルでまとめたアクションをデプロイすることができるため、ここで実行に必要な全てのファイルが揃った zip ファイル(myAction.zip)を作成します:
$ zip -r myAction.zip *

そして、この zip ファイルをアクションとして ibmcloud コマンド CLI でデプロイします:
$ ibmcloud wsk action create requestAction myAction.zip --kind nodejs:8

↑この例ではアクションの名前を "requestAction" として myAction.zip をデプロイしています(アクション名やファイル名は任意です)。なおこの方法でアクションをデプロイする場合、--kind オプションで実行ランタイムの種類を指定する必要があります。今回は Nodejs V8 を指定しています(V6 では未対応の機能を使っているためです)。 また新規にデプロイする場合はこのコマンドになりますが、アクションを変更して同名で上書きデプロイする場合は上記の create 部分を update に変更して実行してください。


【動作確認】
デプロイしたアクションは CLI でもダッシュボード画面からも、どちらでも動作を確認することができます。まずはダッシュボードで確認してみます。ダッシュボードから Actions メニューを選び、アクション一覧の中にデプロイしたアクション(requestAction)が含まれていることを確認します:
2018082401


デプロイしたアクションを選択します。zip ファイルという特殊(?)な方法でデプロイしたので、この画面から直接コードを参照したり変更することはできないのですが、実行(Invoke)したり、実行時の入力パラメータを編集することはできます。今回のアクションは url パラメータを受け取って動作するので、実行前にパラメータを指定します。"Change Input" をクリックします:
2018082402


入力パラメータを編集する画面が表示されます。今回は何らかの url パラメータを指定したいので、
{ "url": "http://dotnsf.blog.jp/" }
のように何らかの実在する(パスワード等なしで参照できる)URL を url パラメータに指定して "Apply" をクリックします:
2018082403
 ↑このブログの URL を指定した例


改めて "Invoke" をクリックして、このアクションを先程の入力パラメータで実行します:
2018082404


アクションが実行され、少し待つと結果の JSON が表示されます。正しく実行されていれば status = true と、指定した URL から取得した HTML が返ってくることが確認できます:
2018082405


全く同じことを ibmcloud CLI からも実行してみます。CLI の場合は以下のようにパラメータを指定して実行し、その実行結果を確認します(実行そのものは --result オプションなしでも行えますが、--result オプションをつけると実行結果が同じ画面に出力され、確認ができます)。期待していたような HTML が表示されれば確認成功です:
$ ibmcloud wsk action invoke requestAction --param url "http://dotnsf.blog.jp/" --result

2018082406


とりあえず、本来の目的であった「Cloud Functions で npm 外部パッケージを使ったアクションを実行する」方法については実現できそうだ、という目処がたちました。


久しぶりに The Weather Company API を使ってみました。なお以下で紹介するサンプルのソースコードはこちらで公開しています:
https://github.com/dotnsf/twc_api

2018081400


使い方は Node.js が導入されているシステムに git clone(またはダウンロード&展開)して、npm install して、node app.js します。成功すると "server starting on XXXX ..." というメッセージが表示されます(この XXXX が動的に決まるポート番号です。以下の例では 6039 番):
$ npm install
$ node app.js
server starting on 6039 ...

ウェブブラウザで上記のポート番号を指定してサンプルアプリケーションに接続します。成功すると東京周辺の(OpenStreetMap の)地図が表示されます:
2018081401


2018/08/14 時点では、このサンプルアプリケーションで3つの The Weather Company API を試すことができ、それぞれ画面上部にある3つのボタンで実行可能です:
2018081402


一番左の "alertsByCoundryCode" ボタンは GET /v1/country/{countrycode}/alerts.json を countrycode = "US" で実行します。つまり "US"(アメリカ合衆国)で現在発生している天候に関する警報の一覧を取得します:
https://twcservice.mybluemix.net/rest-api/#!/Weather_Alerts/v1ccalertheadln


このサンプルアプリケーションでは上位30個の警報を取り出して、その発生位置にマーカーを置き、各警報の内容を参照できるようにしています:
2018081403
(↑この API は以前には存在してなかったような・・・)


真ん中の "currentConditions" ボタンをクリックすると、そのタイミングで地図の中心にある地点を座標を使って GET /v1/geocode/{latitute}/{longitude}/observations.json を実行します。地図の中心にある地点の現在の天候の様子を取得して表示するので、最初に地図をある程度スクロール(マウスドラッグでスクロールします)してから実行し、このサンプルアプリケーションではその情報の一部を表示します:
2018081404


一番右の "historicalData" ボタンをクリックすると、そのタイミングで地図の中心にある地点を座標を使って GET /v1/geocode/{latitute}/{longitude}/observations/timeseries.json を実行します。地図の中心にある地点の過去 24 時間の天候の移り変わりの様子を取得します。このサンプルアプリケーションでは過去 24 時間の気温の移り変わりを表示するようにしています:
2018081405


実装方法など、詳しくは Github 上のソースコードと、The Weather Company API の API Reference を参照ください。

以前に express-ipfilter ライブラリを使って、Node.js アプリの IP アドレスフィルタリングを行うサンプルを紹介しました:
http://dotnsf.blog.jp/archives/1066182158.html

↑ここで紹介したサンプルは一応動くものですが、アプリケーションを IBM Cloud の Cloud Foundry アプリとしてデプロイすると( IP アドレスフィルタリングが)正しく動かないことがわかりました。原因は Cloud Foundry 内のルーティングで x-forwarded-for ヘッダの情報が変わってしまい、正しい IP アドレスを取得できなくなってしまうようでした。

IBM Cloud の Cloud Foundry 環境でもこの IP アドレスフィルタリングを有効にするには、フィルタリングを行う前に Express() の use メソッドを使って、
app.use( 'trust proxy', true );

を呼び出してからフィルタリングを行う必要があります。

(解説)
http://expressjs.com/ja/api.html



 

IBM Cloud から提供されているコグニティブエンジン IBM Watson を使って、
 1. MNIST の手書き数字サンプルデータを学習させて、
 2. 実際に手書き数字データを送信して、認識させる
という、「学習」と「問い合わせ」のコグニティブエンジン一連の作業を再現させてみます(した)。


今回紹介する一連の作業では、IBM Cloud の以下のサービスを連動させて使います:
 ・IBM Watson Studio
 ・IBM Machine Learning
 ・IBM Cloud Storage
 ・SDK for Node.js ランタイム(上記2のサンプルをクラウド上で稼働させる場合)

以下で紹介する手順は IBM Cloud の無料版であるライトアカウントを使っても同様に動かすことができるようにしているので、興味ある方は是非挑戦してみてください。


1. MNIST の手書き数字サンプルデータを学習させる

人工知能とか機械学習とかを勉強していると、そのチュートリアルとして "MNIST" (Modified National Institute of Standards and Technology)を目にする機会があると思っています。機械学習のサンプルとして手書きで描かれた数字の画像データと、そのラベル(何の数字を描いた画像なのか、の答)が大量にサンプルデータとして公開されており、機械学習を説明する際の様々な場面で使われています:
2018050800


今回、この MNIST データを IBM Watson StudioIBM Watson Machine Learning を使って学習させ、かつ問い合わせ用の REST API を用意します。

・・・と、偉そうに書いていますが、この部分の手順については私の尊敬する大先輩・石田剛さんが Qiita 上でわかりやすく紹介していただいています。今回の学習部分についてはこの内容をそっくりそのまま使わせていただくことにします(石田さん、了承ありがとうございます):
Watson Studioのディープラーニング機能(DLaaS)を使ってみた 

2018050801

↑この作業で MNIST の手書き画像を IBM Watson Machine Learning を使って学習させ、その問い合わせ API を REST API で作成する、という所までが完了します。


2. 手書き数字データを送信して、認識させる

マウスやタッチ操作で画面に手書き数字を描き、その内容を 1. の作業で用意した REST API にポストして何の数字と認識するか、を確認できるようなアプリケーションを作成します。

・・・というか、しました(笑):
2018050804


PC またはスマホでこちらのサイトにアクセスすると体験できるようにしています:
https://dotnsf-fingerwrite-mnist.us-east.mybluemix.net/


フロントエンドはもともと以前に「イラツイ」という手描きイラスト付きツイートサービスを作った際のものを丸パク応用し、問い合わせ API を呼び出すバックエンド部分はデプロイしたモデルの Implementation タブ内にある JavaScript の Code Snippets を参考に作りました。この Code Snippents は各種言語のサンプル(アクセストークンを取得してエンドポイントにリクエストするサンプル)が用意されていて、とても便利です:
2018050809


アプリケーションの使い方はマウスまたは指でキャンパス部分に数字を描いて、"fingerwrite" ボタンを押すと、その描いた数字データを上記 1. で作成した REST API を使って識別し、最も可能性が高い、と判断された数値とその確率が表示される、というものです:
2018050805


PC 画面の場合に限りますが、デバッグコンソールを表示した状態で上記を実行すると、可能性が最も高いと思われた結果だけでなく、全ての数値ごとの確率を確認することもできます:
2018050806

↑常に「2」の確率が高くなってる気がする。。原因は学習の調整不足だろうか??それともデータを渡すフロントエンド側??(2018/May/09 ピクセル毎のデータを取り出すロジックに不具合があったので、修正しました)


なお、この 2. のサンプルアプリは Node.js のソースコードを公開しているので、興味ある方は自分でも同様のサイトを作成してみてください:
https://github.com/dotnsf/fingerwrite-mnist

2018050807


このソースコードから動かす場合、事前に settings.js ファイルを編集しておく必要があります:
2018050808


まず上の3つ、 exports.wml_url, exports.wml_username, exports.wml_password の3つの変数の値は 1. で MNIST データを学習した際に使った IBM Watson Machine Learning サービスのサービス資格情報を確認して、その中の url, username, password の値をそれぞれコピー&ペーストしてください(最初の exports.wml_url だけはおそらくデフォルトで url の値になっていると思います。異なっていた場合のみ編集してください):
2018050803


また一番下の exports.ws_endpoint の値は同様に 1. で使った IBM Watson Studio の Web サービスのエンドポイント(学習モデルをデプロイした時に作成した Web サービス画面の Implementation タブから確認できる Scoring End-point の値)をそのまま指定します:
2018050802


ここまでの準備ができた上でアプリケーションを実行します。ローカル環境で動かす場合は普通に npm install して node app で起動します:
$ npm install
$ node app

IBM Cloud (の SDK for Node.js)を使って動かす場合は、cf ツールbx ツールを使って、そのまま cf push で公開されます:
$ cf push (appname)


今回紹介した方法では IBM Watson Studio と IBM Watson Machine Learning を使って画像データを学習させ、その学習結果に対して REST API で問い合わせをする、という機械学習の一連の流れを体験できます。また学習データ(とモデリング)を変更することで、異なる内容の学習をさせる応用もできますし、学習した内容に問い合わせを行う API も自動生成されるので、フロントエンドの開発も非常に楽でした。
 

このページのトップヘ