Spring Boot应用程序即服务
如何configuration良好的Spring Boot应用程序打包为可执行的jar作为一个服务在Linux系统? 这是推荐的方法,或者我应该转换这个应用程序的战争,并安装到Tomcat?
目前我可以从screen
会话运行Spring启动应用程序,什么是好的,但需要手动启动服务器重新启动后。
我正在寻找的是一般的build议/方向或示例init.d
脚本,如果我的方法与可执行jar是正确的。
以下为springboot 1.3及以上版本的作品:
作为init.d服务
可执行的jar包含通常的启动,停止,重新启动和状态命令。 它也将在通常的/ var / run目录中build立一个PID文件,并默认login通常的/ var / log目录。
你只需要像你这样将你的jar符号链接到/etc/init.d
sudo link -s /var/myapp/myapp.jar /etc/init.d/myapp
要么
sudo ln -s ~/myproject/build/libs/myapp-1.0.jar /etc/init.d/myapp_servicename
之后,你可以照常做
/etc/init.d/myapp start
然后设置一个链接,无论你想要的应用程序启动/停止启动,如果需要的运行级别。
作为一个systemd服务
要运行安装在var / myapp中的Spring Boot应用程序,可以在/etc/systemd/system/myapp.service中添加以下脚本:
[Unit] Description=myapp After=syslog.target [Service] ExecStart=/var/myapp/myapp.jar [Install] WantedBy=multi-user.target
参考
以下是在Linux中将Java应用程序安装为系统服务的最简单方法。
让我们假设你正在使用systemd
(现在任何现代的发行版):
首先,在/etc/systemd/system
创build一个名为例如javaservice.service
的服务文件,内容如下:
[Unit] Description=Java Service [Service] User=nobody # The configuration file application.properties should be here: WorkingDirectory=/data ExecStart=/usr/bin/java -Xmx256m -jar application.jar SuccessExitStatus=143 TimeoutStopSec=10 Restart=on-failure RestartSec=5 [Install] WantedBy=multi-user.target
其次,通知systemd
新的服务文件:
systemctl daemon-reload
并启用它,所以它在启动时运行:
systemctl enable javaservice.service
最终,您可以使用以下命令启动/停止您的新服务:
systemctl start javaservice systemctl stop javaservice systemctl restart javaservice systemctl status javaservice
假设您使用的是systemd
,这是将Java应用程序设置为system-service的最不干扰和干净的方法。
我特别喜欢这个解决scheme的是,你不需要安装和configuration任何其他软件。 发货的systemd
为您完成所有工作,您的服务的行为与其他任何系统服务一样。 现在我在生产中使用它一段时间,在不同的发行版上,它就像你所期望的那样工作。
另一个优点是,通过使用/usr/bin/java
,可以轻松添加诸如-Xmx256m
jvm
参数。
另请阅读官方Spring Boot文档中的systemd
部分: http : //docs.spring.io/spring-boot/docs/current/reference/html/deployment-install.html
你也可以使用supervisord这是一个非常方便的守护进程,可以用来方便地控制服务。 这些服务是由简单的configuration文件定义的,这些文件定义了哪个用户在哪个目录等等下面执行哪些操作,有多less个选项。 supervisord有一个非常简单的语法,所以它是编写SysV init脚本的一个非常好的select。
这里有一个简单的supervisordconfiguration文件,用于你正在运行/控制的程序。 (把它放到/etc/supervisor/conf.d/yourapp.conf中 )
/etc/supervisor/conf.d/yourapp.conf
[program:yourapp] command=/usr/bin/java -jar /path/to/application.jar user=usertorun autostart=true autorestart=true startsecs=10 startretries=3 stdout_logfile=/var/log/yourapp-stdout.log stderr_logfile=/var/log/yourapp-stderr.log
要控制应用程序,您将需要执行supervisorctl ,这将显示一个提示您可以在哪里启动,停止,状态yourapp。
CLI
# sudo supervisorctl yourapp RUNNING pid 123123, uptime 1 day, 15:00:00 supervisor> stop yourapp supervisor> start yourapp
如果supervisord
守护进程已经在运行,并且在不重新启动守护进程的情况下添加了服务的configuration,则只需在supervisorctl
shell中执行reread
和update
命令即可。
这真的给你所有使用SysV Init脚本的灵活性,但易于使用和控制。 看看文档 。
我只是想自己做这个,所以以下就是我在CentOS init.d服务控制器脚本中所处的位置。 到目前为止,它的工作还算不错,但我不是Bash黑客,所以我相信还有改进的空间,所以对改进它的想法是值得欢迎的。
首先,我为每个服务都有一个简短的configuration脚本/data/svcmgmt/conf/my-spring-boot-api.sh
,它设置了环境variables。
#!/bin/bash export JAVA_HOME=/opt/jdk1.8.0_05/jre export APP_HOME=/data/apps/my-spring-boot-api export APP_NAME=my-spring-boot-api export APP_PORT=40001
我正在使用CentOS,为了确保我的服务在服务器重启后启动,我在/etc/init.d/my-spring-boot-api
有一个服务控制脚本:
#!/bin/bash # description: my-spring-boot-api start stop restart # processname: my-spring-boot-api # chkconfig: 234 20 80 . /data/svcmgmt/conf/my-spring-boot-api.sh /data/svcmgmt/bin/spring-boot-service.sh $1 exit 0
正如你所看到的那样,它调用了初始configuration脚本来设置环境variables,然后调用一个共享脚本来重新启动我的所有Spring Boot服务。 这个共享脚本就是可以find它的肉的地方:
#!/bin/bash echo "Service [$APP_NAME] - [$1]" echo " JAVA_HOME=$JAVA_HOME" echo " APP_HOME=$APP_HOME" echo " APP_NAME=$APP_NAME" echo " APP_PORT=$APP_PORT" function start { if pkill -0 -f $APP_NAME.jar > /dev/null 2>&1 then echo "Service [$APP_NAME] is already running. Ignoring startup request." exit 1 fi echo "Starting application..." nohup $JAVA_HOME/bin/java -jar $APP_HOME/$APP_NAME.jar \ --spring.config.location=file:$APP_HOME/config/ \ < /dev/null > $APP_HOME/logs/app.log 2>&1 & } function stop { if ! pkill -0 -f $APP_NAME.jar > /dev/null 2>&1 then echo "Service [$APP_NAME] is not running. Ignoring shutdown request." exit 1 fi # First, we will try to trigger a controlled shutdown using # spring-boot-actuator curl -X POST http://localhost:$APP_PORT/shutdown < /dev/null > /dev/null 2>&1 # Wait until the server process has shut down attempts=0 while pkill -0 -f $APP_NAME.jar > /dev/null 2>&1 do attempts=$[$attempts + 1] if [ $attempts -gt 5 ] then # We have waited too long. Kill it. pkill -f $APP_NAME.jar > /dev/null 2>&1 fi sleep 1s done } case $1 in start) start ;; stop) stop ;; restart) stop start ;; esac exit 0
停止时,将尝试使用Spring Boot Actuator执行受控关机。 但是,如果Actuator没有configuration,或者在合理的时间内closures(我给它5秒,实际上有点短),这个过程将被终止。
另外,脚本还假定运行应用程序的java进程将是stream程细节文本中唯一带有“my-spring-boot-api.jar”的文件。 在我的环境中这是一个安全的假设,意味着我不需要跟踪PID。
如果你想使用Spring Boot 1.2.5和Spring Boot Maven Plugin 1.3.0.M2,下面的解决scheme:
<parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>1.2.5.RELEASE</version> </parent> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> <version>1.3.0.M2</version> <configuration> <executable>true</executable> </configuration> </plugin> </plugins> </build> <pluginRepositories> <pluginRepository> <id>spring-libs-milestones</id> <url>http://repo.spring.io/libs-milestone</url> </pluginRepository> </pluginRepositories>
然后编译为ususal: mvn clean package
,做一个符号链接ln -s /.../myapp.jar /etc/init.d/myapp
,使其成为可执行文件chmod +x /etc/init.d/myapp
并启动它的service myapp start
(与Ubuntu服务器)
我知道这是一个较老的问题,但是我想提出另一种方式,那就是app-maven-plugin 。 以下是我的POM中的相关部分,其中包含许多我们认为有用的附加选项值:
<plugin> <groupId>org.codehaus.mojo</groupId> <artifactId>appassembler-maven-plugin</artifactId> <configuration> <generateRepository>true</generateRepository> <repositoryLayout>flat</repositoryLayout> <useWildcardClassPath>true</useWildcardClassPath> <includeConfigurationDirectoryInClasspath>true</includeConfigurationDirectoryInClasspath> <configurationDirectory>config</configurationDirectory> <target>${project.build.directory}</target> <daemons> <daemon> <id>${installer-target}</id> <mainClass>${mainClass}</mainClass> <commandLineArguments> <commandLineArgument>--spring.profiles.active=dev</commandLineArgument> <commandLineArgument>--logging.config=${rpmInstallLocation}/config/${installer-target}-logback.xml</commandLineArgument> </commandLineArguments> <platforms> <platform>jsw</platform> </platforms> <generatorConfigurations> <generatorConfiguration> <generator>jsw</generator> <includes> <include>linux-x86-64</include> </includes> <configuration> <property> <name>wrapper.logfile</name> <value>logs/${installer-target}-wrapper.log</value> </property> <property> <name>wrapper.logfile.maxsize</name> <value>5m</value> </property> <property> <name>run.as.user.envvar</name> <value>${serviceUser}</value> </property> <property> <name>wrapper.on_exit.default</name> <value>RESTART</value> </property> </configuration> </generatorConfiguration> </generatorConfigurations> <jvmSettings> <initialMemorySize>256M</initialMemorySize> <maxMemorySize>1024M</maxMemorySize> <extraArguments> <extraArgument>-server</extraArgument> </extraArguments> </jvmSettings> </daemon> </daemons> </configuration> <executions> <execution> <id>generate-jsw-scripts</id> <phase>package</phase> <goals> <goal>generate-daemons</goal> </goals> </execution> </executions> </plugin>
在这个问题上,@PbxMan的答案应该让你开始:
在Linux上运行Java应用程序作为服务
编辑:
还有另一种不太好的方式来使用cron重新启动进程:
@reboot user-to-run-under /usr/bin/java -jar /path/to/application.jar
这工作,但给你的应用程序没有很好的启动/停止界面。 你仍然可以简单地kill
它…
我的SysVInit脚本为Centos 6 / RHEL(不理想)。 这个脚本需要ApplicationPidListener 。
/etc/init.d/app
来源
#!/bin/sh # # app Spring Boot Application # # chkconfig: 345 20 80 # description: App Service # ### BEGIN INIT INFO # Provides: App # Required-Start: $local_fs $network # Required-Stop: $local_fs $network # Default-Start: 3 4 5 # Default-Stop: 0 1 2 6 # Short-Description: Application # Description: ### END INIT INFO # Source function library. . /etc/rc.d/init.d/functions # Source networking configuration. . /etc/sysconfig/network exec="/usr/bin/java" prog="app" app_home=/home/$prog/ user=$prog [ -e /etc/sysconfig/$prog ] && . /etc/sysconfig/$prog lockfile=/var/lock/subsys/$prog pid=$app_home/$prog.pid start() { [ -x $exec ] || exit 5 [ -f $config ] || exit 6 # Check that networking is up. [ "$NETWORKING" = "no" ] && exit 1 echo -n $"Starting $prog: " cd $app_home daemon --check $prog --pidfile $pid --user $user $exec $app_args & retval=$? echo [ $retval -eq 0 ] && touch $lockfile return $retval } stop() { echo -n $"Stopping $prog: " killproc -p $pid $prog retval=$? [ $retval -eq 0 ] && rm -f $lockfile return $retval } restart() { stop start } reload() { restart } force_reload() { restart } rh_status() { status -p $pid $prog } rh_status_q() { rh_status >/dev/null 2>&1 } case "$1" in start) rh_status_q && exit 0 $1 ;; stop) rh_status_q || exit 0 $1 ;; restart) $1 ;; reload) rh_status_q || exit 7 $1 ;; force-reload) force_reload ;; status) rh_status ;; condrestart|try-restart) rh_status_q || exit 0 restart ;; *) echo $"Usage: $0 {start|stop|status|restart|condrestart|try-restart|reload|force-reload}" exit 2 esac exit $?
示例configuration文件/etc/sysconfig/app
:
exec=/opt/jdk1.8.0_05/jre/bin/java user=myuser app_home=/home/mysuer/ app_args="-jar app.jar" pid=$app_home/app.pid
我正在尝试使springboot应用程序作为一个“init.d”样式的shell脚本,并在最后使用压缩的java应用程序
通过将这些脚本从/etc/init.d/spring-app编译到/opt/spring-app.jar,然后chmod把jar写成可执行文件,可以使“/etc/init.d/spring-app start “/etc/init.d/spring-app stop”和其他可能的状态工作
据推测,从springboot的init.d风格的脚本看,他们有必要的魔术string(如# Default-Start: 2 3 4 5
)chkconfig将能够将其添加为“服务”
但是我想让它和systemd一起工作
为了使这个工作,我尝试了上述其他答案中的许多收件人,但他们都没有在Centos 7.2上与Springboot 1.3工作。他们大多会启动服务,但无法跟踪的PID
最后,我发现下面的工作对我来说,当/etc/init.d链接也在。 类似于下面的文件应安装为/usr/lib/systemd/system/spring-app.service
[Unit] Description=My loverly application After=syslog.target [Service] Type=forking PIDFile=/var/run/spring-app/spring-app.pid ExecStart=/etc/init.d/spring-app start SuccessExitStatus=143 [Install] WantedBy=multi-user.target
这是一个将可执行jar部署为systemd服务的脚本。
它为服务和.service文件创build一个用户,并将该jar文件放在/ var下,并对权限进行一些基本的locking。
#!/bin/bash # Argument: The jar file to deploy APPSRCPATH=$1 # Argument: application name, no spaces please, used as folder name under /var APPNAME=$2 # Argument: the user to use when running the application, may exist, created if not exists APPUSER=$3 # Help text USAGE=" Usage: sudo $0 <jar-file> <app-name> <runtime-user> If an app with the name <app-name> already exist, it is stopped and deleted. If the <runtime-user> does not already exist, it is created. " # Check that we are root if [ ! "root" = "$(whoami)" ]; then echo "Must be root. Please use eg sudo" echo "$USAGE" exit fi # Check arguments if [ "$#" -ne 3 -o ${#APPSRCPATH} = 0 -o ${#APPNAME} = 0 -o ${#APPUSER} = 0 ]; then echo "Incorrect number of parameters." echo "$USAGE" exit fi if [ ! -f $APPSRCPATH ]; then echo "Can't find jar file $APPSRCPATH" echo "$USAGE" exit fi # Infered values APPFILENAME=$(basename $APPSRCPATH) APPFOLDER=/var/javaapps/$APPNAME APPDESTPATH=$APPFOLDER/$APPFILENAME # Stop the service if it already exist and is running systemctl stop $APPNAME >/dev/null 2>&1 # Create the app folder, deleting any previous content rm -fr $APPFOLDER mkdir -p $APPFOLDER # Create the user if it does not exist if id "$APPUSER" >/dev/null 2>&1; then echo "Using existing user $APPUSER" else adduser --disabled-password --gecos "" $APPUSER echo "Created user $APPUSER" fi # Place app in app folder, setting owner and rights cp $APPSRCPATH $APPDESTPATH chown $APPUSER $APPDESTPATH chmod 500 $APPDESTPATH echo "Added or updated the $APPDESTPATH file" # Create the .service file used by systemd echo " [Unit] Description=$APPNAME After=syslog.target [Service] User=$APPUSER ExecStart=/usr/bin/java -jar $APPDESTPATH SuccessExitStatus=143 [Install] WantedBy=multi-user.target " > /etc/systemd/system/$APPNAME.service echo "Created the /etc/systemd/system/$APPNAME.service file" # Reload the daemon systemctl daemon-reload # Start the deployed app systemctl start $APPNAME systemctl status $APPNAME
例:
我不知道Java应用程序采用“标准”缩小包装的方式,但这绝对是一个好主意(如果您希望从操作系统的保持活动和监视function中获益) 。 它提供了Spring Boot工具支持(maven和gradle)的路线图,但现在你可能不得不推出你自己的。 我现在知道的最好的解决scheme是Foreman ,它有一个声明式的方法和一行命令来打包各种标准OS格式(monit,sys V,暴发户等)的init脚本。 也有证据表明人们用gradle(例如这里 )设置了东西。
你在使用Maven吗? 那么你应该试试AppAssembler插件:
应用程序组装者插件是一个Maven插件,用于生成启动Java应用程序的脚本。 …所有工件(依赖项+工程中的工件)被添加到生成的bin脚本中的类path中。
支持的平台:
Unix的变种
Windows NT(不支持Windows 9x)
Java服务包装器(JSW)
请参阅: http : //mojo.codehaus.org/appassembler/appassembler-maven-plugin/index.html