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

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

2016年04月

(今回のエントリはこの続編的な内容になっています)
LINE Bot を IBM Bluemix で作る


IBM Bluemix のサードパーティ製サービスの中に "Statica" というものがあります:
2016041400


このサービスはサーバー間プロクシーを実現するものです。Bluemix 上のランタイムから Bluemix 内外の API を呼び出す際の、リクエスト元 IP アドレスを隠して固定の IP アドレスにすることができるようになります。

例えば Bluemix 上のランタイム IP アドレスをリクエスト先サーバーのホワイトリストにあらかじめ登録しておかないと利用できないような API がある場合、Bluemix ランタイムの IP アドレスは再ステージ毎に変わってしまい、かつ変わった後の IP アドレスを調べるのが面倒でした(参照)。そんな場合にこのサービスを使うことでランタイムサーバーの IP アドレスが固定化されているかのように振る舞うことができるようになります。

実はこのサービスを使うと、LINE Bot API を作る際に非常に便利です。このページで紹介したように LINE Bot API でもコール元の IP アドレスをホワイトリスト登録しておく必要があって非常に面倒だったのですが、この Statica サービスを併用することで解決でき、再ステージング毎のホワイトリスト更新が不要になります。

なお、Statica は有償のサービスですが、IBM Bluemix の Starter プランを選択することで月 250 リクエスト&帯域 100MB までは無料でお使いいただくことが可能です。お試しになる場合はこちらをご利用ください:
2016041406


実際に LINE Bot API で試してみた様子がこちらです。まず Statica サービスをランタイムに追加しました。サービスの「資格情報の表示」をクリックしてクレデンシャル情報を確認します:
2016041401


このような画面が表示されます。モザイクのかかっている "STATICA_URL" の値がプロクシー URL です。この値をメモリしておくか、または実行時に動的に取り出して使うようにします:
2016041402


Statica サービスをクリックすると、作成した Statica サービスインスタンスの管理ダッシュボード画面にアクセスするためのリンクが現れます。これをクリックしてダッシュボードを開きます:
2016041403


Statica のダッシュボード画面が表示されました。この画面内の "Your Static IPs are X.X.X.X and Y.Y.Y.Y" と書かれている部分の X.X.X.X と Y.Y.Y.Y が固定に見せかける IP アドレスです:
2016041404


この2つの IP アドレスを LINE developers サイトのホワイトリストに登録します:
2016041405


後は実際にコールバック API を実行する際に、この Statica サービスインスタンスをプロクシーとして利用すればいいだけです。例えばこのようなコードにしました。変更前と異なる部分を赤く記しておきます:
<?php

$json_string = file_get_contents( 'php://input' );
$jsonObj = json_decode( $json_string );
$results = $jsonObj->{"result"};
$cnt = count($results);
for( $i = 0; $i < $cnt; $i ++ ){
  $result = $results[$i];
  $to = $result->{"content"}->{"from"};


  $cur = "USDJPY";
  $_cur = $result->{"content"}->{"text"};
  if( $_cur ){
    $cur = $_cur;
  }
  $cur = strtoupper( $cur );

  $fx_url = "http://fx.mybluemix.net/";
  $text = file_get_contents( $fx_url );
  $json = json_decode( $text );
  $datetime = $json->datetime;
  
  $rate = $json->rate;
  $r = "(" . $datetime . ")" . $cur . ":" . $rate->{$cur};

  $response_format_text = ['contentType'=>1,"toType"=>1,"text"=>$r];
  $post_data = ["to"=>[$to],"toChannel"=>"1383378250","eventType"=>"138311608800106203","content"=>$response_format_text];


  //. statica をチェック
  $proxy_url = '';
  $proxy_auth = '';
  if( getenv( 'VCAP_SERVICES' ) ){
    $vcap = json_decode( getenv( 'VCAP_SERVICES' ), true );
  
    $credentials1 = $vcap['statica'][0]['credentials'];
    if( $credentials1 != NULL ){
      $statica_url = $credentials1['STATICA_URL'];
      list($v1,$v2) = split( '@', $statica_url );
      list($v3,$v4) = split( '//', $v1 );
      $proxy_url = $v3 . '//' . $v2;
      $proxy_auth = $v4;
    }
  }

  $ch = curl_init("https://trialbot-api.line.me/v1/events");
  curl_setopt($ch, CURLOPT_POST, true);
  curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
  curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
  curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($post_data));
  curl_setopt($ch, CURLOPT_HTTPHEADER, array(
    'Content-Type: application/json; charser=UTF-8',
    'X-Line-ChannelID: (Channel ID)',
    'X-Line-ChannelSecret: (Channel Secret)',
    'X-Line-Trusted-User-With-ACL: (MID)'
    ));
  if( $proxy_url && $proxy_auth ){
    curl_setopt( $ch, CURLOPT_PROXY, $proxy_url );
    curl_setopt( $ch, CURLOPT_PROXYAUTH, CURLAUTH_BASIC );
    curl_setopt( $ch, CURLOPT_PROXYUSERPWD, $proxy_auth );
  }
  $res = curl_exec($ch);
  curl_close($ch);
}
?>
↑Statica サービスが使われている場合のみプロクシー指定して実行するようなコードにしています


