Spring Boot Tomcat 容器化部署实践与总结

在平时的工作和学习中经常会构建简单的web应用程序。如果只是HelloWorld级别的程序,使用传统的Spring+SpringMVC框架搭建得话会将大部分的时间花费在搭建框架本身上面,比如引入SpringMVC,配置DispatcheherServlet等。并且这些配置文件都差不多,重复这些劳动似乎意义不大。所以使用Springboot框架来搭建简单的应用程序显得十分的便捷和高效。

前两天在工作中需要一个用于测试文件下载的简单web程序,条件是使用Tomcat Docker Image作为载体,所以为了方便就使用了SpringBoot框架快速搭建起来。

程序写出来在本机能够正常的跑起来,准备制作镜像,但是闻题就接踵而来了。首先是部署的问题,SpringBoot Web程序默认打的是jar包,运行时使用命令 java -jar -Xms128m -Xmx128m xxx.jar,本机跑的没问题。但是需求是使用外部的tomcat容器而不是tomcat-embed,所以查阅官方文档如下:

The first step in producing a deployable war file is to provide a SpringBootServletInitializer subclass and override its configure method. Doing so makes use of Spring Framework’s Servlet 3.0 support and lets you configure your application when it is launched by the servlet container. Typically, you should update your application’s main class to extend SpringBootServletInitializer, as shown in the following example:

@SpringBootApplication
public class Application extends SpringBootServletInitializer {br/>@Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
return application.sources(Application.class);
}
public static void main(String[] args) throws Exception {
SpringApplication.run(Application.class, args);
}
}

The next step is to update your build configuration such that your project produces a war file rather than a jar file. If you use Maven and spring-boot-starter-parent(which configures Maven’s war plugin for you), all you need to do is to modify pom.xml to change the packaging to war, as follows:

<packaging>war</packaging>
If you use Gradle, you need to modify build.gradle to apply the war plugin to the project, as follows:

apply plugin: ‘war‘
The final step in the process is to ensure that the embedded servlet container does not interfere with the servlet container to which the war file is deployed. To do so, you need to mark the embedded servlet container dependency as being provided.

If you use Maven, the following example marks the servlet container (Tomcat, in this case) as being provided:

<dependencies>

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

