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

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

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 上で動くようになります。


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

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

このページのトップヘ