目次


Java 開発 2.0

Heroku の PaaS を使用してアプリケーションを Git にコミットする

Heroku がそのルーツである Ruby を拡張して、Java アプリケーションのスケーラビリティーに対応します

Comments

コンテンツシリーズ

このコンテンツは全#シリーズのパート#です: Java 開発 2.0

このシリーズの続きに乞うご期待。

このコンテンツはシリーズの一部分です:Java 開発 2.0

このシリーズの続きに乞うご期待。

連載「Java 開発 2.0」では最近、いくつかの記事で Java 開発での PaaS (Platform as a Service) の選択肢にスポットライトを当ててきましたが、今回は、最近 Java アプリケーションをサポートするように拡張された評判の高い PaaS システム、Heroku を紹介します。

Ruby をルーツに持つ Heroku での Java アプリケーションの開発およびデプロイメント手法は、Amazon の Elastic Beanstalk や GAE (Google App Engine) に代表される他の Java PaaS の選択肢で経験したであろう手法とは明らかに異なります。Heroku の実際の使い方について説明する前に、Heroku が Elastic Beanstalk や GAE と共通している点、そして異なる点を説明しておくと参考になるので、まずはそこから始めます。

GAE と Beanstalk: 2 つの代表的な Java PaaS

この連載の以前の記事で学んだように、Google App Engine と Amazon Elastic Beanstalk は、柔軟性という点でまったく対極に位置します。GAE はかなり厳格なサンドボックスで、そのルールに従うか、あるいはルールをまったく無視するかのどちらかを選ばなければならない一方、Elastic Beanstalk は完全にカスタマイズ可能です。完全にカスタマイズした Elastic Beanstalk でも、JVM で実行すれば Amazon の PaaS で使用することができます。GAE では使用できる Java ライブラリーが限られているだけでなく、アプリケーションのスケーリングについてはほとんど GAE によって制御が行われます。実際、アプリケーションを GAE にデプロイするということは制御を手放すことであり、ユーザーには Web アプリケーションがどこに存在するのかも、アプリケーション・インスタンスがいくつ実行されているかさえもわかりません。その反面、アプリケーションのスケーリングはすべて Google が引き受けてくれるので、言うまでもなく、GAE アプリケーションは極めて適切にスケーリングが行われます。制御を手放すということは当然、信頼できるコードを作成しなければならないことを除けば、それ以外に心配の種がほとんどなくなることを意味します。

Amazon Elastic Beanstalk は、GAE とは正反対で、ツールを自由に選択できるだけでなく、アプリケーションをどのようにスケーリングするかを大幅に制御することができます。しかしそれによる欠点も明らかで、細かい指示を出さなければ、Amazon ではほとんど何もしてくれないことです。そうは言っても、アプリケーションのデプロイメントを微調整できる極めてスケーラブルなインフラストラクチャーをお望みであれば、Amazon Elastic Beanstalk を選んで間違いはありません。

コーディングとデプロイメントの両方を含めた使いやすさを 10 から 0 (10 は非常に使いにくいこと、0 はとても楽に使えること) の尺度で測るとしたら、私は GAE には 4、Elastic Beanstalk には 6 の点数をつけます。GAE ではシームレスにスクリプトを実行し、ファイルをアップロードすることができますが、GAE に対応した開発ツールが限られているため、私が持っている能力を十分に発揮できないことがあります。逆に、Elastic Beanstalk では、自分の好みのライブラリーを自由に使えますが、デプロイメントまでの過程が長く、かえって作業が面倒になる場合もあります。つまり、十分な制御ができるのと引き換えに、複雑度が増すというわけです。

Heroku というチャレンジャー

GAE および Elastic Beanstalk と同じように、Heroku は水平スケーリングができるように作成されています。Heroku では、作成したコードは dyno と呼ばれる単位にデプロイされます。dyno とは基本的に Web コンテナーであり、システムをスケーリングするには単に dyno を追加すれば、それによって Heroku が同時に処理できる Web リクエストの数が増えていくという仕組みです。この単純な概念が、GAE を超える制御の自由をもたらすと同時に、Elastic Beanstalk では必要となる構成を不要にします。

