久しぶりのGradleシリーズです。これまで作ったアプリケーションをHerokuにデプロイしてみます。
昔はRailsアプリケーションに限っていたようですが、現在のHerokuではベータ的にGradleもサポートされています。
Q: Does Heroku support Gradle builds?
http://wiki.developerforce.com/page/Webinar:_Heroku_Java_Webinar_FAQ
A: Yes. Gradle support is in private beta currently. Please contact the Heroku team if you want to try our Gradle build support.
というわけで、試してみました。
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以外の言語(Java、Python、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リソースの設定
仕上げに、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}
こちらも無事に表示されました!
まとめ
そもそもHerokuを利用したのは今回がはじめてだったのですが、GradleアプリケーションもHerokuにデプロイして動かせることが分かりました*1。ただし、Gradle Wrapperが使えなかったので、Gradleのバージョンは1.6ではなく、1.0-milestone-5での動作検証です。HerokuのGradleサポートはまだベータであり、今後の発展にぜひ期待したいところです。ベータということで、本記事の内容は今後古くて使えなくなる可能性がありますので、ご注意ください。
Herokuにデプロイできるようにするために、一度アプリケーションのカスタマイズをしてしまえば、以降は「git push heroku master」だけでデプロイできるので、お気軽に公開するには選択肢としてHerokuはアリかなぁと思いました。まぁ、もっと簡単な(or 相性のよい)PaaSがあるかもしれないですが、知らないので…。
*1:つまらないアプリを世に公開してしまいましたが、(゚ε゚)キニシナイ!!(笑)