このコールバック PHP ファイルをデプロイすれば動きますし、(前回苦労したような部分が消えるという意味でも) Bluemix 上での LINE Bot 開発がより簡単になると思われます。


4月7日に LINE が LINE トークを使った bot 開発用のアカウント "BOT API Trial Account" を先着1万名で提供する、という発表がありました:
http://www.itmedia.co.jp/news/articles/1604/07/news134.html


このニュースはあっという間に広まり、その作り方や実際に作ったボットが早くも多く公開されています。これまでは LINE ビジネスコネクトを通したビジネスアカウントがないと作れなかった LINE のボットが誰でも作れるようになりました。今回の発表で提供されている機能では作ったボットは 50 名のユーザーに対してのみ公開可能(ボットは 50 人までとしか友達になれない)という制限は付きますが、仲間内で使うぶんには充分だし、今後この制約が拡張されることがあればより便利に使えると思っています。 ちなみに昨日友人が試した限りでは、まだアカウントの申し込みが可能でした。

自分も早速 Bot を作って動かしてみました。自分の場合は IBM Bluemix を使って Bot を動かしています。Bot の作り方そのものは Qiita などに数多くの投稿がされているようなので、そちらを参照ください。以下は IBM Bluemix を使って Bot を動かす場合のコツや注意点を中心にまとめたものです。



(1) Bluemix のランタイム作成

まず Bluemix 上に Bot として動くことになるウェブアプリケーションのランタイムを作成します。ちなみに Bluemix のランタイムは標準で SSL が動き、Let's Encrypt 問題のような「この SSL 証明書はダメ」のような現象はおきていません。要するに Bluemix でランタイムを作って動かせばあまり深く考えなくても LINE Bot で使える、ということです。

Bluemix に用意されたランタイムはどの種類を使っても大丈夫だと思います。自分は PHP を使ってコールバック部分を作るつもりなので PHP にしました。Bluemix のサービスは必要に応じて追加してください(なくても動きます):
2016041304
 ↑dotnsf-line.mybluemix.net で PHP サーバーを作成


(2) BOT API Trial Account 作成

次に LINE BUSINESS CENTER へ行き、BOT API Trial Account を作成します:
2016041301


で、Bot を新規に作成して登録します。Channel ID と Channel Secret、MID は後で使うことになるのでメモしておきましょう。App icon は Bot が実際に LINE トーク上に現れる時のアイコンになるので、何か入れておいたほうが面白いと思います:
2016041302


重要な要素が Callback URL です。LINE トーク上でこの Bot に話しかけると、ここで指定した URL が呼ばれて実行されます。ここに指定できる URL は SSL が前提で、かつポート番号 443 を明示指定する必要があります。今回は上記で作成した Bluemix 上のランタイムに callback.php(この下で作ります)をデプロイして動かすので、Callback URL としては https://dotnsf-line.mybluemix.net:443/callback.php という指定になります:
2016041303

White List はこの後で指定します。


(3) Bot 作成&デプロイ

