連載「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 では、自分の好みのライブラリーを自由に使えますが、デプロイメントまでの過程が長く、かえって作業が面倒になる場合もあります。つまり、十分な制御ができるのと引き換えに、複雑度が増すというわけです。
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 を導入するには、以下のものをインストールしてセットアップする必要があります。
- 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 では、お好み次第であらゆる 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 に関連付けるために、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 です。分散バージョン管理に慣れていないとしても、Git がその調整を行ってくれます。Heroku に (Git
を介して) デプロイすることは、Subversion でメインラインから派生したブランチにコミットするのと似ていますが、この例の場合、Heroku
はメインのコード・リポジトリーではなく、単なるリモート・リポジトリーの 1 つです。このため、アプリケーションをデプロイするにはそのソース・コードを Git を介して push することになります。
注意する点として、Heroku では war ファイルをデプロイするのではなく、プロジェクトをまるごとデプロイすることになります。この後、サンプル・アプリケーションの
Procfile を作成するときにわかることですが、Heroku は実行するコードを検索する JVM に過ぎません
(私が言っていることを自分の目で確かめたいと思ったら、プロジェクトのパッケージ構造の中に生成された Main.java ファイルを調べて、そのエントリーを POM ファイル内で関連付けてみてください)。
Heroku のデプロイメント・モデルは直ちにわかるほど直観的ではありませんが、使い始めてみるとすぐに理解することができます。しかも、Heroku と Git との密接な統合により、さまざまなブランチを異なる環境に素早く簡単にプッシュできるようになっています。
デプロイメントのセットアップには、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 にデプロイする前に、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 はアプリケーションを単一の 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 は、私にまさにぴったりの選択肢です。
学ぶために
- 連載「Java
開発 2.0」: この dW の連載では Java 開発の様相を変える技術を詳しく探っています。これまで取り上げてきた話題には、Amazon
Elastic Beanstalk (2011年2月)、Google
App Engine (2009年8月)、RESTClient(2009年11月)、Gretty
(2011年8月)、MongoDB (2010年9月)
などがあります。
- 「分散型の Web 開発の様相を変える Git」(William von Hagen 著、developerWorks、2009年8月): 分散型のバージョン管理システムは決して新しいものではありませんが、開発者同士のやり取りや共同作業をサポートする独特の機能を備えた Git は、まさに分散型 Web 開発に大改革をもたらします。
- 「Introduction to
Maven 2」(Sing Li 著、developerWorks、2006年12月): Maven の入門書が必要な場合は、このチュートリアルが Maven について最初から最後まで案内してくれます。
- 「Apache
Wink、Eclipse、および Maven を使用して RESTful な Web サービスを開発する」(Gabriel Mateescu 著、developerWorks、2011年2月): RESTful なアプリケーション開発で Wink と Maven を組み合わせて使用する方法を学んでください。
- ナレッジ・パス「Introduction
to Platform as a Service」(2011年3月): PaaS について学ぶためのガイドです。
- 「クラウドと業界: 第 1
回 PaaS のベスト・プラクティスとパターン」(Yu Chen Zhou 他による共著、developerWorks、2010年12月): クラウド対応業界ソリューションのプラクティス、パターン、およびモデルの概念について学んでください。
- 「クラウド・コンピューティングのサービス・モデル: 第 2 回 サービスとしてのプラットフォーム」(Dan Orlando 著、developerWorks、2011年1月)。
- 「Java PaaS の比較」(Michael Yuan 著、developerWorks、2011年4月): Google App Engine、Amazon Elastic Beanstalk、CloudBees RUN@Cloud の技術を比較している記事です。
- Java Technology
bookstore で、この記事で取り上げた技術やその他の技術に関する書籍を探してください。
- developerWorks Java technology ゾーン: Java プログラミングのあらゆる側面を網羅した記事が豊富に用意されています。
製品や技術を入手するために
- Heroku を使い始めてください。
- Ruby と RubyGems をダウンロードしてください。
- Play、Gretty、および Wink は、RESTful なアプリケーションを構築するためによく使われている 3 つのオプションです。
議論するために
- developerWorks コミュニティーに参加してください。ここでは他の developerWorks ユーザーとのつながりを持てる他、開発者が主導するブログ、フォーラム、グループ、ウィキを調べることができます。

Andrew Glover は、ビヘイビア駆動開発、継続的インテグレーション、アジャイル・ソフトウェア開発に情熱を持つ開発者であるとともに、著者、講演者、起業家でもあります。また、easyb BDD (Behavior-Driven Development) フレームワークの創始者、そして『継続的インテグレーション入門 開発プロセスを自動化する47の作法』、『Groovy in Action』、『Java Testing Patterns』の 3 冊の本の共著者でもあります。詳細は彼のブログにアクセスするか、Twitter で彼をフォローしてください。