419 lines
20 KiB
Plaintext
419 lines
20 KiB
Plaintext
[[deployment.cloud]]
|
|
== Deploying to the Cloud
|
|
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 an 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`, an embedded web server, or 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 look at what it takes to get the <<getting-started#getting-started.first-application, application that we developed>> in the "`Getting Started`" section up and running in the Cloud.
|
|
|
|
|
|
|
|
[[deployment.cloud.cloud-foundry]]
|
|
=== Cloud Foundry
|
|
Cloud Foundry provides default buildpacks that come into play if no other buildpack is specified.
|
|
The Cloud Foundry https://github.com/cloudfoundry/java-buildpack[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 have built your application (by using, for example, `mvn clean package`) and have https://docs.cloudfoundry.org/cf-cli/install-go-cli.html[installed the `cf` command line tool], deploy your application by using the `cf push` command, substituting the path to your compiled `.jar`.
|
|
Be sure to have https://docs.cloudfoundry.org/cf-cli/getting-started.html#login[logged in with your `cf` command line client] before pushing an application.
|
|
The following line shows using the `cf push` command to deploy an application:
|
|
|
|
[source,shell,indent=0,subs="verbatim"]
|
|
----
|
|
$ cf push acloudyspringtime -p target/demo-0.0.1-SNAPSHOT.jar
|
|
----
|
|
|
|
NOTE: In the preceding example, we substitute `acloudyspringtime` for whatever value you give `cf` as the name of your application.
|
|
|
|
See the https://docs.cloudfoundry.org/cf-cli/getting-started.html#push[`cf push` documentation] for more options.
|
|
If there is a Cloud Foundry https://docs.cloudfoundry.org/devguide/deploy-apps/manifest.html[`manifest.yml`] file present in the same directory, it is considered.
|
|
|
|
At this point, `cf` starts uploading your application, producing output similar to the following example:
|
|
|
|
[indent=0,subs="verbatim,quotes"]
|
|
----
|
|
Uploading acloudyspringtime... *OK*
|
|
Preparing to start acloudyspringtime... *OK*
|
|
-----> Downloaded app package (*8.9M*)
|
|
-----> Java Buildpack Version: v3.12 (offline) | https://github.com/cloudfoundry/java-buildpack.git#6f25b7e
|
|
-----> Downloading Open Jdk JRE 1.8.0_121 from https://java-buildpack.cloudfoundry.org/openjdk/trusty/x86_64/openjdk-1.8.0_121.tar.gz (found in cache)
|
|
Expanding Open Jdk JRE to .java-buildpack/open_jdk_jre (1.6s)
|
|
-----> Downloading Open JDK Like Memory Calculator 2.0.2_RELEASE from https://java-buildpack.cloudfoundry.org/memory-calculator/trusty/x86_64/memory-calculator-2.0.2_RELEASE.tar.gz (found in cache)
|
|
Memory Settings: -Xss349K -Xmx681574K -XX:MaxMetaspaceSize=104857K -Xms681574K -XX:MetaspaceSize=104857K
|
|
-----> Downloading Container Certificate Trust Store 1.0.0_RELEASE from https://java-buildpack.cloudfoundry.org/container-certificate-trust-store/container-certificate-trust-store-1.0.0_RELEASE.jar (found in cache)
|
|
Adding certificates to .java-buildpack/container_certificate_trust_store/truststore.jks (0.6s)
|
|
-----> Downloading Spring Auto Reconfiguration 1.10.0_RELEASE from https://java-buildpack.cloudfoundry.org/auto-reconfiguration/auto-reconfiguration-1.10.0_RELEASE.jar (found in cache)
|
|
Checking status of app 'acloudyspringtime'...
|
|
0 of 1 instances running (1 starting)
|
|
...
|
|
0 of 1 instances running (1 starting)
|
|
...
|
|
0 of 1 instances running (1 starting)
|
|
...
|
|
1 of 1 instances running (1 running)
|
|
|
|
App started
|
|
----
|
|
|
|
Congratulations! The application is now live!
|
|
|
|
Once your application is live, you can verify the status of the deployed application by using the `cf apps` command, as shown in the following example:
|
|
|
|
[source,shell,indent=0,subs="verbatim"]
|
|
----
|
|
$ 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 find the application at the URI given.
|
|
In the preceding example, you could find it at `\https://acloudyspringtime.cfapps.io/`.
|
|
|
|
|
|
|
|
[[deployment.cloud.cloud-foundry.binding-to-services]]
|
|
==== Binding to Services
|
|
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 do not 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, as shown in the following example:
|
|
|
|
[source,java,indent=0,subs="verbatim"]
|
|
----
|
|
include::{docs-java}/deployment/cloud/cloudfoundry/bindingtoservices/MyBean.java[]
|
|
----
|
|
|
|
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 the {spring-boot-module-api}/cloud/CloudFoundryVcapEnvironmentPostProcessor.html['`CloudFoundryVcapEnvironmentPostProcessor`'] Javadoc for complete details.
|
|
|
|
TIP: The https://github.com/pivotal-cf/java-cfenv/[Java CFEnv] project is a better fit for tasks such as configuring a DataSource.
|
|
|
|
|
|
|
|
[[deployment.cloud.kubernetes]]
|
|
=== Kubernetes
|
|
Spring Boot auto-detects Kubernetes deployment environments by checking the environment for `"*_SERVICE_HOST"` and `"*_SERVICE_PORT"` variables.
|
|
You can override this detection with the configprop:spring.main.cloud-platform[] configuration property.
|
|
|
|
Spring Boot helps you to <<features#features.spring-application.application-availability,manage the state of your application>> and export it with <<actuator#actuator.endpoints.kubernetes-probes, HTTP Kubernetes Probes using Actuator>>.
|
|
|
|
|
|
|
|
[[deployment.cloud.kubernetes.container-lifecycle]]
|
|
==== Kubernetes Container Lifecycle
|
|
When Kubernetes deletes an application instance, the shutdown process involves several subsystems concurrently: shutdown hooks, unregistering the service, removing the instance from the load-balancer...
|
|
Because this shutdown processing happens in parallel (and due to the nature of distributed systems), there is a window during which traffic can be routed to a pod that has also begun its shutdown processing.
|
|
|
|
You can configure a sleep execution in a preStop handler to avoid requests being routed to a pod that has already begun shutting down.
|
|
This sleep should be long enough for new requests to stop being routed to the pod and its duration will vary from deployment to deployment.
|
|
The preStop handler can be configured by using the PodSpec in the pod's configuration file as follows:
|
|
|
|
[source,yml,indent=0,subs="verbatim"]
|
|
----
|
|
spec:
|
|
containers:
|
|
- name: example-container
|
|
image: example-image
|
|
lifecycle:
|
|
preStop:
|
|
exec:
|
|
command: ["sh", "-c", "sleep 10"]
|
|
----
|
|
|
|
Once the pre-stop hook has completed, SIGTERM will be sent to the container and <<web#web.graceful-shutdown,graceful shutdown>> will begin, allowing any remaining in-flight requests to complete.
|
|
|
|
NOTE: When Kubernetes sends a SIGTERM signal to the pod, it waits for a specified time called the termination grace period (the default for which is 30 seconds).
|
|
If the containers are still running after the grace period, they are sent the SIGKILL signal and forcibly removed.
|
|
If the pod takes longer than 30 seconds to shut down, which could be because you have increased configprop:spring.lifecycle.timeout-per-shutdown-phase[], make sure to increase the termination grace period by setting the `terminationGracePeriodSeconds` option in the Pod YAML.
|
|
|
|
|
|
|
|
[[deployment.cloud.heroku]]
|
|
=== Heroku
|
|
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.
|
|
The following example shows the `Procfile` for our starter REST application:
|
|
|
|
[indent=0]
|
|
----
|
|
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 the port when it starts up.
|
|
The `$PORT` environment variable is assigned to us by the Heroku PaaS.
|
|
|
|
This should be everything you need.
|
|
The most common deployment workflow for Heroku deployments is to `git push` the code to production, as shown in the following example:
|
|
|
|
[source,shell,indent=0,subs="verbatim"]
|
|
----
|
|
$ git push heroku main
|
|
----
|
|
|
|
Which will result in the following:
|
|
|
|
[indent=0,subs="verbatim,quotes"]
|
|
----
|
|
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: https://repo.spring.io/...
|
|
Downloaded: https://repo.spring.io/... (818 B at 1.8 KB/sec)
|
|
....
|
|
Downloaded: https://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
|
|
https://agile-sierra-1405.herokuapp.com/ *deployed to Heroku*
|
|
|
|
To git@heroku.com:agile-sierra-1405.git
|
|
* [new branch] main -> main
|
|
----
|
|
|
|
Your application should now be up and running on Heroku.
|
|
For more details, refer to https://devcenter.heroku.com/articles/deploying-spring-boot-apps-to-heroku[Deploying Spring Boot Applications to Heroku].
|
|
|
|
|
|
|
|
[[deployment.cloud.openshift]]
|
|
=== OpenShift
|
|
https://www.openshift.com/[OpenShift] has many resources describing how to deploy Spring Boot applications, including:
|
|
|
|
* https://blog.openshift.com/using-openshift-enterprise-grade-spring-boot-deployments/[Using the S2I builder]
|
|
* https://access.redhat.com/documentation/en-us/reference_architectures/2017/html-single/spring_boot_microservices_on_red_hat_openshift_container_platform_3/[Architecture guide]
|
|
* https://blog.openshift.com/using-spring-boot-on-openshift/[Running as a traditional web application on Wildfly]
|
|
* https://blog.openshift.com/openshift-commons-briefing-96-cloud-native-applications-spring-rhoar/[OpenShift Commons Briefing]
|
|
|
|
|
|
|
|
[[deployment.cloud.aws]]
|
|
=== Amazon Web Services (AWS)
|
|
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.
|
|
The options include:
|
|
|
|
* AWS Elastic Beanstalk
|
|
* AWS Code Deploy
|
|
* AWS OPS Works
|
|
* AWS Cloud Formation
|
|
* AWS Container Registry
|
|
|
|
Each has different features and pricing models.
|
|
In this document, we describe to approach using AWS Elastic Beanstalk.
|
|
|
|
|
|
|
|
[[deployment.cloud.aws.beanstalk]]
|
|
==== AWS Elastic Beanstalk
|
|
As described in the official https://docs.aws.amazon.com/elasticbeanstalk/latest/dg/create_deploy_Java.html[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`".
|
|
|
|
|
|
|
|
[[deployment.cloud.aws.beanstalk.tomcat-platform]]
|
|
===== Using the Tomcat Platform
|
|
This option applies to Spring Boot projects that produce a war file.
|
|
No special configuration is required.
|
|
You need only follow the official guide.
|
|
|
|
|
|
|
|
[[deployment.cloud.aws.beanstalk.java-se-platform]]
|
|
===== Using the Java SE Platform
|
|
This option applies to Spring Boot projects that produce a jar file and run 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 line to your `application.properties` file:
|
|
|
|
[indent=0]
|
|
----
|
|
server.port=5000
|
|
----
|
|
|
|
|
|
[TIP]
|
|
.Upload binaries instead of sources
|
|
====
|
|
By default, Elastic Beanstalk uploads sources and compiles them in AWS.
|
|
However, it is best to upload the binaries instead.
|
|
To do so, add lines similar to the following to your `.elasticbeanstalk/config.yml` file:
|
|
|
|
[source,xml,indent=0,subs="verbatim"]
|
|
----
|
|
deploy:
|
|
artifact: target/demo-0.0.1-SNAPSHOT.jar
|
|
----
|
|
====
|
|
|
|
[TIP]
|
|
.Reduce costs by setting the environment type
|
|
====
|
|
By default an Elastic Beanstalk environment is load balanced.
|
|
The load balancer has a significant cost.
|
|
To avoid that cost, set the environment type to "`Single instance`", as described in https://docs.aws.amazon.com/elasticbeanstalk/latest/dg/environments-create-wizard.html#environments-create-wizard-capacity[the Amazon documentation].
|
|
You can also create single instance environments by using the CLI and the following command:
|
|
|
|
[indent=0]
|
|
----
|
|
eb create -s
|
|
----
|
|
====
|
|
|
|
|
|
|
|
[[deployment.cloud.aws.summary]]
|
|
==== Summary
|
|
This is one of the easiest ways to get to AWS, but there are more things to cover, such as how to integrate Elastic Beanstalk into any CI / CD tool, use the Elastic Beanstalk Maven plugin instead of the CLI, and others.
|
|
There is a https://exampledriven.wordpress.com/2017/01/09/spring-boot-aws-elastic-beanstalk-example/[blog post] covering these topics more in detail.
|
|
|
|
|
|
|
|
[[deployment.cloud.boxfuse]]
|
|
=== Boxfuse and Amazon Web Services
|
|
https://boxfuse.com/[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 uses 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, and so on).
|
|
|
|
Once you have created a https://console.boxfuse.com[Boxfuse account], connected it to your AWS account, installed the latest version of the Boxfuse Client, and ensured that the application has been built by Maven or Gradle (by using, for example, `mvn clean package`), you can deploy your Spring Boot application to AWS with a command similar to the following:
|
|
|
|
[source,shell,indent=0,subs="verbatim"]
|
|
----
|
|
$ boxfuse run myapp-1.0.jar -env=prod
|
|
----
|
|
|
|
See the https://boxfuse.com/docs/commandline/run.html[`boxfuse run` documentation] for more options.
|
|
If there is a https://boxfuse.com/docs/commandline/#configuration[`boxfuse.conf`] file present in the current directory, it is considered.
|
|
|
|
TIP: By default, Boxfuse activates a Spring profile named `boxfuse` on startup.
|
|
If your executable jar or war contains an https://boxfuse.com/docs/payloads/springboot.html#configuration[`application-boxfuse.properties`] file, Boxfuse bases its configuration on the properties it contains.
|
|
|
|
At this point, `boxfuse` creates an image for your application, uploads it, and configures and starts the necessary resources on AWS, resulting in output similar to the following example:
|
|
|
|
[indent=0,subs="verbatim"]
|
|
----
|
|
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 https://52.28.235.61/ ...
|
|
Payload started in 00:29.266s -> https://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 https://myapp-axelfontaine.boxfuse.io/
|
|
----
|
|
|
|
Your application should now be up and running on AWS.
|
|
|
|
See the blog post on https://boxfuse.com/blog/spring-boot-ec2.html[deploying Spring Boot apps on EC2] as well as the https://boxfuse.com/docs/payloads/springboot.html[documentation for the Boxfuse Spring Boot integration] to get started with a Maven build to run the app.
|
|
|
|
|
|
|
|
[[deployment.cloud.azure]]
|
|
=== Azure
|
|
This https://spring.io/guides/gs/spring-boot-for-azure/[Getting Started guide] walks you through deploying your Spring Boot application to either https://azure.microsoft.com/en-us/services/spring-cloud/[Azure Spring Cloud] or https://docs.microsoft.com/en-us/azure/app-service/overview[Azure App Service].
|
|
|
|
|
|
|
|
[[deployment.cloud.google]]
|
|
=== Google Cloud
|
|
Google Cloud has several options that can 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 with 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 sets up HTTP routes.
|
|
Add a Java app to the project and leave it empty and then use the https://cloud.google.com/sdk/install[Google Cloud SDK] to push your Spring Boot app into that slot from the command line or CI build.
|
|
|
|
App Engine Standard requires you to use WAR packaging.
|
|
Follow https://github.com/GoogleCloudPlatform/java-docs-samples/tree/master/appengine-java8/springboot-helloworld/README.md[these steps] to deploy App Engine Standard application to Google Cloud.
|
|
|
|
Alternatively, App Engine Flex requires you to create an `app.yaml` file to describe the resources your app requires.
|
|
Normally, you put this file in `src/main/appengine`, and it should resemble the following file:
|
|
|
|
[source,yaml,indent=0,subs="verbatim"]
|
|
----
|
|
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 adding the project ID to the build configuration, as shown in the following example:
|
|
|
|
[source,xml,indent=0,subs="verbatim"]
|
|
----
|
|
<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 fails).
|