コールバックで動くことになる Bot アプリケーションを作ってデプロイします。ここは Bluemix だから特別に・・・という部分ではありません。自分はこの API を使って、呼びかけた通過ペア(例えば "USDJPY" とか "EURJPY" とか)に対してリアルタイムの為替価格を表示する、という Bot にしてみました:
2016041301
 ↑実際に動いている様子。問いかけた通貨の現在価格を返答してくれます。


ソースコードはこんな感じ。コード内の (Channel ID), (Channel Secret), (MID) の部分には上記で取得した実際の値を指定して書き換えてください:
<?php

$json_string = file_get_contents( 'php://input' );
$jsonObj = json_decode( $json_string );
$results = $jsonObj->{"result"};
$cnt = count($results);
for( $i = 0; $i < $cnt; $i ++ ){
  $result = $results[$i];
  $to = $result->{"content"}->{"from"};


  $cur = "USDJPY";
  $_cur = $result->{"content"}->{"text"};
  if( $_cur ){
    $cur = $_cur;
  }
  $cur = strtoupper( $cur );

  $fx_url = "http://fx.mybluemix.net/";
  $text = file_get_contents( $fx_url );
  $json = json_decode( $text );
  $datetime = $json->datetime;
  
  $rate = $json->rate;
  $r = "(" . $datetime . ")" . $cur . ":" . $rate->{$cur};

  $response_format_text = ['contentType'=>1,"toType"=>1,"text"=>$r];
  $post_data = ["to"=>[$to],"toChannel"=>"1383378250","eventType"=>"138311608800106203","content"=>$response_format_text];

  $ch = curl_init("https://trialbot-api.line.me/v1/events");
  curl_setopt($ch, CURLOPT_POST, true);
  curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
  curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
  curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($post_data));
  curl_setopt($ch, CURLOPT_HTTPHEADER, array(
    'Content-Type: application/json; charser=UTF-8',
    'X-Line-ChannelID: (Channel ID)',
    'X-Line-ChannelSecret: (Channel Secret)',
    'X-Line-Trusted-User-With-ACL: (MID)'
    ));
  $res = curl_exec($ch);
  curl_close($ch);
}
?>

このコードを Bluemix にデプロイすればOK、なのですが、ちょっとまだ足りない部分があるのでデプロイは次の (4) の後にまとめて行うことにします。


(注 この (4) の作業に関してはこちらの作業を行うことで不要にすることもできます)

(4) WhiteList 登録

LINE Bot の、特に Bluemix ランタイムで LINE Bot を動かす上での最大の関所がこの WhiteList 登録だと思ってます。Bot が LINE のサーバーに対して返答の POST を実行する際、あらかじめ許可されたサーバーからのリクエストのみが受け付けられるようなホワイトリストの仕組みが用意されています。つまり LINE Bot を動かすにはどの IP アドレスからリクエストするのかをあらかじめ登録しておく必要があります。

一方で Bluemix のランタイムは再起動や再ステージングを行う度にリクエスト元の IP アドレスが変わる仕様になっています。Bluemix のアプリケーションはデプロイするたびに再ステージングを行うので、要するに作りなおしたアプリをデプロイする度にリクエスト元の IP アドレスを調べてホワイトリストを更新する必要があるのでした。この仕組がちと面倒で、注意する必要があります。

そして更に話がややこしいことに、このリクエスト元の IP アドレスを調べる方法というのがまた面倒だったりします。IaaS のアプリケーションサーバーであれば SSH などでログインして「確認くん」などで調べる方法もありますが、PaaS のランタイムである Bluemix だとそれも簡単ではありません。

方法は何通りかあると思うのですが、基本的な考え方は外部の HTTP サーバーや DB サーバーを用意し、Bluemix からそのサーバーにアクセスを行って、ログを見る(ログのアクセス元の IP アドレスを調べる)という方法です。自宅サーバーなどでダイナミック DNS を使う時のようなテクニックが必要になると思っています。

僕がやってるのはこんな感じ。わざとつながらない DB サーバーに Bluemix ランタイムからアクセスして(アクセスするような PHP アプリを書いて実行して)エラーログを参照する、という方法です。良い子はマネしちゃいけません:
2016041302


