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

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

タグ:foundry

とある要件を実現するツールを作りました。同じことに悩む人がいた場合を想定して、ツールをソースごと公開することにしました。

某ウェブプラットフォームのサービス終了が決まり(わかる人はこれだけで何の話か推測されそうだけど・・)、現在稼働中のウェブアプリケーションを引っ越しすることになりました。引っ越しそのものはさほど難しくないのですが、問題は「サービスの URL が変わってしまう」ことでした。

図に示すとこのような感じです。これまで運用していたサービスの運用環境を A から B に引っ越しした結果、これまでの URL とは異なる URL で引き続き運用することになりました:
2022070601


これまで使っていたユーザーに対しても「サービスの URL が変わった」ことを知らせてあげたいのですが、具体的にどうするべきでしょうか?A で運用中の画面に「URL が変更になった」と注意書きを含めて、改めて B にアクセスしてもらうこともできます。が、もう少し気の利くやり方として「A にアクセスしたら自動的に B に転送させて、B で運用中の画面で URL が変更になった旨を記載しておく(そのままブックマークできるようにする)」という方法もあります。


この後者の方法を実現するためには A にアクセスした利用者に対して "301" という HTTP ステータスコードと、続けて変更先の URL を Location ヘッダに含めて返すことで実現できます。この HTTP ステータスコード 301 は "Moved Permanentaly" を意味していて「URL が(一時的ではなく)恒久的に変更になった」ことを示しています。続けて Location ヘッダに新しい URL を含めておくことでウェブブラウザ側で新しい URL に自動的に遷移してくれます。つまり「A にアクセスしたら自動的に B に移動させる」ことが実現できます(そして B 側で「URL が変わったので現在のページをブックマークして」といったメッセージを記しておく、といった対応になります)。ウェブページの引っ越しを行う場合の一般的な手段でもあります:
2022070602



問題となるのは、この「301 という HTTP ステータスコードを返す」機能です。A 側のサービスでそのような転送機能や転送の設定が提供されていればそれを使えばいいのですが、必ずしも提供されていないことも考えられます。そのようなケースに対応するため、今回「アプリケーションの機能として 301 HTTP ステータスコードと、引っ越し先 URL を返すアプリケーション」を作ったので、公開することにしました。これまで A で動いてたアプリケーションの代わりにこのアプリケーション(下図の app)をデプロイすることで、A へのアクセスがあった場合に、新しい B への URL に無条件で転送させることができるようになります:
2022070603


このような挙動を実現するための Node.js アプリケーションのソースコードを以下で公開しました:
https://github.com/dotnsf/301movedpermanently

動作確認する場合は、Node.js が導入済みの環境にソースコードをダウンロードするか git clone して、環境変数 URL に転送先の URL を指定して実行します。なおアプリケーションはデフォルトで 8080 番ポートで待ち受けますが、このポート番号を変えたい場合は環境変数 PORT に指定して実行してください:
$ git clone https://github.com/dotnsf/301movedpermanently

$ cd 301movedpermanently

$ npm install

$ URL=https://www.yahoo.co.jp/ node app (全てのリクエストを Yahoo! トップページに転送する場合)

