前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
社区首页 >专栏 >SpringBoot打包部署最佳实践

SpringBoot打包部署最佳实践

作者头像
Bug开发工程师
发布于 2020-04-22 05:04:14
发布于 2020-04-22 05:04:14
3.6K00
代码可运行
举报
文章被收录于专栏:码农沉思录码农沉思录
运行总次数:0
代码可运行

spring boot介绍

Spring Boot目前流行的java web应用开发框架,相比传统的spring开发,spring boot极大简化了配置,并且遵守约定优于配置的原则即使0配置也能正常运行,这在spring中是难以想象的。spring boot应用程序可以独立运行,框架内嵌web容器,使得web应用程序可以像本地程序一样启动和调试,十分的方便,这种设计方式也使得spring boot应用程序非常适合容器化进行大规模部署。生态方面,spring boot提供了非常丰富的组件,目前流行的java web框架基本都有spring boot版本,生态十分庞大,是目前java web开发最好的方案。

spring boot部署问题

Springboot应用程序有两种运行方式

以jar包方式运行

以war包方式运行

两种方式应用场景不一样,各有优缺点

jar包运行

通过maven插件spring-boot-maven-plugin,在进行打包时,会动态生成jar的启动类org.springframework.boot.loader.JarLauncher,借助该类对springboot应用程序进行启动。

优点

本地无需搭建web容器,方便开发和调试。

因为自带web容器,可以避免由于web容器的差异造成不同环境结果不一致问题。

一个jar包就是全部,方便应用扩展。

借助容器化,可以进行大规模的部署。

缺点

应用过于独立,难以统一管理。

数据源无法通过界面进行管理。

应用体积过大。

修改web容器相关配置较为困难,需要借助代码实现。

war包运行

以war包方式运行,通过maven插件spring-boot-maven-plugin进行相关配置后,最终生成一个可运行在tomcat,weblogic等java web容器中的war包。

优点

可以借助web容器管理界面对应用进行管理。

可以管理JNDI数据源。

web容器配置较为灵活,配置和程序分离。

应用体积较小,甚至可以借助web容器的包管理功能(比如weblogic Library)进一步减小应用大小。

缺点

本地需要搭建web容器,对本地环境要求更高点,学习成本也响应更高。

调试较为困难,需要借助web容器。

无法兼容所有web容器(比如spring boot2.x无法运行在weblogic 11g上)。

部署较为困难(比如和weblogic有较多的类冲突)

在实际的项目中,并没有哪一种方式是最好的,根据客户不同的需求制定不同的部署方案,比如有些客户比较看中管理功能,要求数据源和tomcat相关配置必须由管理员进行管理,那么选择war包方式,有些客户希望借助容器化进行大规模部署,那么jar方式更适合。不管选择哪种方式,在部署时都会遇到下面的问题

如果需要打war包,那么不仅是pom文件需要修改,应用程序也要做相应的改动,改动完后,应用程序就无法本地运行,需要打完包后将配置信息修改回来,这样不仅麻烦,还容易出错。

不管是war包还是jar包,如何管理不同环境的配置文件,保证不会出错,虽然spring boot有提供spring.profiles.active配置设置不同的环境,但一方面需要人为修改配置文件,只要是人为的就有可能出错,另一方面,客户有时出于安全考虑不会提供生产环境配置信息,那么这时候就无法指定prifiles.active。

如何将多个spring boot模块打包在一起。

jar包需要配合容器化才能发挥出最大的优势,如果没有容器,spring boot jar包就是一个玩具,随处运行的jar包,缺少统一管理,是达不到生产的要求,那么如果从jar包到容器也是一个问题。

早期碰到这些问题,都是人工解决,不仅效率十分低下,部署一次都需要十几分钟,而且很容易出错,一百次出错一次算是概率低了,但是生产出错一次都是重大事件,所以我们也在思考如何通过自动化解决以上问题,如何将开发和部署分离,开发人员只关心开发,开发完提交代码,打包和部署都是后台透明的完成。以下就是我们的解决方案。

打包war包打包问题解决

spring boot打war包的步骤如下

在pom.xml中将打包方式改为war。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    ...
    war
    ...

设置spring-boot-starter-tomcat范围为provided

    org.springframework.boot
    spring-boot-starter-tomcat
    provided

修改spring boot的启动类,继承SpringBootServletInitializer
public class DemoApplication extends SpringBootServletInitializer{
    @Override
    protected SpringApplicationBuilder configure(SpringApplicationBuilder builder) {
        return builder.sources(DemoApplication.class);
    }
}

