Migrate your Java Application from Heroku to OpenShift


This blog post outlines steps for migrating a Java application running on Heroku over to Red Hat's Platform as a Service, OpenShift.

There are several reasons why you might want to migrate your app to OpenShift. One advantage is that the OpenShift platform itself is open source, not just the technologies and components that run on it. You can take the OpenShift Origin project and run your own cloud platform. OpenShift autoscales applications and natively supports MySQL and MongoDB, as well as PostgreSQL. Your app structure and config is created automatically thanks to the cartridge system and developers have shell access to the app containers -- called gears -- via SSH, which makes it easy to explore what is going on at runtime should you need to. OpenShift is also enterprise-grade, offering full Java EE support with JBoss, a high level of security backed by Red Hat Enterprise Linux and SELinux containerization, and an on-premise private cloud solution, OpenShift Enterprise.

OpenShift has excellent support for Java applications big and small. There is an array of preconfigured cartridges and quickstarts from Red Hat and the OpenShift community to help you quickly and easily get your app online, whether you want it to run on JBoss, Tomcat, Glassfish or Jetty. There is support for frameworks such as Spring and tools including Maven, Cron and Jenkins. OpenShift includes a DIY application cartridge that enables you to bring in additional technologies you might need; a lot of the time you will find someone in the community has already created a cartridge or quickstart for the tech you require.

Given the diversity of options for running Java on OpenShift, this blog post cannot cover all possible situations. However, by sharing how I migrated one particular application to the OpenShift PaaS I am aiming to touch on the main points to consider, whatever your technology stack.

Application to Migrate

The application I have chosen to migrate is one that is used as a Java example for Heroku, the Spring Petclinic app. I have modified it to run on Tomcat rather than Jetty based on another example, as this seemed a more typical use case, and also updated it to use Java 7. The application uses Spring MVC, Hibernate and a PostgreSQL database.

The source code for the modified application is on GitHub. To view the Heroku version, clone the repository and check out the tag heroku with the command git checkout tags/heroku.

Set up OpenShift

The first step towards migration is to sign up for an OpenShift Online account, which allows you to put three apps online for free. At the time of writing, the combined free resources allocated to each user was 1.5GB of memory and 3GB of storage.

Once you have an OpenShift Online account, install the RHC client tools by following the instructions for your operating system.

Finally, run the following on the command line to set up your machine for creating apps with OpenShift:

$ rhc setup

Create OpenShift App

In your terminal, run the following command to create a new OpenShift application called petclinic with the Tomcat 7 and PostgreSQL 9.2 cartridges. These cartridges are preconfigured application components that run in OpenShift's app containers, called gears. You could of course choose a different application server or database at this point, but the sample app requires Tomcat and PostgreSQL.

$ rhc app create petclinic tomcat-7 postgresql-9.2

When the command has finished running, you will have a copy of the new application's Git repository, the URL where your app can be viewed and the SSH details for the app.

Remove Sample App

If you navigate to the directory where the new OpenShift app has been cloned, you will see it contains several files and directories to help you get started with Tomcat. There is a sample app in src and a corresponding pom.xml, which configures the Maven build that occurs when you push changes to the cloud. There is also a webapps directory, which you can use to deploy prebuilt binaries rather than pushing the app source code.

Another item of note is the .openshift directory, which is part of every OpenShift cartridge and contains important config. There is an action_hooks directory for scripts to run during different phases of the application lifecycle, a config directory containing Tomcat config files, a markers directory for specifying build options such as hot deployment, and a cron directory for scheduling jobs.

We don't need the sample application, so you can remove it and commit the changes:

$ git rm -rf src pom.xml
$ git add -A
$ git commit -m "Removed default app files"

Add Original App Source

The next step is to add your Heroku app's source code to the OpenShift repository. There are multiple ways you could do this. If you have the Heroku app checked out locally, you could just copy across the files and then add and commit them in Git. My source code is on GitHub with the tag heroku, so I used the following Git commands instead. This approach has the benefit of maintaining your Git history.

$ git remote add upstream -m master git@github.com:codemiller/migrate-java-app.git
$ git pull -s recursive -X theirs upstream tags/heroku master --tags
$ git reset --hard heroku

Remove Heroku Files

At this point you can delete Heroku-specific files. The main candidate is Procfile, which is used to start the Java process on Heroku. This is no longer necessary as the OpenShift cartridge already has the configuration required to start and stop Tomcat, run a Maven build and deploy the built application to Tomcat. If there is something your app needs to do at particular points in the lifecycle, you can add scripts to .openshift/action_hooks.