というわけで、順序としては (3) で作ったコールバックのアプリケーションに加え、ここでランタイムの IP アドレスをチェックするためのアプリも作り、それらをまとめてデプロイ(プッシュ)する、ということになります。

そして、ランタイムの IP アドレスを取得して LINE Bot の White List IP アドレスに設定します(ネットマスクは 24 を指定して動きました)。この作業はランタイムをステージングするたびに行う必要がある点に注意が必要です:
2016041303


このホワイトリストの反映には数分かかっているようですが、ホワイトリストが正しく反映されていれば自分のボットも動くようになっているはず、たぶん。


Bluemix 環境を活用した LINE Bot は他にも色々作れそうなので、いずれまたブログエントリとして書くつもりです。 上記のようにホワイトリストの所だけちと面倒ではあるのですが、ランタイムが自由に作れて SSL も問題なさそうだし、これにワトソンなどのコグニティブエンジンを組み合わせた Bot を作ると面白いかな、と考えています。



以前にこんな記事を書きました:
ラズパイと PC とをシリアル接続する

小型PCでもあるラズベリーパイは「一度セットアップしてしまえば」便利なのですが、このセットアップ時がちと面倒なのです。イメージの入ったマイクロ SD カードを用意し(ここまではいい)、HDMI ケーブルでディスプレイにつなぎ、キーボードとマウスをUSB接続して起動し、この外付けしたキーボードとマウスを使って初期セットアップを行います。要は初期セットアップ時に GUI を操作するための環境が必要なのです。 このセットアップが済んでしまえば(セットアップ時にネットワークや SSH の設定も済ませてしまえば)後は電源だけを繋いで起動したら SSH でログインして使う、なんてこともできるようになるのですが、最初のセットアップ時だけはどうにもなりません。

そこで上記のシリアル接続環境が便利になります。要は PC とラズパイをシリアルケーブルで接続し、PC 側の CUI 画面とキーボードを使って操作する、というものです。この方法であれば GUI は使えませんが、テキストコマンドを使った初期セットアップができるようになります。要は HDMI や USB マウス&キーボードを用意しなくてもシリアルケーブル1本だけでセットアップができるようになり、初期セットアップ後はネットワーク接続を使って普通に使えるようにできる、というものです。セットアップが完了した後も、ネットワーク環境が変わって SSH ログインなどができなくなるようなケースでもシリアルから操作できるようになり、とても便利で愛用していました。


さてラズベリーパイ3が発売されました。CPU などの基礎機能が上がったことに加え、Bluetooth と無線 LAN を標準搭載したモデルです。実際自分もそうですが、ラズベリーパイを持ち運んで使おうとするとモバイルルータやテザリングで無線 LAN 接続して使う、という形になると思います。要は持ち運んで使うには無線 LAN は必須です。その無線 LAN を標準搭載したのが大きな魅力でした:



早速購入し、これもいつもの感じ(上記リンク参照)でシリアル接続してセットアップ・・・しようと試みたのですが、何かこれまでと違います。起動時のメッセージにいきなり文字化けが・・・
2016041101


しばらく待つとログインプロンプトが表示されました。なので一応起動プロセスは正しく行われていたようです・・:
2016041102


ではセットアップを行おうと、初期ログインユーザー名の "pi" を入力すると・・・なぜか "pl" になってしまう。どういうこと?しかも改行がズレてるし・・・
2016041103


何これ? ログインできないの?? 結果としてはこれはラズパイ3にシリアル接続した時だけ発生する現象でした(普通に HDMI や USB 接続では問題なくセットアップできるし、同じバージョンの Raspbian Jessie をラズパイ1や2に入れると問題は発生しない)。どういうことだろう、と思って調べてみると、どうやらこの現象が起こっているのは僕だけではなかった様子でした。

結論としては、どうやらラズパイ3は現時点ではシリアル接続での初期セットアップはできない模様です:


どうも Bluetooth モジュールがシリアル関係と同じ所を使っているようで、その影響でシリアル接続の調整が上手く行かなくなっているようです。単にボーレートを 115200 から下げればよい、というわけでもなく、下げたら下げたなりに狂ってしまうので、どうにもできないようでした。

で、↑このリンク先にも書かれていますが、シリアル接続を使えるようにするための解決策も一応あります。/boot/config.txt ファイルに
core_freq=250

