在部署应用时,Spring Boot 灵活的打包选项提供了大量的选择。你可以轻松地将Spring Boot 应用部署到各种云平台、容器镜像(例如Docker)或虚拟/真实机器上。

本节将介绍一些更常见的部署场景。

部署到云上

Spring Boot的可执行jar对于大多数流行的云PaaS(平台即服务)提供者都是直接可用的。这些提供者往往要求你“自带容器”;它们管理应用程序进程程(不是专门针对Java应用程序的),因此它们需要一些中间层,以使应用程序适应云的运行过程的概念。

两家受欢迎的云服务提供商,Heroku和Cloud Foundry,采用了一种“buildpack”的方式。buildpack将你部署的代码封装在启动应用程序所需的任何东西中:它可能是一个JDK和对java的调用,它可能是一个嵌入式web服务器,或者它可能是一个成熟的应用程序服务器。buildpack是可插拔的,但理想情况下,你应该能够尽可能少地定制它。这减少了不受你控制的功能的占用。它最小化了开发和生产环境之间的差异。

理想情况下,你的应用程序,就像一个Spring Boot 的可执行jar一样,拥有它需要在其中运行的所有东西。

在本节中,我们将看看在“入门”部分中开发的简单应用程序在云中运行所需的功能。

Cloud Foundry

如果没有指定其他buildpack ,则Cloud Foundry提供了默认的buildpack 。Cloud Foundry Java buildpack为Spring应用程序提供了极好的支持,包括Spring Boot。你可以部署独立的可执行jar应用程序,以及传统的.war包应用程序。

构建应用程序(使用例如mvn clean package)并安装cf命令行工具后,只需像下面这样使用cf push命令部署应用程序,将其中的路径替换为你自己编译好的.jar路径。在推送应用之前,一定要使用你的cf命令行客户端登录

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

有关更多选项,请参阅cf push文档。 如果在同一目录中存在Cloud Foundry manifest.yml文件,则会参考这个文件。

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

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
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

祝贺你!应用现在已经运行了!

很容易验证已部署的应用的状态:

1
2
3
4
5
6
7
8
$ 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/

绑定到服务

默认情况下,关于运行应用程序和服务连接信息的元数据将作为环境变量(例如:$VCAP_SERVICES)暴露给应用程序。这个架构决策是由于Cloud Foundry的polyglot(任何语言和平台都可以作为buildpack支持)的特性;进程范围的环境变量是语言无关的。

环境变量并不总是适合最简单的API,所以Spring Boot 自动提取它们并将数据转化为可以通过Spring的Environment抽象来访问的属性。

1
2
3
4
5
6
7
8
9
10
11
12
13
@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。

Spring Cloud Connectors项目更适合配置DataSource等任务。 Spring Boot包括自动配置支持和spring-boot-starter-cloud-connectors starter。

Heroku

OpenShift

OpenShift是RedHat public(和enterprise)PaaS解决方案。与Heroku一样,它通过运行由git提交的脚本运行,因此,只要Java运行时可用(这是你在OpenShift中可以要求的标准功能),你可以用任何方式编写Spring Boot 应用程序的启动。要做到这一点,你可以在你的仓库的.openshift/action_hooks下使用DIY Cartridge 和hook。

