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

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

2014/07

7月22~25日の4日間、大阪のインテックス大阪にて「下水道展2014」が開催され、「日本全国おもしろマンホール」を紹介するコーナーでマンホールマップを出展させていただく機会がありました。 関係者の皆様、貴重な機会をありがとうございました。


私が出展させていただいたのはGKP(下水道広報プラットフォーム)様の企画による「スイスイ下水道研究所」内の一企画として、夏休みを迎えた子供達に日本全国のいろいろなマンホールを紹介して知ってもらおう、というコーナーでした。PCを3台用意いただいて、マンホールマップだけではないのですが、オンライン上のマンホール紹介サイトやサービスを使って紹介し、実際にPC上での操作もしてもらおう、と考えていました。その参加報告を兼ねて、2日間に感じたことを書いてみます。

(1) 想定以上の人気だった

理由(作戦?)は後述しますが、私が参加した23、24日のマンホールマップはかなりの稼働率だったと思います。PC3台はほぼ常に子供達で埋まり、時間帯によっては順番待ちの列もできていました。事務局の皆様にお手伝いもいただきましたが、かなり大変でした。想定以上の人気で自分の休む時間が取れず、22日はお昼抜き、23日は表彰式もあったので、昼食と合わせて60分くらい場を離れましたが、2日間ほぼずっと出展場所で子供相手の説明をしていました。普段の仕事とは全く違う2日間だったこともありますが、正直かなり疲れました。でも嬉しい疲れです。

(中には一人でストリートビューまで操る子供達も!)
写真 3



(2) やはり子供にはゲーム(笑)

自画自賛ですが、今回の人気については「作戦勝ち」だと思っています(苦笑)。

実は昨年にもこのイベントに出展したのですが、その際は「小学校低学年向け」のつもりで「花」や「アニメキャラクター」などのカテゴリ毎に集めて作成したマンホールマップの子供向けスペシャルコンテンツを用意していました。が、これはあまり興味を持ってもらえず、ほとんど使うこともありませんでした。