每打包一次都要修改pom.xml和启动类,打包完再修改回来,十分的繁琐,因为,我们提出以下整改方案

从pom.xml复制一个pom-war.xml文件,将pom-war.xml修改为war包配置

在根目录下(除了src目录外都可以)复制一份启动类的代码,修改为war包的配置方式。

编写shell脚本进行打包。

shell脚本打包过程为

  1. 备份当前启动类的java代码。
  2. 将war包启动类的代码替换掉当前启动类的代码。
  3. maven指定pom-war.xml文件进行打包。
  4. 打包结束后恢复启动类文件。

以下就是参考脚本

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
app-war.sh
#!/usr/bin/env bash

v1=src/main/java/com/definesys/demo/DemoApplication.java
v2=war/DemoApplication.java
v3=war/DemoApplication-bak.java

cp -rf $v2 $v1

mvn clean package -Dmaven.test.skip=true -f war-pom.xml

#recovery
cp -rf $v3 $v1

通过预先配置好pom文件和启动类文件,开发人员只要运行app-war.sh脚本无需修改任何文件即可生成war包。

更优的方案

以上方案pom文件和启动类文件都需要预先准备好,未实现完全的自动化,通过优化方案做到完全自动化。

脚本可以通过find命令搜索以*Application.java结尾的文件,作为启动类文件,读取文件名获取类名,通过字符串替换方式动态生成war包启动类文件。

在pom.xml中用注释设置好锚点,脚本通过替换锚点动态生成pom.xml文件。

如果不希望通过锚点实现,可以借助更高级的脚本语言,比如python对xml进行解析,再动态生成xml。

多模块打包

这里的多模块指的是maven中的多模块,项目工程中的代码多模块,一个项目按功能划分模块后,在创建工程时一般也按照功能层面上的模块进行创建,这样避免一个模块代码过于庞大,也利于任务的分工,但打包却更麻烦了。

每个模块都是独立的spring boot程序,整合到一个包的时候会出现多个启动类,多个配置文件冲突的问题。

每个模块有引用相同的依赖,依赖包版本升级后,需要每个pom文件都做修改。

通过优化项目结构解决以上问题

父项目的pom指定spring boot的依赖和公共的依赖。

创建一个spring boot的子项目,作为启动项目,我们称为start项目。

其余子项目为普通的java maven项目,parent设置为第一步创建的spring boot父项目。

start项目的pom引用其他子项目的依赖。

本地调试可以直接运行start的启动类,ide会自动编译其他模块并引用。

打包可以在父项目上进行install后再进入start项目进行打包,脚本参考如下

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
mvn clean install
cd start
mvn clean package

目录结构如下

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
.
├── pom.xml
├── role
│   ├── pom.xml
│   └── src
│       ├── main
│       │   ├── java
│       │   │   └── com
│       │   │       └── definesys
│       │   │           └── demo
│       │   │               └── controller
│       │   │                   └── RoleController.java
│       │   └── resources
├── start
│   ├── pom.xml
│   ├── src
│   │   ├── main
│   │   │   ├── java
│   │   │   │   └── com
│   │   │   │       └── definesys
│   │   │   │           └── demo
│   │   │   │               └── DemoApplication.java
│   │   │   └── resources
│   │   │       └── application.properties
└── user
    ├── pom.xml
    └── src
        └── main
            ├── java
            │   └── com
            │       └── definesys
            │           └── demo
            │               └── controller
            │                   └── UserController.java
            └── resources

start项目包含包含启动类和配置文件,pom文件引用其余子项目。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
start pom.xml

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    
        blog0915
        com.definesys.demo
        1.0-SNAPSHOT
    
    4.0.0
    start
    
        
            com.definesys.demo
            user
            1.0.0
        
        
            com.definesys.demo
            role
            1.0.0
        
    
    
        
            
                org.springframework.boot
                spring-boot-maven-plugin

父项目parent为spring boot,引用spring boot相关依赖和各个子项目公共的依赖

父项目 pom.xml

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    4.0.0
    pom
    
        user
        role
        start
    

    
        org.springframework.boot
        spring-boot-starter-parent
        2.1.2.RELEASE
        
    

    com.definesys.demo
    blog0915
    1.0-SNAPSHOT

    
        
            org.springframework.boot
            spring-boot-starter-web

所有非start的子项目需要指定版本号并且父项目都设为根目录项目。

子项目 pom.xml

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    
        blog0915
        com.definesys.demo
        1.0-SNAPSHOT
    
    4.0.0
    role
    1.0.0

