如何在生产环境中运行Spring Boot可执行jar文件?

122

Spring Boot推荐使用可执行的jar文件来部署应用程序,并且该文件包含了Tomcat。

您可以通过简单的命令 java -jar myapp.jar 来启动它。

现在,我想将这个jar文件部署到我的EC2 Linux服务器上,我是否遗漏了某些步骤或者必须创建一个 init 脚本来正确地以守护进程的形式启动应用程序?

如果我简单地调用java -jar,应用程序会在我退出时停止。

虽然我可以在screen或nohup中启动它,但那并不是很优雅,而且在服务器重启后,我需要手动登录并重新启动该进程。

所以,基于Spring Boot框架,是否有已经准备好的解决方案可以实现这个任务呢?


3
nohup/screen(不太规范的方法),init/systemd/upstart(正规的方法) - user180100
@RC 是的,我知道这一点,就像我提到的那样,/sbin/init与/etc/init.d中的自定义脚本可以完成工作,但是每个人都应该构建自己的脚本来管理守护进程(启动、停止、重启、状态)吗?感觉这个解决方案缺少了什么。 - Cleber Goncalves
如果你觉得Spring Boot(顺便说一下,这是一个非常“新鲜”的项目)中缺少了什么,请联系负责团队并提出进化建议。 - user180100
如果您生成了一个war档案,您可以使用发行版上的Tomcat版本,该版本将具有可立即使用的init脚本。另一方面,如果您使用可执行的jar方法,则必须自己编写自定义的init脚本。不确定这是否属于boot领域,但显然缺少这个功能,这有点奇怪,因此我在询问时可能会忽略某些内容。我会联系他们的。 - Cleber Goncalves
1
点击此处查看现有讨论和想法。 - Dave Syer
显示剩余4条评论
9个回答

106
请注意,自从Spring Boot 1.3.0.M1版本以来,您可以使用Maven和Gradle构建完全可执行的jar包。
对于Maven,请在您的pom.xml文件中包含以下内容:
<plugin>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-maven-plugin</artifactId>
    <configuration>
        <executable>true</executable>
    </configuration>
</plugin>

在Gradle中,将以下代码片段添加到您的build.gradle文件中:
springBoot {
    executable = true
}

完全可执行的jar文件在文件的前面包含了一个额外的脚本,这样你就可以将你的Spring Boot jar文件链接到init.d或使用一个systemd脚本。 init.d示例:
$ln -s /var/yourapp/yourapp.jar /etc/init.d/yourapp

这样你就可以像这样启动、停止和重新启动你的应用程序:
$/etc/init.d/yourapp start|stop|restart

或者使用一个 `systemd` 脚本:
[Unit]
Description=yourapp
After=syslog.target

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

[Install]
WantedBy=multi-user.target

更多信息请参考以下链接:


4
为了能够启动/停止/重启您的应用程序,您可能还需要执行$ chmod +x /etc/init.d/yourapp命令。 - Rohith Nandakumar
您必须始终授予服务用户读取和执行jar文件的权限。您还需要配置服务器的默认Java和Java环境变量以使其正常工作。 - micaro
这个不行!我已经尝试过了,但是出现了以下错误:无法启动MyApp.service:找不到MyApp.service单元。 - Martijn Hiemstra
1
kotlin 中如何使用 springBoot { executable = true } 语法? - nayan dhabarde
1
@nayandhabarde 我的Kotlin基于Gradle的配置有tasks.withType<BootJar> { launchScript() },我执行./gradlew bootJar。 - Viktor Born

13

到目前为止,在生产环境中运行Spring Boot应用程序的最简单和可靠的方法是使用Docker。如果需要使用多个连接的服务,请使用Docker Compose、Docker Swarm或Kubernetes。

这是官方的Spring Boot Docker指南中的一个简单的Dockerfile,可以帮助您入门:

