58. 部署到云端

Spring Boot 的可执行 jar 已为大多数流行的云 PaaS(平台即服务)提供商提供。这些提供程序往往要求您“自带容器”;他们 Management 应用程序流程(不是专门用于 Java 应用程序),因此他们需要一些中间层,以使您的应用程序适应运行过程的云概念。

两家受欢迎的云提供商,Heroku 和 Cloud Foundry,采用了“构建包”方法。 buildpack 将您部署的代码包装在启动应用程序所需的任何内容中:它可能是 JDK 和对java的调用,可能是嵌入式 Web 服务器,也可能是成熟的应用程序服务器。一个 buildpack 是可插入的,但是理想情况下,您应该能够通过尽可能少的自定义来获得它。这减少了您无法控制的功能的占用空间。它使开发和生产环境之间的差异最小化。

理想情况下,您的应用程序像 Spring Boot 可执行 jar 一样,具有打包运行所需的一切。

在本部分中,我们将研究如何在“Starter”部分中启动我们开发的简单应用程序并在云中运行。

58.1 Cloud Foundry

如果未指定其他构建包,Cloud Foundry 将提供默认的构建包。 Cloud Foundry Java buildpack对 Spring 应用程序(包括 Spring Boot)提供了出色的支持。您可以部署独立的可执行 jar 应用程序以及传统的.war打包应用程序。

构建了应用程序(例如,使用mvn clean package)和安装了 cf 命令行工具之后,只需使用cf push命令(如下所示)部署应用程序,即可替换已编译的.jar的路径。在推送应用程序之前,请确保拥有使用 cf 命令行 Client 端登录

$ cf push acloudyspringtime -p target/demo-0.0.1-SNAPSHOT.jar

有关更多选项,请参见cf 推文档。如果同一目录中存在 Cloud Foundry manifest.yml文件,将对其进行查询。

Note

在这里,我们用acloudyspringtime代替您给cf作为应用程序名称的任何值。

此时cf将开始上传您的应用程序:

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

恭喜你!该应用程序现已上线!

然后可以很容易地验证已部署应用程序的状态:

$ cf apps
Getting applications in ...
OK

name                 requested state   instances   memory   disk   urls
...
acloudyspringtime    started           1/1         512M     1G     acloudyspringtime.cfapps.io
...

一旦 Cloud Foundry 确认已部署了您的应用程序,您就可以使用给定的 URI(在本例中为http://acloudyspringtime.cfapps.io/)访问该应用程序。

58.1.1 绑定到服务

默认情况下,有关正在运行的应用程序以及服务连接信息的元数据作为环境变量(例如$VCAP_SERVICES)公开给应用程序。该架构决定是由于 Cloud Foundry 的多语言(可以将任何语言和平台支持为 buildpack)所决定的;过程范围的环境变量与语言无关。

环境变量并不总是使用最简单的 API,因此 Spring Boot 会自动提取它们并将数据展平为可以通过 Spring 的Environment抽象访问的属性:

@Component
class MyBean implements EnvironmentAware {

    private String instanceId;

    @Override
    public void setEnvironment(Environment environment) {
        this.instanceId = environment.getProperty("vcap.application.instance_id");
    }

    // ...

}

所有 Cloud Foundry 属性均以vcap作为前缀。您可以使用 vcap 属性来访问应用程序信息(例如,应用程序的公共 URL)和服务信息(例如,数据库凭据)。有关完整的详细信息,请参见CloudFoundryVcapEnvironmentPostProcessor Javadoc。

Tip

Spring Cloud 连接器项目更适合诸如配置数据源之类的任务。 Spring Boot 包括自动配置支持和spring-boot-starter-cloud-connectors启动器。

58.2 Heroku

Heroku 是另一个流行的 PaaS 平台。要自定义 Heroku 构建,请提供Procfile,它提供部署应用程序所需的内容。 Heroku 为 Java 应用程序分配一个port,然后确保路由到外部 URI 起作用。

您必须配置您的应用程序以侦听正确的端口。这是我们的 StarterREST 应用程序的Procfile

web: java -Dserver.port=$PORT -jar target/demo-0.0.1-SNAPSHOT.jar

Spring Boot 使-D参数作为可从 Spring Environment实例访问的属性。 server.port配置属性被馈送到嵌入式 Tomcat,Jetty 或 Undertow 实例,然后在启动时使用它。 $PORT环境变量是由 Heroku PaaS 分配给我们的。

Heroku 默认情况下将使用 Java 1.8. 只要您将 Maven 或 Gradle 构建设置为使用相同版本(Maven 用户可以使用 java.version 属性),就可以了。如果要使用 JDK 1.7,请在pom.xmlProcfile旁边创建一个名为system.properties的新文件。在此文件中添加以下内容:

java.runtime.version=1.7

这应该是您需要的一切。 Heroku 部署最常见的工作流程是将代码git push投入生产。

$ 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 [emailprotected]:agile-sierra-1405.git
 * [new branch]      master -> master

您的应用程序现在应该已经在 Heroku 上启动并运行了。

58.3 OpenShift

OpenShift是 RedHat 公共(和企业)PaaS 解决方案。像 Heroku 一样,它通过运行 git commit 触发的脚本来工作,因此只要 Java 运行时可用,您就可以用几乎任何您喜欢的方式编写脚本来启动 Spring Boot 应用程序(这是您可以要求的标准功能) OpenShift)。为此,您可以使用DIY Cartridge并在存储库中.openshift/action_hooks下的钩子:

基本模型是:

  • 确保 Java 和您的构建工具是远程安装的,例如使用pre_build钩子(默认情况下已安装 Java 和 Maven,未安装 Gradle)

  • 使用build钩子来构建 jar(使用 Maven 或 Gradle),例如

#!/bin/bash
cd $OPENSHIFT_REPO_DIR
mvn package -s .openshift/settings.xml -DskipTests=true
  • 添加一个调用java -jar …start钩子
#!/bin/bash
cd $OPENSHIFT_REPO_DIR
nohup java -jar target/*.jar --server.port=${OPENSHIFT_DIY_PORT} --server.address=${OPENSHIFT_DIY_IP} &
  • 使用stop钩子(因为应该可以干净地返回起点),例如
#!/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
  • 将平台提供的环境变量中的服务绑定嵌入到您的application.properties中,例如
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}

他们的网站在 OpenShift 中运行 Gradle上有一个博客,可让您开始构建 Gradle 以运行该应用。

58.4 Amazon Web Services(AWS)

Amazon Web Services 提供了多种安装基于 Spring Boot 的应用程序的方法,这些方法既可以作为传统的 Web 应用程序(war),也可以作为具有嵌入式 Web 服务器的可执行 jar 文件安装。选项包括:

  • AWS Elastic Beanstalk

  • AWS Code Deploy

  • AWS OPS 作品

  • AWS 云形成

  • AWS 容器注册表

每个都有不同的功能和定价模型,在这里我们将仅描述最简单的选项:AWS Elastic Beanstalk。

58.4.1 AWS Elastic Beanstalk

如官方Elastic Beanstalk Java 指南所述,部署 Java 应用程序有两个主要选项:您可以使用“ Tomcat 平台”或“ Java SE 平台”。

使用 Tomcat 平台

该选项适用于产生 war 文件的 Spring Boot 项目。无需任何特殊配置,只需遵循官方指南即可。

使用 Java SE 平台

该选项适用于产生一个 jar 文件并运行嵌入式 Web 容器的 Spring Boot 项目。 Elastic Beanstalk 环境在端口 80 上运行 nginx 实例来代理在端口 5000 上运行的实际应用程序。要对其进行配置,请将以下内容添加到application.properties

server.port=5000

Best practices

上传二进制文件而不是源文件

默认情况下,Elastic Beanstalk 上载源并将其在 AWS 中编译。要上传二进制文件,请将以下内容添加到您的.elasticbeanstalk/config.yml文件中:

deploy:
    artifact: target/demo-0.0.1-SNAPSHOT.jar
通过设置环境类型来降低成本

默认情况下,Elastic Beanstalk 环境是负载平衡的。为了避免这种情况,负载均衡器具有成本考虑因素,请按照在 Amazon 文档中的说明将环境类型设置为“单个实例”。也可以使用 CLI 使用以下命令创建单实例环境:

eb create -s

58.4.2 Summary

这是使用 AWS 的最简单方法之一,但还有更多内容要讲,例如:如何将 Elastic Beanstalk 集成到任何 CI/CD 工具中,如何使用 Elastic Beanstalk maven 插件代替 CLI,等等。 blog详细介绍了这些主题。

58.5 Boxfuse 和 Amazon Web Services

Boxfuse通过将您的 Spring Boot 可执行 jar 或 war 变成一个最小的 VM 映像来工作,该映像可以在 VirtualBox 或 AWS 上不变地部署。 Boxfuse 与 Spring Boot 进行了深度集成,并将使用 Spring Boot 配置文件中的信息自动配置端口和运行状况检查 URL。 Boxfuse 在生成的图像以及它提供的所有资源(实例,安全组,弹性负载均衡器等)中均利用此信息。

创建Boxfuse account并将其连接到您的 AWS 账户并安装了最新版本的 Boxfuse Client 之后,您可以按以下方式将 Spring Boot 应用程序部署到 AWS(确保该应用程序由 Maven 或 Gradle 首先使用以下命令构建:例如mvn clean package):

$ boxfuse run myapp-1.0.jar -env=prod

有关更多选项,请参见boxfuse 运行文档。如果当前目录中存在boxfuse.com/docs/commandline/#configuration [+102+]文件,将对其进行查询。

Tip

默认情况下,Boxfuse 将在启动时激活名为boxfuse的 Spring 配置文件,并且如果您的可执行 jar 或 war 包含boxfuse.com/docs/payloads/springboot.html#configuration [+107+]文件,则 Boxfuse 将基于其包含的属性来进行配置。

此时,boxfuse将为您的应用程序创建一个映像,将其上传,然后在 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/

您的应用程序现在应该已启动并在 AWS 上运行。

他们的网站上在 EC2 上部署 Spring Boot 应用Boxfuse Spring Boot 集成的文档上都有一个博客,可以帮助您开始使用 Maven 构建程序来运行该应用程序。

58.6 Google Cloud

Google Cloud 提供了几种可用于启动 Spring Boot 应用程序的选项。最容易上手的可能是 App Engine,但是您也可以找到在具有 Container Engine 的容器中或在使用 Compute Engine 的虚拟机上运行 Spring Boot 的方法。

要在 App Engine 中运行,您可以先在 UI 中创建一个项目,该项目将为您以及 HTTP 路由设置唯一的标识符。将 Java 应用程序添加到项目中,然后将其保留为空,然后使用Google Cloud SDK将您的 Spring Boot 应用程序从命令行或 CI 构建推入该插槽。

App Engine 需要您创建一个app.yaml文件来描述您的应用程序所需的资源。通常,您将其放在src/main/appengine中,它看起来像这样:

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

您可以通过将项目 ID 添加到构建配置中,例如,使用 Maven 插件来部署应用程序:

<plugin>
    <groupId>com.google.cloud.tools</groupId>
    <artifactId>appengine-maven-plugin</artifactId>
    <version>1.3.0</version>
    <configuration>
        <project>myproject</project>
    </configuration>
</plugin>

然后使用mvn appengine:deploy进行部署(如果您需要先进行身份验证,则构建将失败)。

Note

Google App Engine 经典版与 Servlet 2.5 API 绑定在一起,因此如果不做一些修改,您将无法在其中部署 Spring Application。请参阅本指南的Servlet 2.5 部分