所有子项目的包路径前缀必须一样,并且以start项目作为基本路径。

配置文件问题

spring boot提供spring.profiles.active指定配置文件,但生产环境有时候客户出于安全考虑不提供配置信息给开发人员,而是预先将配置文件上传到服务器指定路径,程序需要在运行时去引用该配置文件,如果运行环境是kubernetes,则会提供一个config map作为配置文件,这时候就要求spring boot程序读取外部配置文件。

这里讨论的是线上环境配置文件方案,本地调试参考子模块打包相关内容,可以将配置文件统一写在start项目中。

jar包外部配置文件读取

jar运行可以通过指定参数spring.config.location引用外部文件,命令参考如下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
java -jar start-1.0-SNAPSHOT.jar --spring.config.location=/Users/asan/workspace/config

config目录存放properties配置文件

可以通过配合spring.profiles.active参数可以指定目录下配置文件,如:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
java -jar start-1.0-SNAPSHOT.jar --spring.profiles.active=prod --spring.config.location=/Users/asan/workspace/config

则会读取/Users/asan/workspace/config/appliction-prod.properties文件作为配置文件。

war包外部配置文件读取

以tomcat为例,需要在tomcat启动时指定-Dspring.config.location参数,可以设置服务器环境变量CATALINA_OPTS达到目的。可以编辑用户 prifile文件

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
export CATALINA_OPTS=/Users/asan/workspace/config

同样,也可以通过-Dspring.profiles.active指定配置文件名称。

容器化

spring boot借助容器化,可以如虎添翼,发挥出更大的威力,也只有通过容器化,才能体会到spring boot开发的高效。通过以上的介绍,你可以很顺利的打好一个jar包或者war包,那么可以通过编写dockerfile文件进行镜像的构建。spring boot在构建镜像时有两个地方需要考虑

时区问题,基础镜像的时区默认是UTC,比北京时间早8小时,需要指定镜像时区。

配置文件问题,需要指定外部配置文件(根据项目具体情况选择)。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
app-jar-dockerfile.Dockerfile
FROM openjdk:8-jdk-alpine

MAINTAINER definesys.com

VOLUME /tmp
ADD start-1.0-SNAPSHOT.jar app.jar
RUN echo "Asia/Shanghai" > /etc/timezone
EXPOSE 8080
ENTRYPOINT ["java","-Djava.security.egd=file:/dev/./urandom","-jar","--spring.config.location=/Users/asan/workspace/config","/app.jar"]
app-war.dockerfile.Dockerfile
FROM tomcat

MAINTAINER definesys.com

ENV CATALINA_OPTS -Dspring.config.location=file:/middleware/config/
ADD start-1.0-SNAPSHOT.war /usr/local/tomcat/webapps/app.jar
RUN echo "Asia/Shanghai" > /etc/timezone
EXPOSE 8080

EOF部署

早期我们采用的是以下部署过程

首先构建测试环境的镜像,上传到镜像仓库,应用重新部署。

接着构建UAT环境的镜像,上传到镜像仓库,应用重新部署。

最后构建生产环境的镜像,上传到镜像仓库,应用重新部署。

每一次发布都是一个新的镜像,但这种方式有个问题就是如何保证前一个环境验证没问题,后一个环境就一定没问题,因为两个镜像是不一样的,虽然可能两次构建都是基于同一版本代码,但因为是重新构建,中间可能因为各种原因,如maven包版本更新等,无法保证两次构建就是完全一样的镜像。因此我们优化了构建的流程,如下:

所有的环境都是用同一个镜像,环境之间只有配置文件不同,文件通过configmap或者外部配置文件方式进行挂载,这样保证了配置文件没问题的前提下,每个环境的程序一定是一样的。

jenkins自动打包部署

打包和部署在本地进行也是有问题的,本地jdk版本取决于个人电脑,甚至有黑客污染jdk导致编译的class文件自带后门,个人电脑环境也是随着用户不同操作可能改变,构建出来的包不能保证是稳定的包。因此需要一个远程服务器用于打包和部署,能够实现从源码到镜像过程。jenkins是一个基于java开发的持续集成工具,通过配置插件和编写脚本实现程序从代码到制品再到线上运行的过程。jenkins在spring boot开发中主要完成了以下工作。

通过gitlab插件实现源代码的获取。

基于以上介绍的脚本,实现从源码到制品的过程。

通过docker工具实现从制品到镜像的过程。

通过kubectl工具,实现从镜像到上云的过程。

