Spring Boot’s executable jars are ready-made for most popular cloud PaaS (platform-as-a-service) providers. These providers tend to require that you “bring your own container”; they manage application processes (not Java applications specifically), so they need some intermediary layer that adapts your application to the cloud’s notion of a running process.
Two popular cloud providers, Heroku and Cloud Foundry, employ a “buildpack” approach.
The buildpack wraps your deployed code in whatever is needed to start your
application: it might be a JDK and a call to java
, it might be an embedded web server,
or it might be a full-fledged application server. A buildpack is pluggable, but ideally
you should be able to get by with as few customizations to it as possible.
This reduces the footprint of functionality that is not under your control. It minimizes
divergence between development and production environments.
Ideally, your application, like a Spring Boot executable jar, has everything that it needs to run packaged within it.
In this section we’ll look at what it takes to get the simple application that we developed in the “Getting Started” section up and running in the Cloud.
Cloud Foundry provides default buildpacks that come into play if no other buildpack is
specified. The Cloud Foundry Java buildpack
has excellent support for Spring applications, including Spring Boot. You can deploy
stand-alone executable jar applications, as well as traditional .war
packaged
applications.
Once you’ve built your application (using, for example, mvn clean package
) and
installed the cf
command line tool, simply deploy your application using the cf push
command as follows,
substituting the path to your compiled .jar
. Be sure to have
logged in with your
cf
command line client before pushing an application.
$ cf push acloudyspringtime -p target/demo-0.0.1-SNAPSHOT.jar
See the cf push
documentation for more options. If there is a Cloud Foundry
manifest.yml
file present in the same directory, it will be consulted.
Note | |
---|---|
Here we are substituting |
At this point cf
will start uploading your application:
Uploading acloudyspringtime... OK Preparing to start acloudyspringtime... OK -----> Downloaded app package (8.9M) -----> Java Buildpack source: system -----> Downloading Open JDK 1.7.0_51 from .../x86_64/openjdk-1.7.0_51.tar.gz (1.8s) Expanding Open JDK to .java-buildpack/open_jdk (1.2s) -----> Downloading Spring Auto Reconfiguration from 0.8.7 .../auto-reconfiguration-0.8.7.jar (0.1s) -----> Uploading droplet (44M) Checking status of app 'acloudyspringtime'... 0 of 1 instances running (1 starting) ... 0 of 1 instances running (1 down) ... 0 of 1 instances running (1 starting) ... 1 of 1 instances running (1 running) App started
Congratulations! The application is now live!
It’s easy to then verify the status of the deployed application:
$ cf apps Getting applications in ... OK name requested state instances memory disk urls ... acloudyspringtime started 1/1 512M 1G acloudyspringtime.cfapps.io ...
Once Cloud Foundry acknowledges that your application has been deployed, you should be
able to hit the application at the URI given, in this case
http://acloudyspringtime.cfapps.io/
.
By default, metadata about the running application as well as service connection
information is exposed to the application as environment variables (for example:
$VCAP_SERVICES
). This architecture decision is due to Cloud Foundry’s polyglot
(any language and platform can be supported as a buildpack) nature; process-scoped
environment variables are language agnostic.
Environment variables don’t always make for the easiest API so Spring Boot automatically
extracts them and flattens the data into properties that can be accessed through
Spring’s Environment
abstraction:
@Component class MyBean implements EnvironmentAware { private String instanceId; @Override public void setEnvironment(Environment environment) { this.instanceId = environment.getProperty("vcap.application.instance_id"); } // ... }
All Cloud Foundry properties are prefixed with vcap
. You can use vcap properties to
access application information (such as the public URL of the application) and service
information (such as database credentials). See CloudFoundryVcapEnvironmentPostProcessor
Javadoc for complete details.
Tip | |
---|---|
The Spring Cloud Connectors project
is a better fit for tasks such as configuring a DataSource. Spring Boot includes
auto-configuration support and a |
Heroku is another popular PaaS platform. To customize Heroku builds, you provide a
Procfile
, which provides the incantation required to deploy an application. Heroku
assigns a port
for the Java application to use and then ensures that routing to the
external URI works.
You must configure your application to listen on the correct port. Here’s the Procfile
for our starter REST application:
web: java -Dserver.port=$PORT -jar target/demo-0.0.1-SNAPSHOT.jar
Spring Boot makes -D
arguments available as properties accessible from a Spring
Environment
instance. The server.port
configuration property is fed to the embedded
Tomcat, Jetty or Undertow instance which then uses it when it starts up. The $PORT
environment variable is assigned to us by the Heroku PaaS.
Heroku by default will use Java 1.8. This is fine as long as your Maven or Gradle build
is set to use the same version (Maven users can use the java.version property). If you
want to use JDK 1.7, create a new file adjacent to your pom.xml
and Procfile
,
called system.properties
. In this file add the following:
java.runtime.version=1.7
This should be everything you need. The most common workflow for Heroku deployments is to
git push
the code to production.
$ git push heroku master Initializing repository, done. Counting objects: 95, done. Delta compression using up to 8 threads. Compressing objects: 100% (78/78), done. Writing objects: 100% (95/95), 8.66 MiB | 606.00 KiB/s, done. Total 95 (delta 31), reused 0 (delta 0) -----> Java app detected -----> Installing OpenJDK 1.8... done -----> Installing Maven 3.3.1... done -----> Installing settings.xml... done -----> Executing: mvn -B -DskipTests=true clean install [INFO] Scanning for projects... Downloading: http://repo.spring.io/... Downloaded: http://repo.spring.io/... (818 B at 1.8 KB/sec) .... Downloaded: http://s3pository.heroku.com/jvm/... (152 KB at 595.3 KB/sec) [INFO] Installing /tmp/build_0c35a5d2-a067-4abc-a232-14b1fb7a8229/target/... [INFO] Installing /tmp/build_0c35a5d2-a067-4abc-a232-14b1fb7a8229/pom.xml ... [INFO] ------------------------------------------------------------------------ [INFO] BUILD SUCCESS [INFO] ------------------------------------------------------------------------ [INFO] Total time: 59.358s [INFO] Finished at: Fri Mar 07 07:28:25 UTC 2014 [INFO] Final Memory: 20M/493M [INFO] ------------------------------------------------------------------------ -----> Discovering process types Procfile declares types -> web -----> Compressing... done, 70.4MB -----> Launching... done, v6 http://agile-sierra-1405.herokuapp.com/ deployed to Heroku To [email protected]:agile-sierra-1405.git * [new branch] master -> master
Your application should now be up and running on Heroku.
OpenShift is the RedHat public (and enterprise) PaaS solution.
Like Heroku, it works by running scripts triggered by git commits, so you can script
the launching of a Spring Boot application in pretty much any way you like as long as the
Java runtime is available (which is a standard feature you can ask for at OpenShift).
To do this you can use the
DIY Cartridge and hooks in your
repository under .openshift/action_scripts
:
The basic model is to:
pre_build
hook
(Java and Maven are installed by default, Gradle is not)Use a build
hook to build your jar (using Maven or Gradle), e.g.
#!/bin/bash cd $OPENSHIFT_REPO_DIR mvn package -s .openshift/settings.xml -DskipTests=true
Add a start
hook that calls java -jar …
#!/bin/bash cd $OPENSHIFT_REPO_DIR nohup java -jar target/*.jar --server.port=${OPENSHIFT_DIY_PORT} --server.address=${OPENSHIFT_DIY_IP} &
Use a stop
hook (since the start is supposed to return cleanly), e.g.
#!/bin/bash source $OPENSHIFT_CARTRIDGE_SDK_BASH PID=$(ps -ef | grep java.*\.jar | grep -v grep | awk '{ print $2 }') if [ -z "$PID" ] then client_result "Application is already stopped" else kill $PID fi
Embed service bindings from environment variables provided by the platform
in your application.properties
, e.g.
spring.datasource.url: jdbc:mysql://${OPENSHIFT_MYSQL_DB_HOST}:${OPENSHIFT_MYSQL_DB_PORT}/${OPENSHIFT_APP_NAME} spring.datasource.username: ${OPENSHIFT_MYSQL_DB_USERNAME} spring.datasource.password: ${OPENSHIFT_MYSQL_DB_PASSWORD}
There’s a blog on running Gradle in OpenShift on their website that will get you started with a gradle build to run the app.
Boxfuse works by turning your Spring Boot executable jar or war into a minimal VM image that can be deployed unchanged either on VirtualBox or on AWS. Boxfuse comes with deep integration for Spring Boot and will use the information from your Spring Boot configuration file to automatically configure ports and health check URLs. Boxfuse leverages this information both for the images it produces as well as for all the resources it provisions (instances, security groups, elastic load balancers, etc).
Once you have created a Boxfuse account, connected it to your
AWS account, and installed the latest version of the Boxfuse Client, you can deploy your
Spring Boot application to AWS as follows (ensure the application has been built by
Maven or Gradle first using, for example, mvn clean package
):
$ boxfuse run myapp-1.0.jar -env=prod
See the boxfuse run
documentation for
more options. If there is a boxfuse.com/docs/commandline/#configuration
[boxfuse.conf
] file present in the current directory, it will be consulted.
Tip | |
---|---|
By default Boxfuse will activate a Spring profile named |
At this point boxfuse
will create an image for your application, upload it,
and then configure and start the necessary resources on AWS:
Fusing Image for myapp-1.0.jar ... Image fused in 00:06.838s (53937 K) -> axelfontaine/myapp:1.0 Creating axelfontaine/myapp ... Pushing axelfontaine/myapp:1.0 ... Verifying axelfontaine/myapp:1.0 ... Creating Elastic IP ... Mapping myapp-axelfontaine.boxfuse.io to 52.28.233.167 ... Waiting for AWS to create an AMI for axelfontaine/myapp:1.0 in eu-central-1 (this may take up to 50 seconds) ... AMI created in 00:23.557s -> ami-d23f38cf Creating security group boxfuse-sg_axelfontaine/myapp:1.0 ... Launching t2.micro instance of axelfontaine/myapp:1.0 (ami-d23f38cf) in eu-central-1 ... Instance launched in 00:30.306s -> i-92ef9f53 Waiting for AWS to boot Instance i-92ef9f53 and Payload to start at http://52.28.235.61/ ... Payload started in 00:29.266s -> http://52.28.235.61/ Remapping Elastic IP 52.28.233.167 to i-92ef9f53 ... Waiting 15s for AWS to complete Elastic IP Zero Downtime transition ... Deployment completed successfully. axelfontaine/myapp:1.0 is up and running at http://myapp-axelfontaine.boxfuse.io/
Your application should now be up and running on AWS.
There’s a blog on deploying Spring Boot apps on EC2 as well as documentation for the Boxfuse Spring Boot integration on their website that will get you started with a Maven build to run the app.
Google App Engine is tied to the Servlet 2.5 API, so you can’t deploy a Spring Application there without some modifications. See the Servlet 2.5 section of this guide.