If you use Gradle, the following example marks the servlet container (Tomcat, in this case) as being provided:
dependencies {
// …
providedRuntime ‘org.springframework.boot:spring-boot-starter-tomcat‘
// …
}
综上所述,将SpringBoot程序放入Tomcat运行有两步。第一,SpringBoot启动类继承SpringBootServletInitializer,重写configure方法。第二,将包管理软件的打包方式改成war,并将Spring-boot-starter-tomcat设置为provided。但是,为什么应该这么做?
根据Servlet3.0规范可知,Web容器启动时通过ServletContainerInitializer类实现第三方组件的初始化工作,如注册servlet或filter等,每个框架要是用ServletContainerInitializer就必须在对应的META-INF/services目录下创建名为javax.servlet.ServletContainerInitializer的文件,文件内容指定具体的ServletContainerInitializer实现类,在SpringMVC框架中为SpringServletContainerInitializer。一般伴随着ServletContainerInitializer一起使用的还有HandlesTypes注解,通过HandlesTypes可以将感兴趣的一些类注入到ServletContainerInitializerde的onStartup方法作为参数传入。如下为SpringServletContainerInitializer源代码:
@HandlesTypes(WebApplicationInitializer.class)
public class SpringServletContainerInitializer implements ServletContainerInitializer {
@Override
public void onStartup(@Nullable Set> webAppInitializerClasses, ServletContext servletContext)throws ServletException {
List initializers = new LinkedList();
if (webAppInitializerClasses != null) {
for (Class waiClass : webAppInitializerClasses) {
if (!waiClass.isInterface() && !Modifier.isAbstract(waiClass.getModifiers()) &&
WebApplicationInitializer.class.isAssignableFrom(waiClass)) {
try {
// 将@HandlesTypes(WebApplicationInitializer.class)标注的所有这个类型的类都传入到onStartup方法的Set>;为这些WebApplicationInitializer类型的类创建实例。
initializers.add((WebApplicationInitializer)
ReflectionUtils.accessibleConstructor(waiClass).newInstance());
}
catch (Throwable ex) {
throw new ServletException("Failed to instantiate WebApplicationInitializer class", ex);
}
}
}
}
if (initializers.isEmpty()) {
servletContext.log("No Spring WebApplicationInitializer types detected on classpath");
return;
}
servletContext.log(initializers.size() + " Spring WebApplicationInitializers detected on classpath");
AnnotationAwareOrderComparator.sort(initializers);
for (WebApplicationInitializer initializer : initializers) {
//为每个WebApplicationInitializer调用自己的onStartup()
initializer.onStartup(servletContext);
}
}
}
SpringBootInitializer继承WebApplicationInitializer,重写的onStartup如下:
@Override
public void onStartup(ServletContext servletContext) throws ServletException {
this.logger = LogFactory.getLog(getClass());
// 调用自生createRootApplicationContext()方法
WebApplicationContext rootAppContext = createRootApplicationContext(
servletContext);
if (rootAppContext != null) {
servletContext.addListener(new ContextLoaderListener(rootAppContext) {
@Override
public void contextInitialized(ServletContextEvent event) {
}
});
}
else {
this.logger.debug("No ContextLoaderListener registered, as "
+ "createRootApplicationContext() did not "
+ "return an application context");
}
}
protected WebApplicationContext createRootApplicationContext(
ServletContext servletContext) {
SpringApplicationBuilder builder = createSpringApplicationBuilder();
builder.main(getClass());
ApplicationContext parent = getExistingRootWebApplicationContext(servletContext);
if (parent != null) {
this.logger.info("Root context already created (using as parent).");
servletContext.setAttribute(
WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, null);
builder.initializers(new ParentContextApplicationContextInitializer(parent));
}
builder.initializers(
new ServletContextApplicationContextInitializer(servletContext));
builder.contextClass(AnnotationConfigServletWebServerApplicationContext.class);
// 调用重写方法,重写方法传入SpringBoot启动类
builder = configure(builder);
builder.listeners(new WebEnvironmentPropertySourceInitializer(servletContext));
SpringApplication application = builder.build();
if (application.getAllSources().isEmpty() && AnnotationUtils
.findAnnotation(getClass(), Configuration.class) != null) {
application.addPrimarySources(Collections.singleton(getClass()));
}
Assert.state(!application.getAllSources().isEmpty(),
"No SpringApplication sources have been defined. Either override the "
+ "configure method or add an @Configuration annotation");
if (this.registerErrorPageFilter) {
application.addPrimarySources(
Collections.singleton(ErrorPageFilterConfiguration.class));
}
//启动应用程序,就是启动传入的SpringBoot程序
return run(application);
}
在程序和Tomcat打通之后需做的就是将war打成一个Docker镜像,如果每次都是复制war包,然后再docker build会很麻烦,在开源社区早有了解决方案–docker-maven-plugin,查看Github中的使用方法,将如下内容加入pom.xml中:

com.spotify
docker-maven-plugin
1.1.1

wanlinus/file-server

${project.basedir}

/
${project.build.directory}
${project.build.finalName}.war

该配置中有个标签是用来指定构建docker image的Dockerfile的位置,在项目的根目录下新建一个Dockerfile,内容如下:
FROM tomcat
MAINTAINER wanlinus
WORKDIR /docker
COPY target/file-server-0.0.1-SNAPSHOT.war ./server.war
RUN mkdir $CATALINA_HOME/webapps/server && mv /docker/server.war $CATALINA_HOME/webapps/server && unzip $CATALINA_HOME/webapps/server/server.war -d $CATALINA_HOME/webapps/server/ && rm $CATALINA_HOME/webapps/server/server.war && cd $CATALINA_HOME/webapps/server && echo "asd" > a.txt
EXPOSE 8080
终端中输入
mvn clean package docker:build
在本地将会生成一个docker image,如果docker没有运行于本地,需要在标签中输入远端地址和docker daemon端口。
最后在终端中运行
docker run --rm -p 8080:8080 wanlinus/fileserver
在Tomcat启动后将会看到Spring Boot程序的启动日志,至此,Spring Boot Tomcat容器化完成。

Spring Boot Tomcat 容器化部署实践与总结

原文地址:http://blog.51cto.com/13952953/2172889

时间: 2024-08-14 00:11:57

Spring Boot Tomcat 容器化部署实践与总结的相关文章

Spring boot打包为可部署在tomcat下运行的war文件的方法(使用Gradle、Intellij IDEA)