FROM openjdk:8-jdk-alpine
RUN addgroup -S spring && adduser -S spring -G spring
USER spring:spring
ARG JAR_FILE=target/*.jar
COPY ${JAR_FILE} app.jar
ENTRYPOINT ["java","-jar","/app.jar"]

使用Jib是构建Docker镜像的更佳方法,它是由Google维护的针对Java应用程序的开源工具。 Jib不需要Dockerfile,您只需使用Maven(官方快速入门在此)或Gradle(官方快速入门在此)来调用它。

以下是作为守护程序运行容器的示例命令行:

docker run \
  -d --restart=always \
  -e "SPRING_PROFILES_ACTIVE=prod" \
  -p 8080:8080 \
  prefix/imagename

4
在没有服务的Windows操作系统中。 start.bat
@ECHO OFF
call run.bat start

stop.bat:

@ECHO OFF
call run.bat stop

run.bat

@ECHO OFF
IF "%1"=="start" (
    ECHO start myapp
    start "myapp" java -jar -Dspring.profiles.active=staging myapp.jar
) ELSE IF "%1"=="stop" (
    ECHO stop myapp
    TASKKILL /FI "WINDOWTITLE eq myapp"
) ELSE (
    ECHO please, use "run.bat start" or "run.bat stop"
)
pause

3

我的Spring Boot应用程序有两个初始化器。一个用于开发环境,另一个用于生产环境。对于开发环境,我使用以下的main方法:

@SpringBootApplication
public class MyAppInitializer {

    public static void main(String[] args) {
        SpringApplication.run(MyAppInitializer .class, args);
    }

}

我的生产环境初始化器扩展了SpringBootServletInitializer,代码如下:

@SpringBootApplication
public class MyAppInitializerServlet extends SpringBootServletInitializer{
    private static final Logger log = Logger
            .getLogger(SpringBootServletInitializer.class);
    @Override
    protected SpringApplicationBuilder configure(
            SpringApplicationBuilder builder) {
        log.trace("Initializing the application");
        return builder.sources(MyAppInitializerServlet .class);
    }

}

我使用gradle构建工具,我的build.gradle文件应用了'WAR'插件。在开发环境中运行时,我使用'bootrun'任务。而当我想要部署到生产环境时,我使用'assemble'任务生成WAR并进行部署。

在生产环境中,我可以像普通的Spring应用程序一样运行,同时也能充分利用内置Tomcat的优点来进行开发。希望这能有所帮助。


2

实际上,它应该是 bootRepackage { executable = true } 请参阅http://docs.spring.io/spring-boot/docs/current/reference/html/build-tool-plugins-gradle-plugin.html。 - Philippe

2
您可以使用名为Supervisor的应用程序。在supervisor配置中,您可以定义多个服务和执行相同服务的方式。
对于Java和Spring Boot应用程序,命令应该是java -jar springbootapp.jar
可以提供选项来始终保持应用程序运行。因此,如果EC2重新启动,则Supervisor将重新启动您的应用程序。
与将启动脚本放在/etc/init.d/中相比,我发现Supervisor易于使用。在出现错误的情况下,启动脚本会挂起或进入等待状态。

2
在生产环境中,您希望在机器重新启动等情况下再次启动应用程序。创建 /etc/init.d/ 脚本并链接到适当的运行级别以启动和停止它是正确的方法。Spring Boot 不会涵盖这个方面,因为它是一个操作系统特定的设置,而且还有很多其他选项。例如,您是否希望它在 chroot jail 中运行?它是否需要在其他软件之前停止/启动等。

1
我通过 screen -dmS NAME /path/to/script 启动我想要持续或至少半永久运行的应用程序。据我所知,这是最优雅的解决方案。

0

这是一个简单的问题,你可以使用Spring Boot Maven插件来完成代码部署。

插件配置如下:

<plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <configuration>
                    <jvmArguments>-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=n,address=${debug.port}
                    </jvmArguments>
                    <profiles>
                        <profile>test</profile>
                    </profiles>
                    <executable>true</executable>
                </configuration>
            </plugin>

而且,jvmArtuments 是为你的 jvm 添加的。 profiles 将选择一个配置文件来启动您的应用程序。 executable 可以直接运行您的应用程序。

如果您将 mvnw 添加到您的项目中,或者您有一个 maven 环境。 您只需调用 ./mvnw spring-boot:run(对于 mvnw)或 mvn spring-boot:run(对于 maven)即可。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接