基本的模型是:

  1. 确保Java和你的构建工具是远程安装的,例如使用pre_build hook(Java和Maven是默认安装的,而Gradle则不是)。
  2. 使用build hook来构建你的jar(使用Maven 或Gradle )。
    1
    2
    3
    #!/bin/bash
    cd $OPENSHIFT_REPO_DIR
    mvn package -s .openshift/settings.xml -DskipTests=true
  3. 添加一个调用java-jar ...start hook
    1
    2
    3
    #!/bin/bash
    cd $OPENSHIFT_REPO_DIR
    nohup java -jar target/*.jar --server.port=${OPENSHIFT_DIY_PORT} --server.address=${OPENSHIFT_DIY_IP} &
  4. 使用一个stop hook(因为start应该干净地返回???)。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    #!/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
  5. application.properties中由平台提供的环境变量的服务绑定,例如:
    1
    2
    3
    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来运行你的应用。

    Amazon Web Services (AWS)

AWS Elastic Beanstalk

总结

Boxfuse and Amazon Web Services

Google Cloud

Google Cloud有几个选项可以用来启动Spring Boot应用。最容易开始使用的可能是App Engine,但是你也可以找到在容器引擎的容器中运行Spring Boot,或者在虚拟机上使用计算引擎运行Spring Boot的方法。

要在App Engine中运行,你可以首先在UI中创建一个项目,它为你设置一个惟一的标识符和HTTP路由。将Java应用程序添加到项目中,然后将其清空,然后使用Google Cloud SDK将你的Spring Boot应用从命令行或CI构建中push到该项目。

App Engine需要你创建一个app.yaml文件来描述你的应用所需要的资源。通常你把它放在src/min/appengine,它看起来是这样的:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
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

例如,你可以使用Maven插件来部署该应用程序,只需将项目ID添加到构建配置中即可:

1
2
3
4
5
6
7
8
<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(如果你需要先进行身份验证,构建将失败)。

Google App Engine Classic与Servlet 2.5 API绑定住了,因此你不能在没有修改的情况下部署Spring应用。请参阅本指南的Servlet 2.5部分

安装Spring Boot应用

除了使用java -jar运行Spring Boot应用程序外,还可以为Unix系统创建完整的可执行的应用程序。完整可执行的jar可以像任何其他可执行二进制文件一样执行,也可以使用init.dsystemd进行注册。这使得在常见的生产环境中安装和管理Spring Boot应用程序非常容易。

完全可执行的jar通过在文件的前面嵌入一个额外的脚本来工作。目前,有些工具不接受这种格式,因此你可能不总是能够使用这种技术。例如,jar -xf可能会在提取出jar或完全可执行的war时失败。如果你打算直接执行它,而不是用java -jar运行它,或者将它部署到servlet容器中,那么建议你仅使你的jar或者war是完全可执行的。

要使用Maven来创建一个“完全可执行”jar,请使用以下插件配置:

1
2
3
4
5
6
7
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<executable>true</executable>
</configuration>
</plugin>

使用Gradle 的配置为:

1
2
3
springBoot {
executable = true
}

然后,你可以通过键入./my-application.jar(其中my-application是你的artifact的名称)来运行应用程序。 包含jar的目录将被用作应用程序的工作目录。

支持的操作系统

默认的脚本支付大多数Linux分发版本并且已经在CentOS和Ubuntu上测试通过。其他的平台比如OS X和FreeBSD,需要 使用自定义的embeddedLaunchScript

Unix/Linux服务

Spring Boot应用可以方便地作为Unix/Linux服务,既可以使用init.d又可以使用systemd

安装为init.d服务(System V)

如果你配置了Spring Boot的Maven或者Gradle插件来生成完全可执行的jar,并且你没有使用自定义的embeddedLaunchScript,那么你的应用可以当作init.d服务来使用。简单地将jar链接到init.d以支持标准的startstoprestartstatus命令。
该脚本支持以下功能:

  • 以拥有该jar文件的用户身份启动服务
  • 使用/var/run/<appname>/<appname>.pid跟踪应用程序的PID
  • 将控制台日志写入/var/log/<appname>.log

假设你有一个Spring Boot 应用程序安装在/var/ myapp中,安装Spring Boot 应用程序作为init.d服务只需创建一个符号链接:

1
$ sudo ln -s /var/myapp/myapp.jar /etc/init.d/myapp

一旦安装之后 ,你可以按照通常的方式启动和停止服务。例如,在基于Debian的系统上:

1
$ service myapp start

如果你的应用程序无法启动,请检查写入/var/log/<appname>.log的日志文件是否有错误。

你还可以将应用程序标记为使用标准操作系统工具自动启动。例如,在Debian上:

1
$ update-rc.d myapp defaults <priority>
保护init.d服务

以下是关于如何保护作为init.d服务运行的Spring Boot应用程序的一组指导。 它并不是为了强化应用程序和运行环境而应该做的一切的详尽列表。

当以root身份执行时,在使用root来启动init.d服务的情况下,可执行脚本将默认以拥有该jar文件的用户身份运行应用程序。你不应该以root身份运行Spring Boot 应用程序,因此应用程序的jar文件不应该由root拥有。 相反,创建一个特定的用户来运行应用程序,并使用chown将其作为jar文件的所有者。 例如:

1
$ chown bootapp:bootapp your-app.jar

在这种情况下,可执行脚本默认将以bootapp用户运行应用程序。

为了减少应用程序的用户帐户遭到入侵的机会,你应该考虑防止其使用登录shell。例如,将帐户的shell设置为/usr/sbin/nologin

你还应该采取措施来阻止修改应用程序的jar文件。首先,配置其权限,使其不能被写入,并且只能由其所有者读取或执行:

1
$ chmod 500 your-app.jar

其次,如果你的应用程序或运行它的帐户遭到破坏,你还应采取措施减少损失。如果攻击者确实获得访问权限,他们可以使jar文件可写,并更改其内容。防止这种情况的一种方法是使用chattr使其变得不可变:

1
$ sudo chattr +i your-app.jar

这将阻止任何用户(包括root)修改该jar。

如果root用于控制应用程序的服务,则使用一个用于自定义启动的.conf文件。root用户将会读取和验证.conf文件。它也应该保证相应的安全性。使用chmod使文件只能由所有者读取,并使用chown来使root作为其拥有者。

1
2
$ chmod 400 your-app.conf
$ sudo chown root:root your-app.conf

安装为systemd 服务

Systemd是System V init系统的后继者,现在被许多现代Linux发行版使用。尽管你可以继续使用systemdinit.d脚本,但也可以使用systemd ‘service’ 脚本启动Spring Boot应用程序。

假设你在/var/myapp中安装了一个Spring Boot应用程序,要将Spring Boot应用程序安装为系统服务,请创建使用以下示例的名为myapp.service的脚本,并将其放在/etc/systemd/system目录中:

1
2
3
4
5
6
7
8
9
10
11
[Unit]
Description=myapp
After=syslog.target

[Service]
User=myapp
ExecStart=/var/myapp/myapp.jar
SuccessExitStatus=143

[Install]
WantedBy=multi-user.target

请记住更改应用程序的DescriptionUserExecStart字段。

请注意,ExecStart字段不声明脚本操作命令,这意味着默认情况下使用run命令。

请注意,与运行init.d服务不同,运行应用程序,PID文件和控制台日志文件的用户由systemd本身管理,因此必须使用’service’脚本中的适当字段进行配置。 有关详细信息,请参阅服务单元配置手册页

要标记应用程序在系统启动时自动启动,请使用以下命令:

1
$ systemctl enable myapp.service

请参考man systemctl了解更多详情。

自定义启动脚本

由Maven或Gradle插件编写的默认嵌入式启动脚本可以通过多种方式进行自定义。对于大多数人来说,使用默认脚本以及一些自定义设置通常就足够了。如果你发现无法自定义需要的内容,则可以随时使用embeddedLaunchScript选项来完全编写自己的文件。

写脚本时自定义脚本

在将脚本写入jar文件时,自定义开始脚本的元素通常是有意义的。例如,init.d脚本可以提供一个“描述”,因为你知道这一点(它不会改变),你可以在生成jar时提供它。

要自定义写入的元素,请使用Spring Boot Maven 或Gradle 插件的embeddedLaunchScriptProperties选项。

默认脚本支持以下属性替换:

名称 描述
mode 脚本模式。 默认为auto
initInfoProvides “INIT INFO”的Provides部分。默认为Gradle的spring-boot-application和Maven的 ${project.artifactId}
initInfoRequiredStart “INIT INFO”的Required-Start部分。默认为$remote_fs $syslog $network
initInfoRequiredStop “INIT INFO”的Required-Stop部分。默认为$remote_fs $syslog $network
initInfoDefaultStart “INIT INFO”的Default-Start部分。默认为2 3 4 5
initInfoDefaultStop “INIT INFO”的Default-Start部分。默认为0 1 6
initInfoShortDescription “INIT INFO”的Short-Description部分。默认为Gradle的Spring Boot Application和Maven的${project.name}
initInfoDescription “INIT INFO”的Description部分。默认为Gradle的Spring Boot Application和Maven的${project.description}(失败时使用${project.name})。
initInfoChkconfig “INIT INFO”的chkconfig部分。默认为2345 99 01
confFolder CONF_FOLDER的默认值。 默认为包含该jar的文件夹。
logFolder LOG_FOLDER的默认值。 仅适用于init.d服务。
logFilename LOG_FILENAME的默认值。 仅适用于init.d服务。
pidFolder PID_FOLDER的默认值。 仅适用于init.d服务。
pidFilename PID_FOLDER中pid文件名称的默认值。 仅适用于init.d服务。
useStartStopDaemon 如果start-stop-daemon命令可用,应该用于控制进程。默认为true
stopWaitTime STOP_WAIT_TIME的默认值。仅适用于init.d服务。默认为60秒。
在运行时自定义脚本

对于在写入jar需要定制的脚本项目,你可以使用环境变量或配置文件

默认脚本支持以下环境属性:

变量 描述
MODE 操作模式。默认取决于构建jar的方式,但是通常为auto(这意味着它会尝试猜测它是否是初始化脚本,通过检查它是否是init.d目录中的符号链接)。你可以明确地设置它为service,这样就可以使用`stop
USE_START_STOP_DAEMON 如果start-stop-daemon命令可用时,它应该用来控制进程。默认为true
PID_FOLDER pid 文件夹的根名称(默认为/var/run)。
LOG_FOLDER 放置日志文件的目录名称(默认为/var/log)。
CONF_FOLDER 读取.conf文件的文件夹名称(默认与jar-file相同的文件夹)。
LOG_FILENAME LOG_FOLDER中的日志文件名称(默认为<appname>.log)。
APP_NAME 应用的名称。如果jar从符号链接运行,这个脚本将会猜测应用的名称,如果不是符号链接,或者你想要明确设置应用的名称,这个将会非常有用。
RUN_ARGS 传递给程序(Spring Boot 应用)的参数。
JAVA_HOME 默认通过使用PATH来发现java可执行程序,但是如果在$JAVA_HOME/bin/java有一个可执行文件,你可以明确地设置它。
JAVA_OPTS 传递给JVM启动时的选项。
JARFILE 在使用脚本来启动不是实际内嵌的jar时,指定jar文件的明确路径。
DEBUG 当不为空时将设置shell进程的-x标志,可以很容易地查看脚本的逻辑。
STOP_WAIT_TIME 在停止应用时在强制退出之前等待的时间秒数(默认为60)。

PID_FOLDER,LOG_FOLDERLOG_FILENAME变量只在init.d服务时可用。systemd时使用’service’脚本来使用等价的自定义变量。查看更多细节

除了JARFILEAPP_NAME,上面的设置可用使用一个.conf文件。这个文件在jar文件的相同目录并且有相同的名称,但是后缀为.conf而不是.jar。例如,名为/var/myapp/myapp.jar的jar将会使用名为/var/myapp/myapp.conf的配置文件。

myapp.conf:

1
2
JAVA_OPTS=-Xmx1024M
LOG_FOLDER=/custom/log/folder

如果你不想它与jar在同一个目录,你可以使用CONF_FOLDER环境变量来自定义配置文件的位置。

阅读保护init.d服务的指南来学习适当地保护配置文件。

微软Windows服务

Spring Boot 应用使用winsw来作为Windows服务启动。

有个单独维护的示例逐步介绍了如何为Spring Boot 应用程序创建Windows服务。

延伸阅读

查看Cloud FoundryHerokuOpenShiftBoxfuse网站,了解有关PaaS可提供的各种功能的更多信息。这些只是四个最受欢迎的Java PaaS提供商,因为Spring Boot 非常适合基于云的部署,你可以自由考虑其他提供商。

下一节将介绍Spring Boot CLI; 或者你可以前往阅读关于构建工具插件