いろいろなマンホール(http://manholemap.juge.me/childbuttons.htm)
2014072501


昨年のこの失敗を反省して、今年はマンホールマップのゲーム性を前面に出して紹介しました。小学校低学年以下っぽいお子様には「マンホめくり(神経衰弱)」を、それ以上っぽいお子様には「スライドパズル」を見せて、挑戦してもらいました。
2014073001


スライドパズルは我々も昔よく遊んだ「15ゲーム」ですが、これは大人でも結構難しいと思ってます。1、2、3まではほぼ誰でも揃えることができますが、4では急に難易度が上がり、自力で揃えることが出来る子はだいたい2割程度でした。この絶妙な難易度がハマってくれる理由だったと思います。
写真 2

(実は大きいお友達にも人気だった)
写真 1


更に、飽きちゃった子向けに「マンホールさめがめ」も提供しました。これは2日間で解けた子はたった1人、というものでかなり難しかったと思います。でもみんな自分なりに戦略を考えながら挑戦してくれました。こういうアルゴリズム的な発想にも挑戦できるのは未来のシステムエンジニア候補だと思ってます。

と、マンホールマップにはこのような遊び機能をいくつか用意しておいたのですが、それが今回役立つ形になりました。 これは来年も使えるな(笑)、さめがめは少し難易度を下げておくように改良しよう。


(3) マンホールマップがGKP広報大賞・電子媒体部門賞をいただきました!

最後は雑感というか、宣伝というか(笑)、ご報告です。

この度、マンホールマップが第二回 GKP 広報大賞・電子媒体部門賞をいただくことができました。マンホールマップがこのような形で一般の表彰をいただいたのはこれが初めてでした。 今回の下水道展3日目(24日)にその授賞式があり、ユーザーの皆様を代表する形で私が参加させていただきました。
写真



マンホールマップは2010年の開発開始から丸4年を迎え、今でも多くの「マンホール蓋愛好家」の皆さんからの投稿によって日々成長しています。「マンホール蓋愛好家」と書きましたが、その意味は4年前とは全く異なり、当時は「一部のマニア」の領域でしたが、今では「蓋女(ふたじょ)」なる言葉が生まれるほど老若男女に広がり、その発見報告の場の1つとしてマンホールマップを選んでいただけているのだと思っています。 その結果、多くのマンホール蓋ファンの層を広げることが出来、それを今回の受賞理由の1つに挙げていただきました。

今回の受賞は日頃利用いただいているユーザーの皆様のおかげであり、またサービスの向上に役立つアドバイスをいただいた諸先輩方のおかげでもあります。

また昨年に引き続き、今年も出展の機会をご用意いただいたGKP様、事務局として色々手配いただいた日経ピーアール様、下水道展会場で「オッチャン」呼ばわりしてくれた子供達、そしてたまに意識不明になりながらも自宅でずっと稼働を続けてくれているサーバー、本当に本当にありがとうございました。


下水道展、来年は東京で開催される予定です。大人でも半日飽きずに勉強できるエンターテイメントだと思っています。次回参加させていただけるかどうかは分からないのですが、個人的にはまた出展する形で参加したいと思っています。来年の夏、皆様と下水道展でお会いできることを楽しみにしております。
写真(1)


 

Cloud Foundry をベースとした PaaS 環境である IBM Bluemix が正式サービスとなり、価格も発表されました:
IBM Bluemix : Pricing Sheet

Bluemix の場合、アプリケーションサーバーと、サービスと呼ばれる(データベースとかSSOとかの)パーツとで別々の課金が行われて、利用料金という意味ではその合計になります。サービスの価格はサービスの種類によっても異なるので後述します。 で、アプリケーションサーバーの価格を見ると(2014/07/17 時点では)「メモリ1GBの仮想マシンを1時間使うと $0.07 」と設定されています。サーバーインスタンスに搭載しているメモリ量で時間課金、というタイプです。計算を楽にしたいので、仮に $1 = 100円とすると1時間7円ということになります。

ちなみに Bluemix のアプリケーションサーバーはデフォルトではメモリ512MB(0.5GB)で稼働します。つまり1時間3.5円。一ヶ月を30日とすると、1サーバーインスタンスの月額は 3.5 x 24 x 30 = 2520円です。
2014041701


このスペックをモデルケースとして、これが安いのか高いのか?、ということを考えてみます。

比較対象を「本家」の Cloud Foundry としましょう。こちらの料金体型もメモリ量での時間課金タイプですが、料金は1GBを1時間使うと $0.03 、ここだけ見ると Bluemix の半額以下です。同じ条件で512MBメモリを搭載した1サーバーの月額にすると 1.5 x 24 x 30 = 1080 円ということになります。
2014041702


とりあえず、同一条件の1アプリケーションサーバーを使った場合の料金は CloudFoundry の方が半分以下になる、ということはわかりました。ただ現実はそこまで単純ではありません。

ここまでの話はあくまで「アプリケーションサーバーの料金」です。多くの場合、アプリケーションはアプリケーションサーバー上にデプロイして動作させますが、同時にデータベースサーバーなども必要になります。

CloudFoundry の場合、データベース(例えば MySQL)サーバーを1つ追加しようとすると、アプリケーションサーバーと同一のマシンに MySQL サーバーも入れてしまう、という選択肢はありますが、さすがにメモリ 512MB で両方動かすのは無理ではないにしても厳しいと思われます。というわけでメモリを増やすとか別途データベース用サーバーを1台追加することになり、同じスペックのマシンだったとしても2台分の料金(1080 x 2 = 2160 円)が必要になります。メモリを倍に増やした場合も同額です。

一方 Bluemix の場合、データベースサーバーは「サービス」として提供されており、サービスは制約の中であれば無料で使うことも可能です。例えば MySQL は無料、PostgreSQL も無料、SQL DB(DB2) はデータ量2GB以内の1インスタンスであれば無料、といった具合です。制約といってもデータ量2GBまで使えるのであれば、結構多くのケースで無料利用ができると思っています。しかもこれらのサービスはいわば DBaaS として提供されており、Buildpackの用意やアプリケーションの導入などは不要ですぐに使いはじめることができます。つまりデータベースは(制約の中であれば)無料で追加できるオプションとなっているので、利用料金は変わらずアプリケーションサーバー代のみの 2520 円ということになります。
2014041703


この時点でもかなり差がなくなってきました。更に、例えば MongoDB などの no-SQL データベースサーバーを追加したり、データ高速化の memcached サーバーを追加したりすると、CloudFoundry ではサーバー構築の手間が増えるだけでなく、これらのサーバーを追加することになるので、もう1台、更にもう1台・・・とコストもどんどん高くなっていきます。

一方、Bluemix ではこれらのサービスも制約の中ですが無料で使える範囲が設定されています。つまりサービス用のサーバーは増えてもコストは変わらずアプリケーションサーバー代の 2520 円のままで利用することもできるのです。ここまでの環境トータルで考えると Bluemix の方が安くなっていることも珍しくないと思っています。
2014041704


だからといって Bluemix の方が得とも言い切れない所が難しいです。CloudFoundry では Buildpack を用意することで「サーバー1台で済ませる」という選択肢もあり、同一スペック1台での比較となると上記のように CloudFoundry の方が安いのです。 一方 Bluemix のサービスは無料の枠が用意されてはいるものもありますが、その枠を超えて利用する場合には有料となります。無料枠は利用するサービスの種類によっても異なるので、その辺りは設計後に正しく見積もる必要がでてきます。
2014041705


最終的にはシステム構築に必要なサーバー構成を冗長化なども含めて検討した上で選択する、ということになるのだと思います。そこまで含めても Cloud Foundry と比較した Bluemix の価格が一概に高いとは言えない(安いとも言えない)と感じています。 

ただシステム構成が複雑になればなるほどアプリサーバーの課金がメインとなる Bluemix に割安感が出てくるんだろうな、という印象を持ちました。 また比較対象を PaaS ではなく、AWS などの IaaS と比べてみても、アプリケーションサーバーとデータベースサーバーとあれとこれと・・・まで付けて月額 2530 円~ であれば充分に価格競争力もあるんじゃないかな、と思えます。


 

IBM のパブリック PaaS サービスである Bluemix を利用する上では、まず IBM ID ※と呼ばれるアカウントを取得する必要があります。そして IBM ID とパスワードでサービスにログインすることで、各種インスタンスを生成したり、設定変更したり、・・・というオペレーションが可能になります。

※正確には「Bluemix 用の IBM ID」と言うべきだと思ってます。


この IBM ID を自分のアプリケーションの認証として使うことができれば、わざわざユーザーディレクトリを別途用意する必要がなく、アプリケーションを「IBM ID でログインして使う」ということが可能になります。またこれができれば、例えば2つの異なるアプリケーションで共通のユーザーディレクトリを使いたい時にも、2つのユーザーディレクトリを用意する必要がなく、IBM IDという共通のアカウントを使えば(そしてアプリケーションの認証を IBM ID を使って実装すれば)できちゃうことになります。PaaS の環境にこのようなユーザーディレクトリまで用意されているのは便利ですね。 というわけで、今回は Bluemix 環境で SSO サービスを使ったアプリケーションを開発する方法の紹介をします。

まず(普通に)IBM IDで IBM Bluemix 環境にログインし、アプリケーションを1つ作成します(このアプリケーションで SSO サービスを使うことになります)。ここでは bluemixsso.mybluemix.net というホスト名でアプリケーションサーバーを作成しています。
2014071401a


そしてこのアプリケーションに SSO サービスをバインドします。ここまではデータベースサービスをバインドするような手順と同じです。
2014071401b

Single Sign On サービスを選択して、作成したアプリケーションにバインドします:
2014071401c


次に SSO サービスを REST API で使うためのエンドポイントを確認します。アプリケーションのページを開いて、その内の Single Sign On サービスパーツの "Show Credentials" をクリックします。
2014071403


このようなテキストフィールドが開いて、JSON テキストが表示されます:
2014071404


ここで表示される JSON テキストは以下のようになっています。この赤字部分(authorize_url, token_url, profile_resource の3つの値)を後で使うのでメモしておきます:
{
  "single.sign.on" : [ {
    "name" : "Single Sign On -5q",
    "label" : "single.sign.on",
    "plan" : "beta",
    "credentials" : {
      "profile_resource" : "https://idaas.ng.bluemix.net/idaas/resources/profile.jsp",
      "tokeninfo_resource" : "https://idaas.ng.bluemix.net/idaas/resources/tokeninfo.jsp",
      "openidProviderURL" : "https://idaas.ng.bluemix.net/idaas/openid",
      "token_url" : "https://idaas.ng.bluemix.net/sps/oauth20sp/oauth20/token",
      "authorize_url" : "https://idaas.ng.bluemix.net/sps/oauth20sp/oauth20/authorize"
    }
  } ]
}

次に SSO の Credential 情報を設定します。同じ画面の左ペインから "SERVICES" - "Single Sign On" をクリックします。画面右に現在の(初期状態の)SSO の設定内容が編集可能な状態で表示されます:
2014071405


以下のように編集して、最後に "Save" ボタンをクリックします:
 Identity Providers: "IBM" にチェック
 Authentication Protocol: "OAuth 2.0" を選択
 Display Name: ユーザーの画面に表示されるアプリケーション名称を指定
 Enabled: チェック
 Redirect URI: アプリケーションの OAuth リダイレクトURI (後述)を指定
2014071406


認証方法には OpenID か OAuth2.0 かを選ぶことができますが、今回は OAuth2.0 にしています。またその Provider に "IBM" を選ぶことで IBM ID を使った OAuth を指定していることになります。

入力内容に不備がなければ保存され、画面には "Client Identifier" と "Client Secret" という2つの値が表示されるはずです。この2つの値も後で(OAuth の認証時に)使うのでメモしておきます。
2014071407


ここまでの作業でサービス側の準備はできました。では次にこの SSO を実際に使って OAuth でログインするアプリケーションの実装方法を紹介します。なお、以下の例では Java および JavaScript を使って実装していますが、API そのものは REST を使うので他の言語でも同様の処理を記述すれば実装できると思います。

なお、今回はログイン後のページの URL(上記のRedirect URI) を http://localhost:8080/BluemixSSO/index.jsp として SSO で設定しています。あくまで localhost を使った開発環境での設定であり、本番稼働時にはここをhttp://bluemixsso.mybluemix.net/index.jsp という値に書き換える必要があります。ただ今回の紹介ではあくまで開発環境上だけでの稼働確認を行うためこの値にしています。適宜変更して利用してください。


まずはログイン前のページを用意します。ログイン後のページと同じページにしてもいいのですが、いろいろ面倒なのでログイン前は login.html、ログイン後は index.jsp というページが表示されるものとします。

で、ログイン前のページはこのような感じにします。このサンプルでは単純にログインボタンがあるだけです:
<html>
<head>
<title>ログイン前</title>
<script type="text/javascript">
var client_identifier = "(Client Identifierの値)";
//var client_secret = "(Client Secretの値)";
var server_url = "http://localhost:8080/BluemixSSO/index.jsp";
function login(){
  // authorize_url へリクエスト
  location.href = "https://idaas.ng.bluemix.net/sps/oauth20sp/oauth20/authorize?client_id=" + client_identifier + "&redirect_uri=" + server_url + "&scope=profile&response_type=code";
}
</script>
</head>
<body>
<input type="button" value="ログイン" onClick="login();"/>
</body>
</html>

このページ内のボタンをクリックすると JavaScript が実行されて、authorize_url に対してパラメータを付けてリクエストが実行されます。成功すると Redirect URL で指定した URL(http://localhost:8080/BluemixSSO/index.jsp) にアクセスコードが付与された形でリダイレクトされます。

そしてリダイレクト先(index.jsp)では以下の様なコードを用意しておきます:
<%@ page contentType="text/html;charset=UTF-8" language="java" pageEncoding="UTF-8" %>
<@ page import="javax.servlet.http.*" %>
<@ page import="java.util.*" %>
<@ page import="java.io.*" %>
<@ page import="java.net.*" %>
<@ page import="java.util.*" %>
<@ page import="org.apache.commons.httpclient.*" %>
<@ page import="org.apache.commons.httpclient.methods.*" %>

<% request.setCharacterEncoding("utf-8"); %>

<html>
<head>
<title>ログイン後</title>
<%
String client_identifier = "aDUvXmXYEFSGtnNgX7v1";
String client_secret = "6w3YoxVpdoZ0mp2Q41IA";
String server_url = "http://localhost:8080/BluemixSSO/index.jsp";

String code = request.getParameter( "code" );
String access_token = null;
String email = "";
if( code != null && code.length() > 0 ){
  //. アクセストークンを取得
  String req_url = "https://idaas.ng.bluemix.net/sps/oauth20sp/oauth20/token?client_id=" + client_identifier + "&client_secret=" + client_secret;
  String param = "grant_type=authorization_code"
      + "&redirect_uri=" + URLEncoder.encode( server_url )
      + "&code=" + code;
  try{
    HttpClient client = new HttpClient();
    PostMethod post = new PostMethod( req_url );
		
    post.setRequestHeader( "Content-Type", "application/x-www-form-urlencoded" );
    post.setRequestHeader( "Content-Length", "" + param.length() );
    post.setRequestBody( param );
		
    int sc = client.executeMethod( post );
    if( sc == 200 ){
      String body = post.getResponseBodyAsString();
      int n1 = body.indexOf( "\"access_token\":\"" );
      if( n1 > 0 ){
        int n2 = body.indexOf( "\"", n1 + 16 );
        if( n2 > n1 ){
          access_token = body.substring( n1 + 16, n2 );
					
          //. アクセストークンを使ってプロファイルデータを取得
          String req_url1 = "https://idaas.ng.bluemix.net/idaas/resources/profile.jsp";
          String param1 = "access_token=" + access_token;

          HttpClient client1 = new HttpClient();
          PostMethod post1 = new PostMethod( req_url1 );
						
          post1.setRequestHeader( "Content-Type", "application/x-www-form-urlencoded" );
          post1.setRequestHeader( "Content-Length", "" + param1.length() );
          post1.setRequestBody( param1 );
          int sc1 = client.executeMethod( post1 );
          if( sc1 == 200 ){
            String body1 = post1.getResponseBodyAsString();
            n1 = body1.indexOf( "\"email\":[\"" );
            if( n1 > 0 ){
              n2 = body1.indexOf( "\"", n1 + 10 );
              if( n2 > n1 ){
                email = body1.substring( n1 + 10, n2 );
              }
            }
          }
        }
      }
    }
  }catch( Exception e ){
    e.printStackTrace();
  }
}else{
	
}
%>
</head>
<body> 
<h1>ようこそ、<%= email %> さん!</h1>
</body>
</html>

index.jsp 内では2回 HTTP リクエストを発行しています。
まず1回目のリクエストでは code パラメータの値を受け取り、その値を指定してアクセストークンを取得しています。
次に2回目のリクエストでは取得したアクセストークンを利用して、ログインユーザーのプロファイルデータにアクセスし、その email アドレスを取り出しています。 ここまでの処理が成功していれば、HTML 部分の <h1> タグ内にそのメールアドレスの値が出力される、というものです。

この辺りの情報について、詳しくは SSO のドキュメントも参考にしてください:
Getting started with Single Sign On (BETA)


実際にこのようなアプリを作って動かしてみると、以下のようになります:

まずログイン前の login.html ページにアクセスします。これは静的なコンテンツなので誰がアクセスしても同じ「ログイン」ボタンだけが表示される画面になるはずです:
2014071408

この「ログイン」ボタンをクリックすると JavaScript が実行されてリダイレクト処理が行われます。その処理の中で IBM ID によるログインが行われます:
2014071409

もし未ログイン状態であれば IBM ID とパスワードを指定していログインしてください:
2014071410

このアプリを最初に使う場合のみ、アクセスコードの入力が求められます。以下の様な画面が表示されると同時に、IBM ID のメールアドレスにメールが送信されます。この例では "4232-" と書かれたテキストフィールドが表示されていますが、送信メールには 4232- で始まる数字4桁-数字6桁 の文字列が書かれているはずです。
2014071411

受け取ったメールの内容の数字6桁部分を書き足して SUBMIT ボタンをクリックします:
2014071412

内容が正しければ先へ進みます。このような処理を毎回繰り返したくない場合はこのブラウザを登録することで回避出来ます。その場合はこの画面で "Register this Device" にチェックを入れ、その下のテキストフィールドにデバイス名を入力(半角スペースは使えません)して「送信」ボタンをクリックします:
2014071413

そして最後に OAuth 認証時にお馴染みに「このアプリに権限を与えるか?」の確認画面が表示されます。チェックボックスにチェックを入れて "Approve" ボタンをクリックします:
2014071414

で、無事に権限の譲渡が行われ、アクセストークンを使ってプロフィールの取得が行われ、(この例では)メールアドレスを取得して画面に表示する、という処理を行うことができました。
2014071415


今回紹介した例はかなりシンプルで、単にログイン前とログイン後のページを分けて、ログイン後のページでログイン情報を表示する、というだけの処理を実装しています。 実際にはログイン前後のページを同じにしたいこともあるでしょうし、またログイン後にログイン情報を残したまま別のページに移動したい、ということもあるでしょう。それらの場合はセッション情報などを使ってステータス管理をしながら処理を記述していくことになると思います。その辺りは IBM Bluemix の SSO サービスに特化した話ではないので、ここでは割愛します。

とはいえ、この例でも分かるようにサードパーティでも IBM Bluemix 上で IBM ID によるログイン管理を行うアプリの開発が出来ることがわかりました。Cloud Foundry をはじめとする各種 PaaS 環境でも共有ユーザーディレクトリが提供されているのは珍しいので便利に使えそうです。

しかも、IBM Bluemix 環境での SSO サービスの利用は(少なくとも 2014/07/14 の時点では)無料だったりします:
2014071401
※IBM Bluemix の各種サービス価格の最新情報はこちらを参照してください:
 IBM Bluemix: Pricing Sheet


どこかのタイミングで有料化される可能性がないとは言えませんが、PaaS 環境で共有で使えるユーザーディレクトリのサービス自体が珍しく、嬉しいです。







CentOS にオープンソースな Microsoft Windows レイヤーアプリである Wine を導入する手順を紹介します。なお CentOS 6.5 64bit 環境を前提とします。

また Wine は各種 UNIX 環境向けに開発されており、Ubuntu ベースの Linux に対してはバイナリも提供されています。ただ今回は自分がメインで使っている CentOS に、ソースからビルドして導入する手順を紹介します。


まず、CentOS 側に必要なモジュールをあらかじめ導入しておきます(青字はインストールしている内容の説明であって、コマンドとしては入力しません):
# yum groupinstall "X Window System" "GNOME Desktop Environment" GNOMEウィンドウ環境
# yum groupinstall "Development Tools" コンパイル環境一式
# yum install libX11-devel freetype-devel 必須ライブラリ

次に Wine の最新版ソースコードを入手します。SourceForge を参照して最も新しいバージョンを確認し、そのアーカイブモジュールを wget で入手します。なお 2014.7.10 時点では安定バージョンの最新版は 1.6.2, 開発版の最新版は 1.7.21 でした。お好きな方をどうぞ(以下は1.7.21 での例):
# cd /usr/local/src
# wget http://citylan.dl.sourceforge.net/project/wine/Source/wine-1.7.21.tar.bz2
# tar xvf wine-1.7.21.tar.bz2
# rm wine-1.7.21.tar.bz2

ビルドして、インストールします:
# cd wine-1.7.21
# ./configure --enable-win64
# make
# make install

なお、上記は 64bit 環境用のコマンドです。参考程度ですが、32bit 環境の場合のコマンドは以下になります:
# cd wine-1.7.21
# ./tools/wineinstall

また 64bit 環境用では Wine の実行コマンド名が wine64 となります。このままだといくつか不具合が発生するので 32bit 環境と同じ wine というファイル名でシンボリックリンクを作成しておきます:
# cd /usr/local/bin
# ln -s wine64 wine

また、日本語環境で Wine を利用する場合、このまま次の初期設定コマンドを実行するとフォントの関係で文字化けしてしまいます。この文字化けを回避するためにフォントをコピーして用意しておきます:
# cp -R /usr/share/fonts/ipa-* ~/.wine/drive_c/windows/Fonts/


これで Wine のインストールができました。最後に(SSHなどのコマンドライン端末ではなく) GNOME 環境からWine の設定コマンドを実行して、初期設定を実行します。特に何かを変更するわけではなく、設定内容の確認を行うことと、上記のコマンドによって(文字化けなく)正しく日本語が表示されていることを確認してください:
# winecfg
2014071001


これで Wine の導入は完了です。例えば「メモ帳」を起動する場合はコマンドラインからこんな命令を実行します(最後の & を付けずに実行した場合は、起動したアプリが終了するまでコマンドラインはビジー状態になります):
# wine notepad &
2014071002


ちなみに、上記のコマンドは以下のコマンドと同じ意味です:
# wine notepad.exe &
# wine c:\\windows\\notepad.exe &

同様に、「コントロールボックス」や「レジストリエディタ」を起動するにはそれぞれこんな感じで:
# wine control & コントロールボックス
# wine regedit & レジストリエディタ
2014071003


理論上は Windows 用のアプリやインストーラーが入手できれば、それを指定して、
# wine XXX.exe &
のように実行することで CentOS 上で動くようになります。


でも上で「理論上は」と書いたように、実際は動かないアプリが多いです。動いても想定外の見栄えになることもあります。

うーん、ここで昔の色んなアプリが動いたら面白かったんだけどなあ・・・・
 

最近のマイブームの1つになっている、全文検索エンジン ElasticSearch に MySQL のデータを取り込んで、MySQL データベースの全文検索エンジンとして ElasticSearch を使う手順の紹介です。
2014070401


まず検索エンジンである ElasticSearch を導入します。日本語形態素解析エンジンである Kuromoji まで含めてのインストール手順を別エントリで紹介しているので、こちらを参照ください:
ElasticSearch に Kuromoji プラグインを導入する


また取り込み先である MySQL サーバーについても環境は構築済みであると仮定します。こちらの構築手順についても、こちらのエントリを参照ください:
CentOS に MySQL をインストール/セットアップする

なお、自分個人的には MySQL ではなく MariaDB を使って同じことをできているので、以下の内容に関しては MariaDB でも同様に可能だと思っています。


さて、ElasticSearch に MySQL データを取り込むための準備として ElasticSearch 自体のインストール後に以下のステップを行う必要があります:
1. (MySQL クライアントと)JDBC ドライバの導入
2. JDBC River プラグインのインストール


最終的には ElasticSearch の River プラグインと呼ばれる拡張機能を使って MySQL からのデータ取り込みを行います。このプラグインの動作に必要な MySQL JDBC ドライバを先にインストールしておく、というステップになります。


まず、これは必須ではありませんが、あると確認に便利なので MySQL のクライアント環境を ElasticSearch サーバー内に構築しておきます。MySQL サーバーに接続する機能があればいいので、MySQL サーバー機能は不要で、クライアント機能だけが必要、ということになります。もし MySQL サーバーと ElasticSearch サーバーが同じサーバーだとすると、既に MySQL クライアント環境は導入済みだと思うので、インストールは不要です。 MySQL クライアントが導入されていない場合は以下のコマンドで MySQL クライアントをインストールします:
# yum install mysql
# vi /etc/my.cnf
  :
(以下の2行を追加)
[mysql]
default-character-set=utf8

MySQL クライアントが導入できた所で、取り込み元の MySQL サーバーへ接続してみます。仮に今回取り込むデータの内容が以下であると仮定します:
 MySQL サーバー: mysql.mylocal.com
 ユーザー名: username
 パスワード: password
 データベース名: mydb
 取り込む内容: samples テーブル


実際に MySQL クライアントで目的のデータベースにアクセスしてみます。ここまでが出来るようであればファイアウォールなども含めて接続準備ができているといえます:
# mysql -h mysql.mylocal.com -u username -ppassword mydb
> select * from samples;
:
:
(samples テーブルの内容)
:
: > quit


次に MySQL サーバーへ Java 環境から接続するための JDBC ドライバー(MySQL Connector/J)を導入します。ドライバー自体はこちらのサイトからダウンロードできます:
MySQL :: Download Connector/J 


ダウンロードしたファイルを展開して JAR ファイルを取り出し、/usr/share/java にコピーします:
# unzip mysql-connector-java-5.1.30.zip
# cd mysql-connector-java-5.1.30
# cp mysql-connector-java-5.1.30-bin.jar /usr/share/java

環境変数 CLASSPATH に、この JAR ファイルを追加します:
# vi /etc/bashrc
  :
(以下の1行を追加)
export CLASSPATH=$CLASSPATH:/usr/share/java/mysql-connector-java-5.1.30-bin.jar
 

# source /etc/bashrc

JDBC ドライバの準備が出来た所で River プラグインをインストールします。リポジトリを確認したところ、2014/07/01 時点での最新バージョンは 1.2.1.1 だったので、このバージョンを指定して導入します:
# /usr/share/elasticsearch/bin/plugin --install jdbc --url http://xbib.org/repository/org/xbib/elasticsearch/plugin/elasticsearch-river-jdbc/1.2.1.1/elasticsearch-river-jdbc-1.2.1.1-plugin.zip
# cp /usr/share/java/mysql-connector-java-5.1.30-bin.jar /usr/share/elasticsearch/plugins/jdbc/

これで必要なソフトウェアは揃いました。では実際に MySQL からデータを取り込んでみましょう

まず ElasticSearch 側に kuromoji を使った検索インデックス(kuromoji_sample)を作成します。インデックスの作成については別エントリでも紹介しましたが、まだこの内容を実行していない場合は以下のコマンドを実行します(コマンドは黒字部分で、青字はレスポンスを表しています):
# curl -XPUT http://localhost:9200/kuromoji_sample -d '{ "index": { "analysis": { "tokenizer": { "kuromoji_user_dict" : { "type":"kuromoji_tokenizer" } }, "analyzer": { "analyzer": { "type":"custom", "tokenizer": "kuromoji_user_dict" } } } } }'
{"acknowledged":true}

次に River を使って、作成した kuromoji_sample インデックスに MySQL データベースサーバーからデータを取り込みます:
# curl -XPUT http://localhost:9200/_river/my_jdbc_river/_meta -d '{ "type": "jdbc", "jdbc": { "url": "jdbc:mysql://mysql.mylocal.com:3306/mydb", "user": "username", "password": "password", "sql": "select * from samples", "index": "kuromoji_sample", "type": "samples" } }'
{"_index": "_river", "_type": "my_jdbc_river", "_id": "_meta", "_version": 1, "created": true}

上記入力パラメータ(JSON)の中で選択(select)の SQL を発行しています。この SQL の実行結果が ElasticSearch に取り込まれることになります。

取り込みができたら、最後に検索してみます。この例では取り込んだデータの name フィールドに「ほげほげ」が含まれているデータを検索しています:
# curl -XPOST http://localhost:9200/kuromoji_sample/_search?pretty -d '{ "query": { "query_string": { "query": "name:ほげほげ" } } }'
{
  "took" : 2,
  "timed_out" : false,
  "_shards" : {
    "total" : 5,
    "successful" : 5,
    "failed" : 0
  },
  "hits" : {
    "total" : 29,
    "max_score" : 3.687642,
    "hits" : [ {
      "_index" : "kuromoji_sample",
      "_id" : "XXXXXXXXXXXXXX",
      "_score" : 3.687642,
      "_source":{"id":"1234","name":"ほげほげ"}
    }, {
      "_index" : "kuromoji_sample",
        :
    } ]
  }
}

こんな感じで実現できました。

MySQL の like 節を使った単純検索をしていた頃と比べると、以前は localhost 内の MySQL に対して検索していたのでネットワークによる遅延はほとんどなかったはずで、今回作ってみた環境はリモートの ElasticSearch 環境にアクセスしているので、ネットワークの遅延影響がでるはずです。

にも関わらず、検索パフォーマンスは 10 倍程度になりました。これはでかい!

ElasticSearch の検索パターンやその API 実行方法についてはいずれまたプログに書く予定です。


 

このページのトップヘ