使用Gradle: dependencies { compile("org.springframework.boot:spring-boot-starter-web") providedCompile("org.springframework.boot:spring-boot-starter-tomcat")//此处使用providedCompile,则生成的jar包可放入tomcat内运行// compile("org.springframework.b

Kubernetes 集群的两种部署过程(daemon部署和容器化部署)以及glusterfs的应用!

ClusterIp:通过VIP来访问, NodePort: 需要自己搭建负载据衡器 LoadBalancer:仅仅用于特定的云提供商 和 Google Container Engine https://www.nginx.com/blog/load-balancing-kubernetes-services-nginx-plus/ port:相当于服务端口(对及集群内客户访问) targetPort: 相当于pods端口 nodePort: 宿主机端口(也是服务端口,只不过是对集群外客户访问)

ASP.NET Core在CentOS上的最小化部署实践

原文:ASP.NET Core在CentOS上的最小化部署实践 引言 本文从Linux小白的视角, 在CentOS 7.x服务器上搭建一个Nginx-Powered AspNet Core Web准生产应用. 在开始之前,我们还是重温一下部署原理,正如你所常见的.Net Core 部署图: 在Linux上部署.Net Core App最好的方式是在Linux机器上使用Kestrel 服务在端口5000上支撑web应用: 然后设置Nginx作为反向代理服务器,将输入请求转发给Kestrel服务器,

Kolla 容器化部署Openstack

1.集群架构  其中e1,e2,e3 表示网卡. e1 所在网络为管理网. e2 为vm对外通信网卡,无需配置网络协议和ip地址: BOOTPROTO=none DEVICE=eth1 HWADDR=fa:16:3e:38:20:88 ONBOOT=yes TYPE=Ethernet USERCTL=no .... e3所在网络为osd集群通信网络 备注:操作系统为centos7.4 最小化安装 2. kolla容器化部署openstack及ceph集群 2.1 基础环境 以下操作在部署机执行

Flask容器化部署原理与实现

本文将介绍Flask的部署方案:Flask + Nginx + uWSGI,并使用docker进行容器化部署,部署的实例来源 Flask开发初探,操作系统为ubuntu. Flask系列文章: Flask开发初探 WSGI到底是什么 Flask源码分析一:服务启动 Flask路由内部实现原理 部署方案 在开发时,我们使用flask run命令启动的开发服务器是werkzeug提供的,但是这种方式目的是为了开发,不能很好的扩展,不适合生产部署.生产部署,我们需要一个更强健,性能更高的WSGI服务器

Spring Boot将WAR文件部署到Tomcat

在本文中,将演示如何将Spring Boot WAR文件部署到Tomcat servlet容器中. 对于Spring Boot WAR部署,需要执行三个步骤: 扩展SpringBootServletInitializer 根据提供标记嵌入式servlet容器. 更新包装为 War 测试工具: Spring Boot 1.4.2.RELEASE Tomcat 8.5.9 Maven 3 注意在Spring Boot中,具有嵌入服务器解决方案的最终可执行JAR文件可能不适合所有生产环境,特别是部署团

如何把kotlin+spring boot开发的项目部署在tomcat上

本文只讲部署过程,你首先要保证你的程序能在IDE里跑起来: 先看看你的application.properties中设置的端口号与你服务器上tomcat的端口号是否一致 server.port=80 (我现在不确定此配置是否会影响部署) 看看你的应用程序的入口函数 @SpringBootApplication@EnableAutoConfiguration(exclude = arrayOf(DataSourceAutoConfiguration::class))class JnaApplica

spring boot tomcat 部署

前几天springboot项目部署到linux中,整个过程就是个坑啊.踩坑的过程中也学到了许多.spring boot 项目部署时由于其内置了tomcat和jdk,而且还都是8. 所以部署的话就分为两种部署了, 第一种就是使用其内置的tomcat部署, 第二种就是采用外部的tomcat部署.采用内部的tomcat部署又分为两种: 第一种是打包成war包部署,第二种事打包成jar包部署. 两者区别就在于打包成jar包的是无静态资源的,如jsp,HTML等,像只是提供restful接口. 阅读此篇博

docker 部署Spring Boot:Docker化Spring Boot应用程序

第一章 1.创建项目存放目录 mkdir /root/sproot -p 2.准备好Spring Boot应用程序 jar 包 testrest.jar 第二章 1. 安装docker 在所有节点执行:   setenforce 0 iptables -F iptables -t nat -F iptables -I FORWARD -s 0.0.0.0/0 -d 0.0.0.0/0 -j ACCEPT iptables -I INPUT -s 192.168.0.0/24 -j ACCEPT