Filed under Grails, Open Source, Groovy
Today I decided to find out exactly how easy it was to deploy a grails 3 application to Heroku. I have never tried a deployment of a java based app to heroku (just Ruby) so this was a bit of a learning experience for myself. I have however, attempted to deploy to Cloud Foundry in a previous expirement with some success there.
Grails 3 has a much smaller memory footprint than its preceding versions and a much cleaner build architecture than before. Which should help us fit both build time and startup time within the time limit restrictions given by heroku.
The first step is to go ahead and create a grails application. In this example I will call my app 'Quest' and use it to keep track of some ideas for Quests (seemed like it fit the theme of Grails).
#I use gvm to set my grails version
gvm use grails 3.0.1
grails create-app quest
cd quest
Sweet now that we have a grails app lets get started. First I should create a domain class for storing my quest ideas
grails create-domain-class Quest
Now that I have created a file for storing my quests lets go ahead and set some of the properties we want to keep track of on our Quest.groovy
package quest
class Quest {
String name
String description
Integer difficulty=9
Date dateCreated
Date lastUpdated
static mapping = {
description type: 'text'
}
static constraints = {
name unique:true
}
}
Ok now that thats done lets go ahead and generate some controller view code for a quick example:
grails generate-all quest.Quest
Now we have a basic grails app to play around with on heroku. Time to prep the build. The first thing we need to do is configure out grails app to connect to the shared Postgres database that heroku provides. The somewhat dated heroku documentation about running grails has a section that is somewhat relevant here.
First lets go ahead and create an application.groovy file in grails-app/conf
By default Heroku gives us a reference to the database connection in the form of a DATABASE_URL
environment variable. We can extract the elements we need to connect the dataSource provider to it via the following.
environments {
production {
dataSource {
dbCreate = "update"
driverClassName = "org.postgresql.Driver"
dialect = org.hibernate.dialect.PostgreSQLDialect
uri = new URI(System.env.DATABASE_URL?:"postgres://test:test@localhost/test")
url = "jdbc:postgresql://" + uri.host + ":" + uri.port + uri.path
username = uri.userInfo.split(":")[0]
password = uri.userInfo.split(":")[1]
}
}
}
NOTE: This file will get merged with the application.yml
file and not take precedence. so you should also remove the entire dataSource
block from application.yml
.
Next up we need to define a new task in our build.gradle
file. When Heroku sees this application it will detect that it was built using gradle and use the gradle build tool configuration.
Add these dependency lines to your build.gradle
:
runtime 'postgresql:postgresql:8.4-702.jdbc3'
provided "org.springframework.boot:spring-boot-starter-tomcat"
And at the bottom of my build.gradle
the following should be added:
task stage() {
dependsOn clean, war
}
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")
}
war.mustRunAfter clean
What we are doing here is setting up a new task called stage
. This task will run the clean
and assemble
tasks in the appropriate order. For our purposes we only really need the generated war
created by the build task to run the application. The rest of the files created for distribution should be removed otherwise your heroku Slug size will be way beyond the 300MB limit. By doing this your slug size should be hovering around 102MB.
Next we need to copy in the webapp-runner
into our project:
mkdir server
curl http://repo2.maven.org/maven2/com/github/jsimone/webapp-runner/7.0.34.3/webapp-runner-7.0.34.3.jar > server/webapp-runner.jar
Now we need to setup the Procfile
. This file tells Heroku what type of application you are running as well as how to run it. Create a file in the root of the project called Procfile
:
web: cd build/libs ; java $JAVA_OPTS -Dgrails.env=prod -jar ../../server/webapp-runner.jar --port $PORT *.war
This tells the heroku runtime to run the jar file within the build/libs
folder. Currently it is very important that you move the current directory because if you don't grails will detect the root grails-app
folder and assume it is running in a development style mode, therefore starting up incorrectly. This is currently an issue in version 3.0.1
and should be resolved in the coming weeks.
NOTE: You may have noticed we are using a war file to run within heroku instead of the grails 3 fatJar container. This is because the fatJar can take more than 60 seconds to bind to the listening port of the dyno. If this happens Heroku will shutdown your app and mark it as crashed. By running as a war file within the webapp-runner we allow the tomcat server to bind to the port immediately and start up the application after the fact.
The last file we need to create is a .gitignore
file appropriate for git:
.DS_Store
build/
.gradle
Now we need to initialize our app as a git repository and create a heroku application:
git init
git add .
git commit -am 'first commit'
heroku create
heroku addons:create heroku-postgresql
NOTE: If you have not done so already you may need to install the heroku cli by running gem install heroku
.
Now deploying to heroku is as easy as pushing to a git repository:
git push heroku master
Now you can watch as heroku's build servers compile your project into a nice Slug that can be run across the dyno architecture. At the end of the deployment type heroku open
to open your browser to the url of your running application. It may take a few minutes for your application to startup. You can check the status of the startup process by running heroku logs