Heroku でデプロイメントのパイプラインとなるのは、Ant でも Maven でもなく、Git です。アプリケーションをデプロイする準備が整ったら、Git の push を使用してデプロイします (このデプロイメント方法については、記事の後半で詳しく説明します)。

先ほどの使いやすさの尺度で GAE や Elastic Beanstalk と比較すると、Heroku の点数は 2 です。これは、ほぼ苦労なく使用できることを意味します。Heroku は使用するツールに関して非常に融通が利くため、それぞれのジョブに応じて最も生産性の高いツールを選んで使用することができます。構成に関して言えば、Heroku では GAE より柔軟に制御することができますが、Elastic Beanstalk ほどではなく、ちょうど良いぐらいです。また、Heroku で構築したアプリケーションは簡単に Elastic Beanstalk に移植できるので、アプリケーションのスケーラビリティーを微調整する必要が出てきた場合には、いつでもアプリケーションを Elastic Beanstalk に移せばよいのです。

Heroku の導入方法

Heroku を導入するには、以下のものをインストールしてセットアップする必要があります。

  • Ruby および RubyGems (Heroku のコマンドライン・ツールは Ruby で作成されているので、RubyGems を使用してインストールします。)
  • Heroku アカウント (無料で使い始めることができます)
  • Git
  • Maven

Maven は、Heroku に必要なわけではないことに注意してください。ここでは、ビルド・ツールとして Maven を使用しているだけです。Heroku の Java ドキュメントも Maven を使用して作られています。

アプリケーションをビルドして実行する

上記のすべてをインストールしたら、作業ディレクトリーを選択して、以下の Maven コマンドを実行します (このコマンドは、記事のページ幅に合わせて改行を挿入してありますが、実際のコマンドでは、改行は不要です)。

mvn archetype:generate -DarchetypeGroupId=org.mortbay.jetty.archetype 
  -DarchetypeArtifactId=jetty-archetype-assembler -DarchetypeVersion=7.5.0.RC0

これにより、Maven は groupId および artifactId の入力を求めるプロンプトを出すはずです。私は通常、groupId にはパッケージ名を使用し、artifactId にはプロジェクト名を使用しています。これらを入力すると、Maven は Web アプリケーションのビルドと実行を Jetty 上で行えるようなプロジェクト構造を生成します。つまりワン・ステップで、Heroku のクラウド・インフラストラクチャーにデプロイできる状態の Web アプリケーションのスケルトンが用意されるというわけです。ここでは Jetty を Maven アーキタイプとして使用していますが、Heroku が Web サーバー側でサポートするのは Jetty だけではありません。実のところ、Heroku は Jetty の存在さえ知らず、気にも掛けません。

上記の Maven コマンドによって生成されるデフォルト・アプリケーションは、ごくありふれた「hello world」アプリケーションです。実際、アプリケーションの src ディレクトリー配下にある main ディレクトリーのサブディレクトリー webapp の中を覗くと、index.html ファイルが見つかります。アプリケーションを実行すると、(お察しの通り) このファイルが「hello world」というテキストを出力します。

アプリケーションを実行する方法も同じく簡単です。新しく生成されたプロジェクト・ディレクトリー (artifactId に対して入力した名前が付けられます) でコマンド mvn install を実行すると、対象とするオペレーティング・システム (私の場合は OSX) に応じたシェル・スクリプトが生成されます。「$>sh target/bin/webapp」と入力した後、任意のブラウザーで http://localhost:8080 にアクセスすると、挨拶で迎えられることになります。

実際の Heroku