I also deleted the system.properties file, which was used to specify the Java version required on Heroku. At the time of writing, the OpenShift Tomcat 7 cartridge came out the box with Java 7, so no configuration for this was required. If we wanted Java 6 instead, we would remove the java7 marker file in .openshift/markers.

$ git rm Procfile system.properties
$ git add -A
$ git commit -m "Deleted Heroku-specific files"

Add OpenShift Config

To get the Java app running on Tomcat on OpenShift, we need to add an OpenShift profile in the Maven build config. Open the pom.xml and add the following profile snippet. You may want to copy across any other special config you have elsewhere for the maven-war-plugin.

<project>
    ...
    <build>
        ...
    </build>
    <profiles>
        <profile>
            <id>openshift</id>
            <build>
                <finalName>${project.artifactId}</finalName>
                <plugins>
                    <plugin>
                        <artifactId>maven-war-plugin</artifactId>
                        <version>2.1.1</version>
                        <configuration>
                            <outputDirectory>webapps</outputDirectory>
                            <warName>ROOT</warName>
                        </configuration>
                    </plugin>
                </plugins>
            </build>
        </profile>
    </profiles> 
</project>

I also chose to set OpenShift to hot deploy new builds by creating an empty marker file. This command is *nix-specific, so on Windows you will have to create the file using another method.

$ touch .openshift/markers/hot_deploy

Add and commit the changes in Git.

$ git add -A
$ git commit -m "Added OpenShift config"

Update Database Config

The last change is to update the application so it can connect to the new database on OpenShift. We haven't changed the database type -- it is still PostgreSQL -- but we need to update the environment variables used.

Open src/main/resources/META-INF/spring/applicationContext.xml and update the constructor argument for the dbUrl bean from ${DATABASE_URL} to the following:

${OPENSHIFT_POSTGRESQL_DB_URL}/${OPENSHIFT_APP_NAME}

The OpenShift environment contains variables for all of the component parts of the PostgreSQL connection string; the URL has the form postgresql://OPENSHIFT_POSTGRESQL_DB_USERNAME:OPENSHIFT_POSTGRESQL_DB_PASSWORD@$OPENSHIFT_POSTGRESQL_DB_HOST:$OPENSHIFT_POSTGRESQL_DB_PORT, where each of the captialized strings are environment vars you can use.

You can view all of the OpenShift environment variables by SSHing into the app gear and running the command printenv. You can also set custom environment variables with the RHC client tools.

Add and commit the change in Git.

$ git add -A
$ git commit -m "Updated database connection string"

Push App to OpenShift

Push the app to OpenShift with Git:

$ git push

View the app in your browser. You can use the command rhc app show to view the URL.

Migrate Data

You may wish to migrate data between your Heroku and OpenShift PostgreSQL databases, which you can achieve with a SQL dump. I extracted a data dump from my Heroku app with the following commands (if you do not have curl on your system, you could paste the URL generated by heroku pgbackups:url in your browser instead):

$ heroku addons:add pgbackups
$ heroku pgbackups:capture
$ curl -o latest.dump `heroku pgbackups:url`

To feed this data dump file into the OpenShift database, I ran the following commands to start port forwarding and do the import. You can use rhc app show to check your gear's SSH and database details; make sure you replace the admin username below with your own. If you do not have PostgreSQL installed locally, you can SSH into your gear and run your PostgreSQL commands there instead.

$ rhc port-forward -a petclinic
$ pg_restore -h 127.0.0.1 -p 5432 -U admin1abcabc -d petclinic < latest.dump

Other Considerations

If your app creates files in the repository directory that you wish to persist across rebuilds, you can store them in the data directory on OpenShift, specified by the environment variable OPENSHIFT_DATA_DIR.

To check how much disk space you are using and what your quota is, SSH in to your gear and run the command quota -s. If your Java application is memory intensive and struggling with the 512MB of RAM included with small gears, you may want to consider OpenShift Online Silver. It offers medium (1GB RAM) gears and recently also added large (2GB RAM) gears.

If you want to run your Maven builds on Jenkins and then have the app synced to your gear, you can add a Jenkins server on OpenShift and embed Jenkins in your app. There are instructions on how to do that here.

To have your application scale horizontally automatically, add the -s flag when creating your Tomcat (or other) cartridge.

Summary

This blog post looked at the steps involved in moving a Java app from Heroku to OpenShift. I migrated a Spring MVC app running on Tomcat with a PostgreSQL database, but the process would be similar for many other web servers and technologies. The two main things changed in the app source code were the pom.xml (adding an OpenShift profile) and the database config (adding OpenShift environment variables).

The source code for the OpenShift version of the sample app, Petclinic, is available on GitHub with the tag openshift.

Learn more about Java application hosting on OpenShift

What's Next?