の1行を追加してあげるだけです。特にパフォーマンスが落ちることもなくシリアル接続が使えるようになりました:
2016041201
 ↑できたーっ!


ただ、この1行を追加するには一度セットアップを完了させる必要があり、そのためには HDMI や USB 機器を接続してセットアップする必要があります。

要するに現時点での結論としてはラズパイ3でもシリアル接続はできる、でも初期セットアップ時には HDMI や USB キーボード/マウスが必要になりそうです。ということは最初からシリアルケーブルだけで、というわけにはいかない。うーむ・・・ (--;


解決策の続報を待つ!

マンホール、およびマンホールマップの話です。


マンホールマップでは登録されたマンホール写真が地図上のどこにあるのか、を調べるだけでなく、どの都道府県のどの市区町村にどんなマンホールがあるか、といった逆引き(?)の検索も可能です:
http://manholemap.juge.me/mc.jsp


こういう話題の際に必ず出てくるのが「市町村合併」の話です。1995年に成立した合併特例法により、2005年から2006年にかけて市区町村の合併がピークを迎えました。俗にいう「平成の大合併」です。法律の変更によって、市になる条件が緩和された上に、財政支援策が拡充されたことで、町や村が合併して市になる(あるいは既存の市の一部として合併する)という動きが見られました。 こういった合併が行われると、合併前の(無くなってしまった)自治体のマンホールは新たに製造されることがなくなるので、「レア物」という形で、ある意味での昇格をする形になります。

↑旧埼玉県浦和市(現さいたま市)のマンホール


で、この市町村合併が行われる際には、同一の都道府県内の市町村が合併する、というのが一般的ですが、ごく稀に例外があります。つまり都道府県を越えた合併が行われることがあります。これが越境合併です。ある日を境に○○県民が住居を変えずに××県民になる、ということが発生します。

比較的最近では 2005 年2月13日、それまでは長野県木曽郡山口村と呼ばれていた地域がこの日から岐阜県中津川市に編入する形で合併が行われました。長野県がちょっとだけ小さくなり、その分岐阜県が大きくなったわけです。

なお、この直前の越境合併は1959年なので、実に46年ぶりの越境合併が行われたことになります。ちなみにこの越境合併によって、旧山口村の郵便番号と自動車のナンバープレート(の管轄支局)も中津川市のものに変わったようです。なぜか市外局番は変わらなかったらしいです。

で、そのような背景を持つ、超レアな旧山口村のマンホールがこちらです。村の木であったツバキと、同じく村の花であったムラサキツツジをあしらったデザインになっています:



財政的な理由もあって、今でも市町村合併や市政への移行は多く発生していますが、道にはその名残が残り続けるわけです。そんな歴史の道しるべという意味でもマンホールは面白いです。デザインの華やかさだけがファンの心を掴んでいるわけではない、ということを紹介したかったのでした。

Node.js を使っていると、たまにこんなエラーメッセージが出てサーバーが止まることがあります:
# node .
  :
  :

FATAL ERROR: JS Allocation failed - process out of memory
Aborted (core dumped)

"out of memory" と書かれているのでメモリ不足な状態になっているように見えます。が、これは物理メモリが足りないというメッセージではなく、Node.js の(1オブジェクトあたりの)ヒープサイズが足りなくなっている、というメッセージです。ちなみにデフォルトでは 512MB です。なので物理メモリを大量に搭載しているサーバーでも、サイズの巨大なファイルを扱ったり、大容量のアウトプットデータを取り扱おうとするとこのエラーは起こりうる、ということになります。

このヒープサイズを増やして利用するには node の実行時にパラメータを指定することで可能です。以下の例では 2048MB(=2GB) を指定しています:
# node --max-old-space-size=2048 .
  :
  :

あくまで「Node.js のヒープサイズ」の指定なので、他に使うサーバーアプリとの兼ね合いやスワップも含めた物理メモリサイズも含めて指定サイズを見積もる必要があります。


(参考)
http://stackoverflow.com/questions/29442965/fatal-error-js-allocation-failed-process-out-of-memory-aborted-core-dumped 


このページのトップヘ