Heroku では、お好み次第であらゆる Java ライブラリーをデプロイすることができます。一例として、(少なくともこの連載を読み続けている読者にとっては) お馴染みの Magnus を、今度は Heroku で生まれ変わらせます。Magnus は Amazon の Elastic Beanstalk を紹介した私の記事で作成した、位置情報を収集するためのモバイル Web サービスです (「参考文献」を参照)。RESTful なライブラリーとしては、JAX-RS 仕様の実装の 1 つ、Apache Wink を使用することにします。この Web サービス実装が提供する PUT 用エンドポイントは、JSON を受け入れ、その JSON 文書から取得した関連データを MongoDB インスタンスに挿入します。MongoDB インスタンスは、Morphia を介して MongoHQ でホストされます (「参考文献」を参照)。

最初に行うべき作業は、Magnus の Maven pom.xml ファイルに、新たな依存関係として Wink および Morphia を追加して、このファイルを更新することです (リスト 1 を参照)。

リスト 1. Maven POM に依存関係として Wink および Morphia を追加する
<dependency>
 <groupId>org.apache.wink</groupId>
 <artifactId>wink-server</artifactId>
 <version>1.1.3-incubating</version>
</dependency>

<dependency>
 <groupId>org.apache.wink</groupId>
 <artifactId>wink-json-provider</artifactId>
 <version>1.1.3-incubating</version>
</dependency>

<dependency>
 <groupId>com.google.code.morphia</groupId>
 <artifactId>morphia</artifactId>
 <version>0.99</version>
</dependency>

さらに、この POM ファイルを更新して、Morphia の Maven リポジトリー内を探してバージョン 0.99 を取得するようにします。

リスト 2. Maven POM に新規リポジトリーを追加する
<repositories>
 <repository>
  <id>morphia repository</id>
  <url>http://morphia.googlecode.com/svn/mavenrepo/</url>
 </repository>
</repositories>

次に、位置情報リソースを作成します。つまり、ユーザーの位置を表す Wink エンドポイントを作成します。

リスト 3. Wink の LocationResource を作成する
@Path("/location")
public class LocationResource {

 @PUT
 @Consumes(MediaType.APPLICATION_JSON)
 @Path("{id}")
 public String updateAccountLocation(@PathParam("id") int accountId, JSONObject 
  requestJSON) {
  try{

   double latitude = Double.parseDouble(requestJSON.get("latitude").toString());
   double longitude = Double.parseDouble(requestJSON.get("longitude").toString());

   SimpleDateFormat formatter = new SimpleDateFormat("dd-MM-yyyy HH:mm");
   Date dt = formatter.parse(requestJSON.get("timestamp").toString());

   new Location(accountId, dt, latitude, longitude).save();
   return new JSONObject().put("status", "success").toString();
  }catch(Exception e){
   return "failure with " + requestJSON;
  }
 } 
}

上記のように、Wink でこの RESTful なサービスを記述するには、3 つのアノテーションを使用します。具体的には、HTTP メソッド (PUT) を示すアノテーション、要求するリクエストのコンテンツ・タイプ (JSON) を示すアノテーション、そしてエンドポイントがパラメーター (この例では、location/:accountId) を受け入れることを示すアノテーションです。

この Location クラスは、Elastic Beanstalk を紹介したときに導入した、Morphia がサポートするオブジェクトと同じで、指定されたアカウントの位置情報を表す文書を MongoHQ 内に作成するに過ぎません。位置情報 (実際にはモバイル機器から受信されます) は、RESTful なエンドポイントのパラメーターによって表されます。

Wink と Jetty

次に、Wink を Jetty に関連付けるために、2 つの作業が必要となります。1 つは Application クラスを作成すること、もう 1 つは web.xml ファイル内での構成です。

Wink の Application クラス (リスト 4 を参照) の目的は、対応するリソース・クラスをロードすることです。

リスト 4. Wink の Application クラス
public class MarmarisApplication extends Application {
 @Override
 public Set<Class<?>> getClasses() {
  Set<Class<?>> classes = new HashSet<Class<?>>();
  classes.add(LocationResource.class);
  return classes;
 }
}

続いてリスト 5 では、アプリケーションの web.xml ファイルに、Application クラスへのポインターや目的の URL パターン (この例の場合は、/service/resource) といった Wink に固有の属性を追加して更新します。

