如何在Linux系统中将Spring Boot应用程序打包为可执行jar as a Service ?这是推荐的方法吗,还是应该将这个应用程序转换为war并将其安装到Tomcat中?

目前,我可以从屏幕会话运行Spring引导应用程序,这很好,但需要在服务器重新启动后手动启动。

我正在寻找的是一般的建议/方向或样本init。D脚本,如果我的方法与可执行jar是适当的。


当前回答

下面是一个脚本,它将可执行jar部署为systemd服务。

它为服务和.service文件创建一个用户,并将jar文件放在/var下,并对特权进行一些基本的锁定。

#!/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 e.g. 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

例子:

其他回答

在systemd单元文件中,您可以通过目录或EnvironmentFile设置环境变量。我建议这样做,因为这似乎是最小的摩擦。

示例单元文件

$ cat /etc/systemd/system/hello-world.service
[Unit]
Description=Hello World Service
After=systend-user-sessions.service

[Service]
EnvironmentFile=/etc/sysconfig/hello-world
Type=simple
ExecStart=/usr/bin/java ... hello-world.jar

然后在/etc/sysconfig/hello-world下设置一个文件,其中包含Spring Boot变量的大写名称。例如,一个名为server的变量。port将遵循SERVER_PORT形式作为环境变量:

$ cat /etc/sysconfig/hello-world
SERVER_PORT=8081

这里所利用的机制是Spring Boot应用程序将获取属性列表,然后转换它们,将所有内容都改为大写,并将点替换为下划线。一旦Spring Boot应用程序完成了这个过程,它就会寻找匹配的环境变量,并相应地使用找到的环境变量。

在这篇题为:如何通过环境变量在其名称中设置带有下划线的Spring Boot属性的SO Q&A中强调了更多细节?

参考文献

第四部分。弹簧引导功能- 24。外部化配置

我试图使springboot应用程序呈现为“init”。D”风格的shell脚本与压缩Java应用程序钉在最后

通过符号链接这些脚本从/etc/init.D /spring-app到/opt/spring-app.jar,并chmod jar使其可执行。D /spring-app启动/etc/init。D /spring-app stop”和其他可能的状态工作

假设是init。来自springboot的d风格脚本看起来他们有必要的魔法字符串(像# 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

在这个问题中,@PbxMan的回答可以让你开始:

在Linux上运行Java应用程序作为服务

编辑:

还有另一种不太好的方式在重启时启动进程,使用cron:

@reboot user-to-run-under /usr/bin/java -jar /path/to/application.jar

这是可行的,但不能为应用程序提供良好的启动/停止界面。你仍然可以简单地杀死它……

我知道这是一个老问题,但我想提出另一种方法,即appassembler-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>

我最终为WAR/JAR布局做了systemd服务

我调用java -jar是因为它更灵活。我还试着把ExecStart=spring-mvc。战争,但即使是可执行的,我得到'Exec格式错误'

无论如何,现在systemd出现在所有发行版上,并提供了一个很好的解决方案来重定向日志(syserr是重要的,当你的服务甚至没有启动log4j文件位置将为空:))。

cat /etc/systemd/system/spring-mvc.service 
[Unit]
Description=Spring MVC Java Service

[Service]
User=spring-mvc
# The configuration file application.properties should be here:
WorkingDirectory=/usr/local/spring-mvc


# Run ExecStartPre with root-permissions
PermissionsStartOnly=true

ExecStartPre=-/bin/mkdir -p /var/log/spring-mvc


ExecStartPre=/bin/chown -R spring-mvc:syslog /var/log/spring-mvc
ExecStartPre=/bin/chmod -R 775 /var/log/spring-mvc



#https://www.freedesktop.org/software/systemd/man/systemd.service.html#ExecStart=
ExecStart=/usr/bin/java \
        -Dlog4j.configurationFile=log4j2-spring.xml \
        -DLog4jContextSelector=org.apache.logging.log4j.core.async.AsyncLoggerContextSelector \
        -Dspring.profiles.active=dev \
        -Denvironment-type=dev \
        -XX:+UseConcMarkSweepGC \
        -XX:CMSInitiatingOccupancyFraction=80 \
        -XX:NewSize=756m \
        -XX:MetaspaceSize=256m \
        -Dsun.net.inetaddr.ttl=5 \
        -Xloggc:/var/log/spring-mvc/gc.log \
        -verbose:gc \
        -verbosegc \
        -XX:+DisableExplicitGC \
        -XX:+PrintGCDetails \
        -XX:+PrintGCDateStamps \
        -XX:+PreserveFramePointer \
        -XX:+StartAttachListener \
        -Xms1024m \
        -Xmx1024m \
        -XX:+HeapDumpOnOutOfMemoryError \
        -jar spring-mvc.war

SuccessExitStatus=143
StandardOutput=journal
StandardError=journal


KillSignal=SIGINT
TimeoutStopSec=20
Restart=always
RestartSec=5
StartLimitInterval=0
StartLimitBurst=10

LimitNOFILE=500000
LimitNPROC=500000

#https://www.freedesktop.org/software/systemd/man/systemd.exec.html#LimitCPU=
#LimitCPU=, LimitFSIZE=, LimitDATA=, LimitSTACK=, LimitCORE=, LimitRSS=, LimitNOFILE=, LimitAS=, LimitNPROC=, LimitMEMLOCK=, LimitLOCKS=, LimitSIGPENDING=, LimitMSGQUEUE=, LimitNICE=, LimitRTPRIO=, LimitRTTIME=¶

SyslogIdentifier=spring-mvc

[Install]
WantedBy=multi-user.target


# https://www.freedesktop.org/software/systemd/man/journalctl.html
#check logs --- journalctl -u spring-mvc -f -o cat

Rsyslog -将应用程序的syslog输入重定向到特定的文件夹/文件

cat /etc/rsyslog.d/30-spring-mvc.conf 
if $programname == 'spring-mvc' then /var/log/spring-mvc/spring-mvc.log
& stop

logrotate

cat /etc/logrotate.d/spring-mvc.conf 
/var/log/spring-mvc/spring-mvc.log
{
    daily
    rotate 30
    maxage 30
    copytruncate
    missingok
    notifempty
    compress
    dateext
    dateformat _%Y-%m-%d_%H-%M
    delaycompress
    create 644 spring-mvc syslog
    su spring-mvc syslog
}

logrotate gc

cat /etc/logrotate.d/spring-mvc-gc.conf 
/var/log/spring-mvc/gc.log
{
    daily
    rotate 30
    maxage 30
    copytruncate
    missingok
    notifempty
    compress
    dateext
    dateformat _%Y-%m-%d_%H-%M
    delaycompress
    create 644 spring-mvc syslog
    su spring-mvc syslog
}