jenkins在构建镜像时需要借助docker工具,但jenkins本身也是有docker版本的,所以就面临着docker in docker的问题,这里选择的方案是用二进制文件安装jenkin而非镜像方式,虽然丧失了docker的便利性,但可以简化docker方案,降低集成的复杂度。

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2020-04-21,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 码农沉思录 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
Spring boot(4)-应用打包部署
Spring Boot 其默认是集成web容器的,启动方式由像普通Java程序一样,main函数入口启动。其内置Tomcat容器或Jetty容器,具体由配置来决定(默认Tomcat)。当然你也可以将项目打包成war包,放到独立的web容器中(Tomcat、weblogic等等),当然在此之前你要对程序入口做简单调整。
黄规速
2022/04/14
1.5K0
Spring boot(4)-应用打包部署
搭建SpringBoot项目三种方式(超详细版)
接下来我们搭建一个SpringBoot项目,并引入SpringMVC的功能,首先我们可以通过官网搭建项目:
会洗碗的CV工程师
2023/11/05
9.2K0
搭建SpringBoot项目三种方式(超详细版)
SpringBoot项目打成war和jar的区别「建议收藏」
1.我的一个springboot项目,用mvn install打包成jar,换一台有jdk的机器就直接可以用java -jar 项目名.jar的方式运行,没任何问题,为什么这里不需要tomcat也可以运行了? 2.然后我打包成war放进tomcat运行,发现端口号变成tomcat默认的8080(我在server.port中设置端口8090)项目名称也必须加上了。 也就是说我在原来的机器的IDEA中运行,项目接口地址为 ip:8090/listall,打包放进另一台机器的tomcat就变成了ip:8080/项目名/listall。这又是为什么呢?
全栈程序员站长
2022/08/25
2.5K0
SpringBoot项目打成war和jar的区别「建议收藏」
Spring Boot项目打包部署,打Jar包和War包有什么区别?
部署 Spring Boot 项目可以采用多种方式,下面是常用的几种部署方式,同学们可以简单做一个了解。
Designer 小郑
2024/01/25
2.7K0
Spring Boot项目打包部署,打Jar包和War包有什么区别?
springboot测试、打包、部署
本文使用《springboot集成mybatis(一)》项目,依次介绍springboot测试、打包、部署。
名山丶深处
2022/05/10
1.7K0
Spring Boot 两种部署到服务器的方式
jar包方式启动,也就是使用spring boot内置的tomcat运行。服务器上面只要你配置了jdk1.8及以上,就ok。不需要外置tomcat
闻说社
2025/01/20
1120
Spring Boot 两种部署到服务器的方式
框架 | SpringBoot项目发布到自动化容器Docker步骤
如何发布Sping Boot项目? 新建好SpringBoot项目之后,SIT测试如果没问题,则下一步肯定要进行UAT测试。 那么如何将SpringBoot项目进行发布部署,这和我们之前的普通web项目不太一样,之前的项目直接部署到tomcat的webapps中,然后启动tomcat即可访问。 但是SpringBoot项目内嵌tomcat,这如何发布部署访问呢? 目前比较常用的方式有三种:发布JAR包、发布WAR包、部署到自动化容器中,以下具体讲解发布部署过程。 1 技术选型 JDK1.7、MYSQL57、
码神联盟
2018/06/04
2.2K0
SpringBoot入门系列(三十)Spring Boot项目打包、发布与部署
Spring Boot使用了内嵌容器,因此它的部署方式也变得非常简单灵活,一方面可以将Spring Boot项目打包成独立的jar或者war包来运行,也可以单独打包成war包部署到Tomcat容器中运行,如果涉及到大规模的部署Jinkins成为最佳选择之一。
架构师精进
2021/08/18
27.3K0
Springboot的项目如何打成war包
1、在SpringBoot中默认支持Tomcat容器,所以当一个SpringBoot项目打包生成*.jar文件,并且直接执行的时候就会自动启动内部的Tomcat容器。除了此种模式之外,也可以将Web项目打包为*.war文件,采用部署的形式通过Tomcat进行发布处理,这种方式和传统模式比较类似,打成war包丢到tomcat里面进行运行。
别先生
2020/10/30
2.3K0
Springboot的项目如何打成war包
打包SpringBoot工程并部署
   在Goals中输入:org.apache.maven.plugins:maven-jar-plugin:2.4:jar org.springframework.boot:spring-boot-maven-plugin:1.0.1.RELEASE:repackage
