Spring Boot 六 - 部署Spring Boot应用
在部署应用时,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 | Uploading acloudyspringtime... OK |
祝贺你!应用现在已经运行了!
很容易验证已部署的应用的状态:
1 | $ cf apps |
一旦Cloud Foundry认为你的应用已经部署,你应该能够访问给定的URI,在这个例子中是http://acloudyspringtime.cfapps.io/
。
绑定到服务
默认情况下,关于运行应用程序和服务连接信息的元数据将作为环境变量(例如:$VCAP_SERVICES
)暴露给应用程序。这个架构决策是由于Cloud Foundry的polyglot(任何语言和平台都可以作为buildpack支持)的特性;进程范围的环境变量是语言无关的。
环境变量并不总是适合最简单的API,所以Spring Boot 自动提取它们并将数据转化为可以通过Spring的Environment
抽象来访问的属性。
1 |
|
所有的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。
基本的模型是:
- 确保Java和你的构建工具是远程安装的,例如使用
pre_build
hook(Java和Maven是默认安装的,而Gradle则不是)。 - 使用
build
hook来构建你的jar(使用Maven 或Gradle )。1
2
3#!/bin/bash
cd $OPENSHIFT_REPO_DIR
mvn package -s .openshift/settings.xml -DskipTests=true - 添加一个调用
java-jar ...
的start
hook1
2
3#!/bin/bash
cd $OPENSHIFT_REPO_DIR
nohup java -jar target/*.jar --server.port=${OPENSHIFT_DIY_PORT} --server.address=${OPENSHIFT_DIY_IP} & - 使用一个
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 - 在
application.properties
中由平台提供的环境变量的服务绑定,例如:在他们的网站上有一个关于在OpenShift上运行Gradle的博客,让你使用gradle来运行你的应用。1
2
3spring.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}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 | service: default |
例如,你可以使用Maven插件来部署该应用程序,只需将项目ID添加到构建配置中即可:
1 | <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.d
或systemd
进行注册。这使得在常见的生产环境中安装和管理Spring Boot应用程序非常容易。
完全可执行的jar通过在文件的前面嵌入一个额外的脚本来工作。目前,有些工具不接受这种格式,因此你可能不总是能够使用这种技术。例如,
jar -xf
可能会在提取出jar或完全可执行的war时失败。如果你打算直接执行它,而不是用java -jar
运行它,或者将它部署到servlet容器中,那么建议你仅使你的jar或者war是完全可执行的。
要使用Maven来创建一个“完全可执行”jar,请使用以下插件配置:
1 | <plugin> |
使用Gradle 的配置为:
1 | springBoot { |
然后,你可以通过键入./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
以支持标准的start
,stop
,restart
和status
命令。
该脚本支持以下功能:
- 以拥有该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 | $ chmod 400 your-app.conf |
安装为systemd 服务
Systemd是System V init系统的后继者,现在被许多现代Linux发行版使用。尽管你可以继续使用systemd
的init.d
脚本,但也可以使用systemd
‘service’ 脚本启动Spring Boot应用程序。
假设你在/var/myapp
中安装了一个Spring Boot应用程序,要将Spring Boot应用程序安装为系统服务,请创建使用以下示例的名为myapp.service
的脚本,并将其放在/etc/systemd/system
目录中:
1 | [Unit] |
请记住更改应用程序的
Description
,User
和ExecStart
字段。
请注意,
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_FOLDER
和LOG_FILENAME
变量只在init.d
服务时可用。systemd
时使用’service’脚本来使用等价的自定义变量。查看更多细节。
除了JARFILE
和 APP_NAME
,上面的设置可用使用一个.conf
文件。这个文件在jar文件的相同目录并且有相同的名称,但是后缀为.conf
而不是.jar
。例如,名为/var/myapp/myapp.jar
的jar将会使用名为/var/myapp/myapp.conf
的配置文件。
myapp.conf:
1 | JAVA_OPTS=-Xmx1024M |
如果你不想它与jar在同一个目录,你可以使用
CONF_FOLDER
环境变量来自定义配置文件的位置。
阅读保护init.d服务的指南来学习适当地保护配置文件。
微软Windows服务
Spring Boot 应用使用winsw
来作为Windows服务启动。
有个单独维护的示例逐步介绍了如何为Spring Boot 应用程序创建Windows服务。
延伸阅读
查看Cloud Foundry,Heroku,OpenShift和Boxfuse网站,了解有关PaaS可提供的各种功能的更多信息。这些只是四个最受欢迎的Java PaaS提供商,因为Spring Boot 非常适合基于云的部署,你可以自由考虑其他提供商。
下一节将介绍Spring Boot CLI; 或者你可以前往阅读关于构建工具插件。