起動後にアプリケーションにアクセスすると、全てのリクエストが環境変数 URL で指定した値(上の例だと https://www.yahoo.co.jp/)に転送されます。なおコード内ではメソッド/パス/パラメータに関係なく、全てのリクエストを GET リクエストに変換して転送しています。GET/HEAD メソッド以外のリクエストについては HTTP ステータス 308 を返して対応するケースもありますが、今回のような「サービスごと引っ越し」のケースでは新 URL のトップページに転送することが多いと思うので、今回は説明を控えます(下の青字部分が実行されます):
app.all( '*', function( req, res ){
  if( post_redirect ){
    var method = req.method;
    if( method == 'GET' || method == 'HEAD' ){
      //. https://developer.mozilla.org/ja/docs/Web/HTTP/Status/301
      res.status( 301 );
    }else{
      //. https://developer.mozilla.org/ja/docs/Web/HTTP/Status/308
      res.status( 308 );
    }
  }else{
    res.status( 301 );
  }

  res.set( 'Location', url );
  res.end();
});

古いサイト A が動いている間だけ有効な強制転送方法ですが、他に方法がない場合はこんな感じで古いサイトを訪ねた人を強制的に B へ転送する方法が有効だと考えています。

なお、docker イメージとしても公開しているので、移行元で docker が使える環境であればこちらを使っていただくのが手っ取り早いと思っています:
dotnsf/301movedpermanently


IBM Bluemix はオープンソース PaaS である Cloud Foundry をベースに作られています。ということは Cloud  Foundry の API を Bluemix に対して使う、ということもできるわけです。

実際に試してみた様子を以下に記載します。なお、実行にあたっては Bluemix のアカウントと、実行環境として curl を利用します。以下の例では CentOS 6 上の curl を使っていますが、他のプラットフォームでもほぼ同様に使えるはずです。また以下の例では北米データセンターを対象に API を実行しますが、他のデータセンターを使う場合は、"ng" の部分を "eu-gb"(英国)か "au-syd"(オーストラリア)といった具合に書き換えて実行してください:


(1) アクセストークンの取得

具体的な API の利用の前に OAuth のアクセストークンを取得しておく必要があります(このアクセストークンを使って個別の API を実行します)。

以下の curl コマンドを実行します(青字部分がレスポンスです):
# curl -XPOST -H"Application/json" -u "cf:" --data "username=username@xxxx.com&password=P@ssw0rd&grant_type=password&scope=" https://login.ng.bluemix.net/UAALoginServerWAR/oauth/token

{
 "access_token":"(accessToken)", 
"token_type":"bearer",
"refresh_token":"*******", "expires_in":43199, "scope":"cloud_controller.read password.write cloud_controller.write openid", "jti":"********" }

"--data" に続く部分にログイン情報を含めています。username は Bluemix のログインIDです(上の例では username@xxxx.com を指定しています)。また password にパスワードを指定しています(上の例では P@ssw0rd を指定します)。それぞれ自分の情報に書き換えて実行してください。すると青字のような JSON テキストが返ってきます。この中の access_token の値(青太字の部分)がアクセストークンです。実際にはかなり長い文字列になっているはずですが、これを使って (2) 以降で API を実行します。


(2) API の実行

アクセストークンが取得できたら、その情報を使って Cloud Foundry API を実行します。Cloud Foundry API そのもののリファレンスはこちらを参照ください:
https://apidocs.cloudfoundry.org/231/

例えば「現在利用中のアプリケーションランタイムの一覧を取得」するには、この API を使います:
https://apidocs.cloudfoundry.org/231/apps/list_all_apps.html

具体的には以下の様な curl コマンドを実行します。ヘッダ情報(Authorization ヘッダ)に上記で取得したアクセストークンを指定しています:
# curl -H"Authorization: bearer (accessToken)" https://api.ng.bluemix.net/v2/apps

{
 "total_results": 15,
 "total_pages": 1,
   :
   :
 "resources":[
  {
   "metadata": {
    "guid": "(appGuid1)",
    "url": "/v2/apps/(appGuid1)",
    "created_at": "2015-03-09T02:49:49Z",
    "updated_at": "2016-02-20T06:23:39Z"
   },
    :
    :
  },
  {
   "meatadata": {
    "guid": "(appGuid2)",
     :
     :
  },
     :
     :
  }
] }

"resources" 内に配列形式でランタイムアプリケーションの情報が含まれているはずです。各ランタイムアプリケーションにはアプリケーション GUID と呼ばれる ID が割り振られており、その値は各ランタイムアプリケーション内の metadata.guid として確認できます。

では、今度はこのアプリケーション GUID を使って、このランタイムアプリケーションの環境変数情報を API で確認してみましょう。具体的にはこちらの API を利用します:
https://apidocs.cloudfoundry.org/231/apps/get_the_env_for_an_app.html
# curl -H"Authorization: bearer (accessToken)" https://api.ng.bluemix.net/v2/apps/(appGuid1)/env

{
 "staging_env_json": {
  "BLUEMIX_REGION": "ibm:yp:us-south"
 },
 "running_env_json": {
  "BLUEMIX_REGION": "ibm:yp:us-south"
 },
 "environment_json": {
 },
 "system_env_json": {
  "VCAP_SERVICES": {
   "relationship_extraction": [
    {
     "name": "Relationship Extraction-dc",
     "label": "relationship_extraction",
     "tags": [
      "ibm_beta",
      "watson",
      "ibm_created",
      "ibm_dedicated_public"
     ],
     "plan": "relationship_extraction_free_plan",
     "credentials": {
      "url": "https://gateway.watsonplatform.net/relationship-extraction-beta/api",
      "sids": [
       {
        "sid": "ie-es-news",
        "description": "information extraction from Spanish news"
       },
       {
        "sid": "ie-en-news",
        "description": "information extraction from English news"
       }
      ],
      "username": "(username)",
      "password": "(password)"
     }
    }
   ],
    :
    :
    :
   ]
  }
 },
 "application_env_json": {
   :
   :
 }
}

(appGuid1) に上記で確認したアプリケーション GUID を指定して実行します。すると上記のようなフォーマットで、このランタイムアプリケーションの環境変数情報が(VCAP_SERVICES もそれ以外も)まとめて取得できます。特に VCAP_SERVICES の credentials(上記の青太字部分)にはバインドしているサービスの接続情報が含まれており、その値を外部から API で確認できたことになります。


