Gradleを使ったWebアプリケーションのさくさく開発(Herokuデプロイ編)

久しぶりのGradleシリーズです。これまで作ったアプリケーションをHerokuにデプロイしてみます。

昔はRailsアプリケーションに限っていたようですが、現在のHerokuではベータ的にGradleもサポートされています。

Q: Does Heroku support Gradle builds?
A: Yes. Gradle support is in private beta currently. Please contact the Heroku team if you want to try our Gradle build support.

http://wiki.developerforce.com/page/Webinar:_Heroku_Java_Webinar_FAQ

というわけで、試してみました。

Herokuアカウントを作る

アカウントをもっていなかったので、https://id.heroku.com/login の下にある「Sign Up」からアカウントを作成します。必要なのはメールアドレスのみ。

Heroku Toolbelt をインストール

Herokuの各コマンドが利用できるように、https://toolbelt.heroku.com/ からインストールします。インストールが完了したかを確認するには、コマンドプロンプトから

heroku version

と実行して、バージョン情報が表示されればOK(私の環境だと「heroku/toolbelt/2.39.3 (i386-mingw32) ruby/1.9.3」と表示されます)。

Herokuにアプリケーションを追加する

今まで試してきた https://github.com/tq-jappy/gradle-sample-webapp を利用します。ローカルリポジトリ(git cloneしたディレクトリ)にて、

heroku create --stack cedar gradle-sample-webapp 

と実行します。gradle-sample-webappの部分はHeroku全体でユニークでないといけないみたいですが、運良く(?)怒られなかったのでこのまま進めます。

「--stack ceder」オプションは、Javaアプリケーションを実行可能にするために必要です。

また -s cedar というオプションはherokuに作られる実行環境の種類を指定するものです。
この場合はCedarスタックという最新の実行環境が適用された実行環境ができます。
ちなみに -s cedar を指定しないと bambooスタックで実行環境がつくられます。

CedarスタックとBambooスタックの最も大きな違いはBambooスタックではRubyアプリケーションしか実行できないがCedarスタックではRuby以外の言語(JavaPython、Node.js、Scalaなど)が実行可能なことです。

http://dqn.sakusakutto.jp/2012/04/github-heroku-push.html

heroku createの後は、通常であれば、

git push heroku master

とするだけでデプロイが完了するのですが、今回の場合、カスタマイズが必要です。

Herokuで動かすためのGradleアプリケーションのカスタマイズ

devcenter-gradleを参考にアプリケーションをカスタマイズしていきます。また、うまく行かなかった時の原因や挙動を調べるのに、Heroku Buildpack Gradleリポジトリを参照しました。

まず、Heroku上のGradleアプリケーションでは、デフォルトでOpenJDK 1.6とgradle-1.0-milestone-5が使われるので、それぞれのバージョンを変更するための設定についてまとめておきます。

OpenJDKのバージョンを1.7に上げる

アプリケーションの直下(build.gradleと同じ階層)にsystem.propertiesを作って以下のように記述します。

java.runtime.version=1.7
Gradleのバージョンをgradle-1.0-milestone-5以外にする

「git push heroku master」を実行してHerokuにpushすると分かるのですが、以下のようなメッセージが表示されます。

-----> Installing gradle-1.0-milestone-5..... done
       (Use the Gradle Wrapper if you want to use a different gradle version)

今の最新は1.6なので、1.0-milestone-5だとちょっと古いですよね。

というわけで、別バージョンを使うためにGradle Wrapperを使います。内容はほとんどbluepapa32さんの「インストールレスで Gradle してみる」の通りです。

まずはbuild.gradleに以下を追加。

task wrapper(type: Wrapper) {
    gradleVersion = '1.6'
}

あとは「gradle wrapper」タスクを実行して、作成されたファイル・フォルダをコミットします。HerokuのGradle Buildpackを見ると分かるのですが、ビルドディレクトリにgradlewがあると、gradlewを優先して実行するみたいです。

手順としてはこれでよいと思うのですが、実際に試してみると残念ながらGradle Wrapperを使うとエラーになり、デプロイできませんでした…。

エラー内容はgradlewを実行する時の permission denied という初歩的なものですが、修正の仕方がよく分からなかったので、諦めておとなしく1.0-milestone-5を使うことにします。

幸い、作っていたbuild.gradleでは、FindBugs, CheckStyleプラグインに関する記述を削るだけで、1.0-milestone-5でも使えるものになりました。

stageタスクを作る

