Heroku での Gradle アプリのデプロイ
最終更新日 2023年09月29日(金)
Table of Contents
この記事では、既存の Java アプリを Heroku にデプロイする方法について説明します。
Heroku をはじめて使う場合は、『Heroku スターターガイド (Gradle)』のチュートリアルから始めた方がよい場合もあります。
前提条件
このチュートリアルでは、以下が用意されていることを前提としています。
- Gradle をビルドツールとして使用する既存の Java アプリ。
- 無料の Heroku アカウント
- Heroku CLI
- Java JDK
概要
Heroku の Java サポートについての詳細は、『Heroku Java サポート』の記事に記載されています。
Heroku の Gradle 用 Java サポートは、build.gradle
ファイル、settings.gradle
ファイル、または gradlew
ファイルを含むアプリケーションに適用されます。
gradlew
を使用している場合、gradle/wrapper/gradle-wrapper.jar
および gradle/wrapper/gradle-wrapper.properties
ファイルも Git リポジトリに追加する必要があります。また、.gitignore
ファイルでそれらを無視することはできません。これらのファイルを追加しない場合、次のようなエラーが表示されます。
-----> executing ./gradlew stage
Error: Could not find or load main class org.gradle.wrapper.GradleWrapperMain
以下のコマンドを実行することにより、ラッパーファイルを追加できます。
$ git add gradle/wrapper
$ git commit -m "Gradle Wrapper"
続いて git push heroku master
を使用して再度デプロイします。
ビルドファイルが正しく設定されていることの確認
Gradle buildpack は、アプリで検出したフレームワークに応じて、異なるビルドタスクを実行します。Spring Boot の場合、./gradlew build -x test
を実行します。Ratpack の場合、./gradlew installDist -x test
を実行します。既知の Web フレームワークが検出されない場合、./gradlew stage
を実行します。
ビルドをカスタマイズする必要がある場合、build.gradle
ファイルに stage
タスクを作成できます。このタスクが存在する場合、Heroku は、フレームワークのデフォルトを優先してこのタスクを実行します。Spring Boot を使用している場合、ステージタスクは次のように表示されます。
task stage(dependsOn: ['build', 'clean'])
build.mustRunAfter clean
次のように GRADLE_TASK
環境設定を行うことにより、デフォルトのタスクを上書きすることもできます。
$ heroku config:set GRADLE_TASK="build"
ご使用のフレームワークが自動的に依存関係をベンダー化しない場合、build.gradle
ファイルには、アプリが依存している jar ファイルを build/libs
ディレクトリにコピーするように Gradle に指示する Copy
タスクを含める必要があります。この方法により、これらは slug に配置され、.m2
ディレクトリを slug から除去できます。これは次のようになります。
task copyToLib(type: Copy) {
into "$buildDir/libs"
from(configurations.compile)
}
stage.dependsOn(copyToLib)
Heroku ビルド中にテストを実行させない場合は、カスタム環境設定で -x test
オプションを提供することも、build.gradle
で次のコードを使用して stage
タスクの実行中に test
タスクを無効にすることもできます。
gradle.taskGraph.whenReady {
taskGraph ->
if (taskGraph.hasTask(stage)) {
test.enabled = false
}
}
この例の stage
を編集して、Heroku で実行するように設定しているどのタスクにも適合させることができます。
JDK の指定
オプションで、JDK を指定できます。詳細は、「Java バージョンの指定」を参照してください。
デフォルトの Web プロセスタイプ
Gradle buildpack は、Spring Boot および Ratpack Web フレームワークの使用を自動的に検出します。Spring Boot の場合、次のコマンドで、Web プロセスタイプを作成します。
java -Dserver.port=$PORT $JAVA_OPTS -jar build/libs/*.jar
Ratpack の場合、buildpack は次の形式でコマンドを使用します。
build/install/${rootProject.name}/bin/${rootProject.name}
ここで ${rootProject.name}
は、Gradle ビルドで (多くの場合 settings.gradle
で) 設定されている値です。
デフォルトの web
コマンドをカスタマイズしたり上書きしたりする必要がある場合は、Procfile
を作成する必要があります。
Procfile
Procfile は、アプリケーションのルートディレクトリにあるテキストファイルです。このファイルを使って、プロセスタイプを定義し、アプリを起動するために実行するコマンドを明示的に宣言します。Spring Boot の場合、Procfile
は次のようになります。
web: java -Dserver.port=$PORT $JAVA_OPTS -jar build/libs/demo-0.0.1-SNAPSHOT.jar
これらの例はどちらも、単一のプロセスタイプの web
と、その実行に必要なコマンドを宣言しています。「Web」という名前が重要です。これは、このプロセスタイプを Heroku の HTTP ルーティングスタックにアタッチし、デプロイ後に Web トラフィックを受信することを宣言しています。
ビルド成果物を Git の外部に保持する方法
.gitignore
ファイルを作成することによって、ビルド成果物がリビジョン管理の対象に入らないようにします。標準的な .gitignore
ファイルは次のとおりです。
build
.gradle
.idea
out
*.iml
*.ipr
*.iws
.DS_Store
アプリのビルドとローカルでの実行
アプリをローカルでビルドするには、次を実行します。
Git Bash
アプリケーションを使用して、Windows でコマンドシェルを開きます。CLI のインストール中に、このアプリケーションのショートカットがデスクトップに追加されます。$ ./gradlew stage
$ heroku local --port 5001
Windows の場合は次のようにします。
$ gradlew.bat stage
$ heroku local --port 5001
これでアプリは http://localhost:5001/ 上で実行されます。
Heroku へのアプリケーションのデプロイ
変更内容を Git にコミットしたら、アプリを Heroku にデプロイできます。
$ git add .
$ git commit -m "Added a Procfile."
$ heroku login
Enter your Heroku credentials.
...
$ heroku create
Creating arcane-lowlands-8408... done, stack is heroku-18
http://arcane-lowlands-8408.herokuapp.com/ | git@heroku.com:arcane-lowlands-8408.git
Git remote heroku added
$ git push heroku master
...
-----> Gradle app detected
...
-----> Launching... done
http://arcane-lowlands-8408.herokuapp.com deployed to Heroku
アプリをブラウザで開くには、heroku open
と入力します。
Webapp Runner を使用した WAR ファイルのデプロイ
WAR デプロイツールを使用して、事前コンパイル済みの WAR ファイルをデプロイできますが、WAR ファイルが Heroku 上でコンパイルされている場合は、次の設定を build.gradle
に追加することによって、Tomcat Webapp Runner を含める必要があります。
dependencies {
compile 'com.github.jsimone:webapp-runner:8.5.11.3'
}
task stage() {
dependsOn clean, war
}
war.mustRunAfter clean
task copyToLib(type: Copy) {
into "$buildDir/server"
from(configurations.compile) {
include "webapp-runner*"
}
}
stage.dependsOn(copyToLib)
続いて、次のように Procfile
を編集します。
web: java -jar build/server/webapp-runner-*.jar build/libs/*.war
最後に、git push
でアプリを再デプロイします。
Grails 3 の使用
Grails 3 は、WAR ファイルとしてビルドされている場合、上記のように Webapp Runner と、build.gradle
で次の設定を必要とします。
tasks.stage.doLast() {
delete fileTree(dir: "build/distributions")
delete fileTree(dir: "build/assetCompile")
delete fileTree(dir: "build/distributions")
delete fileTree(dir: "build/libs", exclude: "*.war")
}
これは、stage
コマンドの適切なタスクを実行し、不要なビルド成果物を slug から削除します。これの完全な例については、『Grails 3 例アプリの build.gradle ファイル』を参照してください。
Procfile
には、次のような内容が含まれている必要があります。
web: cd build ; java -Dgrails.env=prod -jar ../build/server/webapp-runner-*.jar --expand-war --port $PORT libs/*.war
これによって、Grails が必要とする build
ディレクトリに移動し、WAR ファイルを使用して Tomcat Webapp-Runner ランチャーを実行します。
複数プロジェクトのビルドのデプロイ
複数プロジェクトの Gradle ビルドには様々なタイプがあり、これらを Heroku にデプロイする方法は、これらがどのように実行されるかによって異なります。以下の説明は、最も一般的なパターンを扱ったものです。
同じプロジェクトに複数のプロセスタイプがある場合
Gradle リポジトリ内のサブプロジェクトに、別々の Heroku dyno タイプに対応する複数のプロセスタイプが含まれている (たとえば、web
プロジェクトと worker
プロジェクトが同じリポジトリにある) 場合、Heroku が stage
タスクを実行するときに、これらが両方ともビルドおよび実行されるように Gradle を設定できます。
最初に、すべてのサブプロジェクトの stage
タスクを定義するコードを含む gradle/heroku/stage.gradle
ファイルをリポジトリに作成します。次に例を示します。
task stage(dependsOn: ['clean', 'shadowJar'])
続いて次のように、ルートプロジェクトの build.gradle
で stage.gradle
を適用します。
subprojects {
// ...
apply from: file("$rootProject.projectDir/gradle/heroku/stage.gradle")
}
最後に、サブプロジェクトに従って、成果物を実行するコマンドで Procfile
を作成します。たとえば、次のようになります。
web: java -jar build/libs/server-all.jar
worker: java -jar build/libs/worker-all.jar
このようにして設定されたアプリケーションの完全な例については、「gradle-multi-project-example Github プロジェクト」を参照してください。
同じプロジェクトの複数のアプリケーションタイプ
場合によっては、Gradle プロジェクトには、Heroku で実行できないサブプロジェクトが含まれます。たとえば、サーバーアプリケーションとコードを共有する Android アプリが、リポジトリに含まれることがあります。この場合、Heroku にデプロイするときに、サーバー側のプロジェクトだけをビルドできます。settings.gradle
において、フラグをチェックする if
ブロックにその include
文をラップすることにより、Android サブプロジェクトを除外できます。
if (INCLUDE_ANDROID == "true") {
include 'android', 'android:app'
}
この場合、フラグがオフになるように、Heroku 上で GRADLE_TASK
を設定できます。
$ heroku config:set GRADLE_TASK="-Dorg.gradle.project.INCLUDE_ANDROID=false :backend:stage"
これは、stage
タスクを伴う backend
と呼ばれるサブプロジェクトがあることを前提としています。Heroku は、コードを受信すると、GRADLE_TASK
環境設定で定義されたタスクを実行します。
同じプロジェクトの複数の Web サーバーアプリ
Gradle リポジトリが、同じプロジェクト内の複数の Web サーバーアプリケーションから構成される場合、プロジェクトを複数の Heroku アプリにデプロイする必要があります。この場合、環境設定でパラメーター化することにより、両方のアプリケーションで機能できるだけ十分に柔軟な Procfile
が必要です。たとえば、Procfile
は次のようになります。
web: $WEB_CMD
続いて、サブプロジェクトの起動に使用される適切なコマンドを使用して、WEB_CMD
環境設定を、各 Heroku アプリ上で設定できます。たとえば、次のようになります。
$ heroku config:set WEB_CMD="java -jar build/libs/server-1.jar" -a sushi-1
$ heroku config:set WEB_CMD="java -jar build/libs/server-2.jar" -a sushi-2
git push
でアプリに直接デプロイしている場合は、--remote
フラグで heroku create
を実行することにより、それらのアプリを Gradle リポジトリと関連付ける必要があります。または、dyno が 0 の「マスター」アプリと組み合わせて、Heroku Pipelines を使用できます。マスターアプリは、デプロイするときに、プロジェクト内のすべての成果物をビルドします続いて、slug アップストリームを、それぞれ WEB_CMD
環境設定が異なる複数のアプリにプロモートできます。このようにして、Gradle 成果物を一度だけビルドする必要があります。