curl(HTTP) だけで Cloud Foundry API を Bluemix に対して使うことができる、ということが確認できました。これで複数のランタイムアプリケーションのステータスを確認する、といった管理用のアプリケーションも作れそうです。もちろん管理用途だけでなく、Bluemix のダッシュボードで実現しているようなアプリケーションを独自に作る、といったことも含めてできそうです。

 

Stackato を使ってプライベート環境に構築した Cloud Foundry 環境に、App Store というアプリケーション一覧からアプリケーションをデプロイしてみます。Stackato 環境構築の手順はこちらを参照してください:
Stackato を導入してプライベートな Cloud Foundry 環境を(KVM に)構築する
 ↑これの続きです


Stackato 管理コンソールにログインして、最初のページを下にスクロールすると "Deploy from the App Store" と書かれたリンクがあります。ここをクリックします:
2014051501


"App Store" という、Stackato 対応アプリケーション(正確にはサーバー&アプリケーション)の一覧が表示されます。CMS の Joomla やアプリケーション開発フレームワークの cakePHP など、メジャーどころが結構対応しています。これらは全て以下に紹介する簡単な手順で Stackato にデプロイできるようになっています:
2014051502


試しに、ブログや CMS では定番の1つになっている WordPress をデプロイしてみます。一覧の最後の方までスクロールして、"WordPress" を見つけたら、"Deploy App" ボタンをクリックします:
2014051503


WordPress アプリケーションサーバーのデプロイ情報を入力します。といってもここで気をつけるべきは一番上の "App Name" 欄だけです。デフォルトで設定されている名称をそのまま使ってもいいし、変更してもいいのですが、これがアプリケーションサーバー名の一部(****.stackato-mm7d.local の **** 部)になります。とりあえずこの例では "wordpress-y7unx" という名称にしています。最後に "Deploy Application" ボタンをクリックすると Stackato 環境にデプロイが開始されます:
2014051504


デプロイ中の画面はこんな感じでログが表示されます。しばらく待ちます・・・:
2014051505


ログに "Created app 'wordpress-y7unx(App Nameの値)'" と表示されて、ログの動きが止まったらデプロイが完了しています:
2014051506


この状況で画面左の "Instance" をクリックすると、Stackato 環境内で動いているサーバーインスタンスの一覧が表示されます。先程作成した WordPress サーバーがちゃんと Stackato 内で動いていることが確認できます。またこの画面から再起動などの管理オペレーションが可能になっています:
2014051507
 ↑仮想環境の KVM の中で動いている Stackato の中で動いている WordPress サーバー、です


では実際のアプリケーションにアクセスしてみます。

・・・が、その前に、このアプリケーションは Stackato 内で動いていて、IP アドレス自体は Stackato と同じものです。(恐らく VirtualHost 設定などで)アクセスサーバー名で切り替えられるようなので、この WordPress にアクセスするには改めて /etc/hosts を編集するなどして、サーバー名(wordpress-y7unx.stackato-mm7d.local)でアクセスできるようにしておく必要があります。

前回も紹介したように、ブラウザを利用するマシンの hosts ファイルを更に変更して、サーバー名と IP アドレスの対応を追加しておきます:
192.168.0.101 stackato-mm7d.local api.stackato-mm7d.local wordpress-y7unx.stackato-mm7d.local
(↑内容は各自の環境に合わせるようにして赤字部分を追加します


そしてブラウザで http://wordpress-y7unx.stackato-mm7d.local/ を開くと、、、WordPress の初期設定画面が表示されています。あっけなく動いてますね:
2014051508


初期設定を済ませれば、そのまま WordPress を使いはじめることができます:
2014051509


この WordPress の例に限らず、Stackato の App Store には初めから使えるアプリケーションが数多く用意されているので、これらを使ってアプリケーションをすぐに構築したり、ミドルウェア環境を手早く用意する、といったことができそうです。この WordPress アプリケーションの場合は 128MB メモリ環境で動作するようなので、これ1つ作った時点であればまだまだ余裕があるので他のアプリケーションを同時に作って動かす、ということもできると思っています(他のリソースはともかくメモリ的には)。

今回使った Stackato の mini Cloud Foundry 環境の場合はメモリ上限が 4GB と比較的小規模運用が前提になっていますが、この範囲で収まる使い方であれば、社内やチーム内利用には充分すぎる環境だと思います。仮に収まりきらなくなった場合でも、(有料の)上位エディションに移行したり、各社から提供されている Cloud Foundry ベースの環境に移行ことで運用先に困ることはないと思っています。 

こういう移行選択肢がある、ということがオープン環境の素晴らしいところです。


このページのトップヘ