というわけでOpenJDK1.7とGradle1.0-milestone-5を使うという前提で、以下の話を進めます。

Herokuに対してpushすると、stageという名前のタスクを実行してデプロイが行われるようです。

ガイドラインにしたがって、build.gradleに以下を追加しました。

apply plugin: 'application'
// 略
mainClassName = "sample.Main"
applicationName = "app"
// 略
task stage(dependsOn: ['clean', 'installApp'])
メインクラスを作る

https://github.com/heroku/devcenter-gradle に書かれている HelloWorld.java と、以前のエントリーで作ったJettyの起動・停止を行うRuleをベースに、Webアプリケーションを起動するメインクラスを作成します。

sample.Main

package sample;

import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.webapp.WebAppContext;

public class Main {

    /**
     * @param args
     */
    public static void main(String[] args) throws Exception {
        Server server = new Server(Integer.valueOf(System.getenv("PORT")));

        WebAppContext context = new WebAppContext();
        context.setServer(server);
        context.setContextPath("/gradle-sample");
        context.setResourceBase("src/main/webapp");
        context.setClassLoader(Main.class.getClassLoader());
        server.setHandler(context);

        System.out.println("server starting...");
        server.start();
        server.join();
    }
}
Procfileを作る

Procfileというのは、Heroku上で動作するプロセスと、それに割り当てる具体的な処理を記述するHerokuのお約束ファイルみたいですね。

applicationNameはbuild.gradle中にて"app"としているので、以下のような記述になります(ガイドラインとまったく同じ)

web: ./build/install/app/bin/app
いよいよデプロイ

必要なカスタマイズが終わったので、Herokuにpushしてデプロイしてみます。

git push heroku master

うまくいけば、上記のように表示され、デプロイに成功します(長かった…)。

Herokuにログインして、アプリケーションを選択し、設定を見ると、以下のとおり、Gradleアプリケーションとして認識されているようです。


Herokuリソースの設定

仕上げに、Herokuのダッシュボードからリソースの設定を行います。「web ./build/install/app/bin/app 」のところにチェックを入れるだけです。

この操作についての正確な説明はHerokuの勉強不足でうまくはできないのですが、dynoというのがリソースの単位で、課金の計算に使われる模様(1dyno1時間で1Dyno時間として計算され、750Dyno時間までは無料です)。今みたいに無料の範囲で遊んでいる動作確認の段階では、不要だったら停止しておくのが無難?。

確認

※ 注:以下のURLは今後、名前を変えたり、アプリケーション自体を停止しておくため、アクセスできなくなる予定です。

アプリケーション名はgradle-sample-webappであり、sample.Mainにてコンテキストパスは"/gradle-sample"を設定しているので、下記URLにブラウザからアクセスしてみます。

http://gradle-sample-webapp.herokuapp.com/gradle-sample/hello

Hello, gradle!

無事に表示されました!

また、Heroku Javaの「RESTful API with JAX-RS」を参考に、時刻情報をJSONで返すサービスも試してみました。以下のURLにアクセス。

http://gradle-sample-webapp.herokuapp.com/gradle-sample/rest/time

{"timezone":"Coordinated Universal Time","year":2013,"month":5,"day":26,"hour":9,"minute":10,"second":15}

こちらも無事に表示されました!

Tips

エラーを調べたり、確認するにあたり、使ったherokuコマンドについて列挙しておきます。

  • heroku ps
    • 各dynoのステータスを確認
  • heroku run bash
    • bashを実行するための新たなdynoが起動し端末に接続
  • heroku logs
    • ログを確認

まとめ

そもそもHerokuを利用したのは今回がはじめてだったのですが、GradleアプリケーションもHerokuにデプロイして動かせることが分かりました*1。ただし、Gradle Wrapperが使えなかったので、Gradleのバージョンは1.6ではなく、1.0-milestone-5での動作検証です。HerokuのGradleサポートはまだベータであり、今後の発展にぜひ期待したいところです。ベータということで、本記事の内容は今後古くて使えなくなる可能性がありますので、ご注意ください。

Herokuにデプロイできるようにするために、一度アプリケーションのカスタマイズをしてしまえば、以降は「git push heroku master」だけでデプロイできるので、お気軽に公開するには選択肢としてHerokuはアリかなぁと思いました。まぁ、もっと簡単な(or 相性のよい)PaaSがあるかもしれないですが、知らないので…。

*1:つまらないアプリを世に公開してしまいましたが、(゚ε゚)キニシナイ!!(笑)