リスト 5. web.xml で Wink を関連付ける
<servlet>
 <servlet-name>MarmarisApp</servlet-name>
 <servlet-class>org.apache.wink.server.internal.servlet.RestServlet</servlet-class>
 <init-param>
  <param-name>javax.ws.rs.Application</param-name>
  <param-value>com.b50.marmaris.MarmarisApplication</param-value>
 </init-param>
 <load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
 <servlet-name>MarmarisApp</servlet-name>
 <url-pattern>/service/*</url-pattern>
</servlet-mapping>

テストとして、mvn install コマンドを再び実行して、アプリケーション (この例では Magnus) をローカルで起動してみてください。エンドポイント http://localhost:8080/service/location/{account_id} (ここで、account_id は番号) に JSON 文書 (リスト 6 を参照) を送信できるようになっているはずです。

リスト 6. 位置情報を表す JSON 文書
{
 "name":"location payload",
 "latitude":"46.49",
 "longitude":"71.11",
 "timestamp":"09-02-2011 14:43"
}

このアプリケーションを作成するのは難しい作業ではありませんでした。次はさらに簡単な作業に移ります。それは、アプリケーションを Heroku のクラウドにデプロイすることです!

Heroku と Git

Heroku でデプロイメントのパイプラインとなるのは、Git です。分散バージョン管理に慣れていないとしても、Git がその調整を行ってくれます。Heroku に (Git を介して) デプロイすることは、Subversion でメインラインから派生したブランチにコミットするのと似ていますが、この例の場合、Heroku はメインのコード・リポジトリーではなく、単なるリモート・リポジトリーの 1 つです。このため、アプリケーションをデプロイするにはそのソース・コードを Git を介して push することになります。

注意する点として、Heroku では war ファイルをデプロイするのではなく、プロジェクトをまるごとデプロイすることになります。この後、サンプル・アプリケーションの Procfile を作成するときにわかることですが、Heroku は実行するコードを検索する JVM に過ぎません (私が言っていることを自分の目で確かめたいと思ったら、プロジェクトのパッケージ構造の中に生成された Main.java ファイルを調べて、そのエントリーを POM ファイル内で関連付けてみてください)。

Heroku のデプロイメント・モデルは直ちにわかるほど直観的ではありませんが、使い始めてみるとすぐに理解することができます。しかも、Heroku と Git との密接な統合により、さまざまなブランチを異なる環境に素早く簡単にプッシュできるようになっています。

2 段階のデプロイメント

デプロイメントのセットアップには、2 つのステップがあります。最初のステップでは、コードを作成してローカル Git リポジトリーにコミットします。それにはターミナル・ウィンドウで、カレント・ディレクトリーをプロジェクトのルート・ディレクトリーに変更し、リスト 7 のコマンドを入力します。

リスト 7. Git の初期化とコミット
$> git init
$> git add .
$> git commit -m "initial commit before Heroko deployment"

これで、ローカル Git リポジトリーにコードのスナップショットが作成されました (つまり、コードがバージョン管理の対象になったということです)。

次のステップでは、Heroku にアプリケーションを作成します。このステップは、リスト 8 で、heroku コマンドライン・クライアントを使用して行いました (Ruby と RubyGems がインストールされていることが必須となるのは、この部分です)。

リスト 8. Heroku アプリケーションを作成する
$> heroku create marmaris --stack cedar

リスト 8 の heroku create コマンドは、cedar スタックに marmaris という名前のアプリケーションを作成します。これで、この名前は使用済みとなったため、皆さんは別のアプリケーション名を選ばなければならないことに注意してください。あるいは、アプリケーション名の指定を Heroku に任せて、固有の名前を生成してもらうという手もあります。

Heroku にはいくつものスタックがあります。cedar スタックは Java と Node.js をサポートする一方、他のスタック (Bamboo など) は比較的新しいバージョンの Ruby をサポートします。heroku create コマンドを実行すると、このコマンドによって Git 構成が更新されて、heroku というリモート・リポジトリーが追加されます。

Heroku の Procfile

コード・ベースを Heroku にデプロイする前に、Heroku にアプリケーションの実行方法を指示する必要があります。これは、Procfile を使って簡単に行えるようになっています。Procfile はコマンドを記述する単純なテキスト・ファイルです。私が以下のコマンドで作成した Procfile は、target ディレクトリーに置かれている webapp シェル・スクリプトを指しています。

リスト 9. Procfile を作成する
$> echo 'web: sh target/bin/webapp' > Procfile

新しい Procfile ファイルを作成したら、その新規ファイルについて Git に通知することが重要です。そうしないと、Git の push によってアプリケーションをデプロイするときに、Heroku にその Procfile ファイルが認識されなくなってしまいます。

リスト 10. Git への通知
$> git add Profile
$> git commit -m "updated to include my Profile"

いよいよ、アプリケーションをデプロイします。それには、リモート・リポジトリー heroku に対して Git の push を実行します (リスト 11 を参照)

リスト 11. Heroku にデプロイする
$> git push heroku

heroku から一連のメッセージが返されてきますが、そのなかから、以下のようなメッセージを探してください。

http://your_app_name.herokuapp.com deployed to Heroku

メッセージに示された URL をお好みのブラウザーで入力すると、(プロジェクト・ディレクトリーにデフォルトの index.html ファイルが残されていれば)「hello, world!」という出力が表示されるはずです。

「hello, world!」を出力するアプリケーションは常に興味深いものですが、このアプリケーションで目的としているのは、HTTP PUT によって送られてくる位置情報を受け入れることです。それには、WizTools.org の RESTClient を使用すれば、先ほど作成した RESTful なエンドポイントに対して HTTP PUT を実行し、JSON 文書を提供することができます。これで見事に、success という素晴らしい言葉を含んだ JSON レスポンスが送信されます。

Heroku のスケーリングと保守

デフォルトでは、Heroku はアプリケーションを単一の dyno 上で実行します。この dyno は無料であり、基本的にアクティビティーが行われていない間は自動的にオフになり、リクエストを受け取ると自動的に起動されます。アプリケーションをスケーリングする必要がある場合には、dyno を追加します。それには、以下の heroku scale コマンドを使用することができます。

リスト 12. アプリケーションをスケーリングする
$> heroku scale web=2

必要な場合には、要求する dyno の数を減らすことによって、アプリケーションをスケールバックすることもできます。上記の例で言うと、web=1 にスケールバックするなどです。この dyno の追加や削除によるスケーリングの操作は、Heroku の Web インターフェースで行うこともできます。

また、heroku logs コマンドを使用して、Heroku のログをリアルタイムで追跡することもできます (リスト 13 を参照)。

リスト 13. Heroku のログをリアルタイムで監視する
$> heroku logs -t

heroku コマンドライン・クライアントは、アプリケーションをスケーリング、監視、管理するための多数の機能をサポートします (ドキュメントについては「参考文献」を参照)。また、デフォルトのデータストアに加え、多くのサード・パーティー製アドオンもサポートします。ぜひ、Heroku の MongoHQ および PostgreSQL サポートについて調べてください。

まとめ

Heroku と Git の緊密な統合は、クラウドに Java アプリケーションをデプロイしてスケーリングする際の新しいパラダイムを提示しているだけでなく、このプロセスを並外れて容易で強力なものにしています。全体としてみると、Java ライブラリーを無制限に使用して、最終的に仕上がったアプリケーションをほとんど苦労することなくデプロイできる Heroku は、私にまさにぴったりの選択肢です。


ダウンロード可能なリソース


関連トピック


コメント

コメントを登録するにはサインインあるいは登録してください。

static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=60
Zone=Java technology, Cloud computing
ArticleID=782564
ArticleTitle=Java 開発 2.0: Heroku の PaaS を使用してアプリケーションを Git にコミットする
publish-date=01062012