JQ实验室
2022/02/09
4860
打包SpringBoot工程并部署
Maven课堂笔记
Apache Maven 是一个软件项目管理和依赖管理工具。基于项目对象模型 (POM:Project Object Model) 的概念,Maven 可以从一个中心信息块管理项目的构建、报告和文档。
闲花手札
2021/08/24
6800
让SpringBoot应用同时可以嵌入式和部署到Tomcat容器来运行详解
使用 Spring Boot 应用,我们可以jar方式启动,可以创建一个war文件部署到web服务器中。
白石
2021/01/06
7900
springboot(十二):springboot如何测试打包部署
有很多网友会时不时的问我,spring boot项目如何测试,如何部署,在生产中有什么好的部署方案吗?这篇文章就来介绍一下spring boot 如何开发、调试、打包到最后的投产上线。 开发阶段 单元测试 在开发阶段的时候最重要的是单元测试了,springboot对单元测试的支持已经很完善了。 1、在pom包中添加spring-boot-starter-test包引用 <dependency> <groupId>org.springframework.boot</groupId> <arti
纯洁的微笑
2018/04/19
1.9K0
SpringBoot ( 十二 ) :SpringBoot 如何测试打包部署
有很多网友会时不时的问我,spring boot项目如何测试,如何部署,在生产中有什么好的部署方案吗?这篇文章就来介绍一下spring boot 如何开发、调试、打包到最后的投产上线。
前朝楚水
2018/07/26
1K0
docker微服务简单打包部署
application.properties或者yml文件都可以,我这里用的properties
别团等shy哥发育
2023/02/25
7880
docker微服务简单打包部署
springboot学习(一):初识SpringBoot(入门篇)
Spring Boot是由Pivotal团队提供的全新框架,其设计目的是用来简化新Spring应用的初始搭建以及开发过程。该框架使用了特定的方式来进行配置,从而使开发人员不再需要定义样板化的配置。通过这种方式,Spring Boot致力于在蓬勃发展的快速应用开发领域(rapid application development)成为领导者。
sunonzj
2022/06/21
4570
springboot学习(一):初识SpringBoot(入门篇)
5分钟快速上手Spring Boot
与一些动态语言(如Ruby、Groovy、Node.js)相比,Java开发显得异常笨重。接触过外包项目的朋友也有所了解,如果要开发一个小型项目,首选的编程语言并不是Java,而是PHP。为什么呢?因为开发起来快!目前很多大型互联网公司的早起编程语言都是类似PHP这种能够快速开发的语言。 既然问题出现了,那必然有解决问题的方案,SpringBoot做到了。SpringBoot是由Pivotal公司所属团队研发,该公司的企业宗旨为:
程序新视界
2019/05/29
8610
5分钟快速上手Spring Boot
框架 | SpringBoot项目创建和发布部署步骤
如何创建Spring Boot项目? 1 技术选型 JDK1.7、MYSQL57、Spring Boot、Logback、Mybatis 2 开发工具 Myeclipse、Maven、Linux 3 数据库设计 表名:userinfo 结构如下: CREATE TABLE `userinfo` ( `id` int(20) NOT NULL AUTO_INCREMENT, `username` varchar(20) DEFAULT NULL, `password` varchar(20) D
码神联盟
2018/06/04
5.2K2
Spring Boot框架基础概览
Spring Boot是所有基于Spring开发的项目的起点,Spring Boot的设计是为了让你尽可能快的跑起来Spring应用程序并且尽可能减少你的配置文件,它采用了"习惯优于配置"的理念,就像Maven整合了所有的JAR包一样,Spring boot整合了所有框架
Al1ex
2022/09/07
5350
Spring Boot框架基础概览
Maven 划分模块最佳实践
所有用Maven管理的真实的项目都应该是分模块的,每个模块都对应着一个pom.xml。它们之间通过继承和聚合(也称作多模块,multi-module)相互关联。那么,为什么要这么做呢?我们明明在开发一个项目,划分模块后,导入Eclipse变成了N个项目,这会带来复杂度,给开发带来不便。 为了解释原因,假设有这样一个项目,很常见的Java Web应用。在这个应用中,我们分了几层:
BUG弄潮儿
2022/03/08
5920
相关推荐
Spring boot(4)-应用打包部署
更多 >
领券
社区富文本编辑器全新改版!诚邀体验~
全新交互,全新视觉,新增快捷键、悬浮工具栏、高亮块等功能并同时优化现有功能,全面提升创作效率和体验
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
查看详情【社区公告】 技术创作特训营有奖征文
本文部分代码块支持一键运行,欢迎体验
本文部分代码块支持一键运行,欢迎体验