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_hooks
:
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.
Amazon Web Services offers multiple ways to install Spring Boot based applications, either as traditional web applications (war) or as executable jar files with an embedded web server. Options include :
Each has different features and pricing model, here we will describe only the simplest option : AWS Elastic Beanstalk.
As described in the official Elastic Beanstalk Java guide, there are two main options to deploy a Java application; You can either use the “Tomcat Platform” or the “Java SE platform”.
This option applies to Spring Boot projects producing a war file. There is no any special configuration required, just follow the official guide.
This option applies to Spring Boot projects producing a jar file and running an embedded
web container. Elastic Beanstalk environments run an nginx instance on port 80 to proxy
the actual application, running on port 5000. To configure it, add the following to your
application.properties
:
server.port=5000
By default Elastic Beanstalk uploads sources and compiles them in AWS. To upload the
binaries instead, add the following to your .elasticbeanstalk/config.yml
file:
deploy: artifact: target/demo-0.0.1-SNAPSHOT.jar
By default an Elastic Beanstalk environment is load balanced. The load balancer has a cost perspective, to avoid it, set the environment type to “Single instance” as described in the Amazon documentation. Single instance environments can be created using the CLI as well using the following command:
eb create -s
This is one of the easiest ways to get to AWS, but there are more things to cover, e.g.: how to integrate Elastic Beanstalk into any CI / CD tool, using the Elastic Beanstalk maven plugin instead of the CLI, etc. There is a blog covering these topics more in detail.
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 Cloud has several options that could be used to launch Spring Boot applications. The easiest to get started with is probably App Engine, but you could also find ways to run Spring Boot in a container with Container Engine, or on a virtual machine using Compute Engine.
To run in App Engine you can create a project in the UI first, which sets up a unique identifier for you and also HTTP routes. Add a Java app to the project and leave it empty, then use the Google Cloud SDK to push your Spring Boot app into that slot from the command line or CI build.
App Engine needs you to create an app.yaml
file to describe the resources your app
requires. Normally you put this in src/main/appengine
, and it looks something like this:
service: default runtime: java env: flex runtime_config: jdk: openjdk8 handlers: - url: /.* script: this field is required, but ignored manual_scaling: instances: 1 health_check: enable_health_check: False env_variables: ENCRYPT_KEY: your_encryption_key_here
You can deploy the app, for example, with a Maven plugin by simply adding the project ID to the build configuration:
<plugin> <groupId>com.google.cloud.tools</groupId> <artifactId>appengine-maven-plugin</artifactId> <version>1.3.0</version> <configuration> <project>myproject</project> </configuration> </plugin>
Then deploy with mvn appengine:deploy
(if you need to authenticate first the build will
fail).
Note | |
---|---|
Google App Engine Classic 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. |