想一想,若是Spring Boot的项目在本地IDEA能够直接经过内嵌tomcat的jar运行,而只要往GitHub上提交代码,就能自动帮你打包成war包部署在项目组的WebLogic服务器上,该减小多少非开发的工做量。这也是DevOps中很重要的一环。本文档就是针对该设想,作出技术上的尝试。首先申明这纯属技术上的尝试研究,不会主动应用在客户的生产环境架构中。java
当我接触了Spring Boot,就果断抛弃了Spring MVC。一则咱们能够经过各类IDEA很是简单的生成一个Spring Boot的项目,不像Spring MVC还有麻烦的XML配置文件;二则由于Spring Boot内嵌Tomcat,本地运行时直接运行启动类就好了,不须要再准备一个Tomcat 服务器,打War包部署。但同时也遇到了其余的问题:git
我所在的项目使用的服务器就是WebLogic 11g,在受益于Spring Boot开发的灵活性与多元性同时,又浪费了不少无效的时间在解决WebLogic上部署的问题:添加weblogic.xml文件,Jar包冲突等等。最近周末业余时间我作了一些尝试,简单实现了以上的需求,如今进入正题吧。github
须要准备的基本配置很简单:GitHub帐号,Jenkins和WebLogic 11g服务器就够了。若是是企业内网,访问公网的Maven中央仓库,可能还须要Nexus搭建Maven私服。内网的具体方案本文档会讨论,但考虑到保密,不会引用具体的实践操做。web
上文说到,Spring Boot项目我本地是直接运行Jar包,而部署在WebLogic上的是War包。Jar包和War包的结构有什么关联和区别呢?这里我新建了一个最简单的Spring Boot项目,分别生成了Jar和能够部署在WebLogic上的War包:spring
一、jar包结构shell
. ├── BOOT-INF │ ├── classes │ │ ├── application.properties │ │ └── com │ │ └── kerry │ │ └── springbootdemo │ │ ├── DemoController.class │ │ └── SpringbootDemoApplication.class │ └── lib │ ├── classmate-1.3.4.jar │ ├── hibernate-validator-6.0.13.Final.jar │ ├── jackson-annotations-2.9.0.jar │ ├── jackson-core-2.9.7.jar │ ├── jackson-databind-2.9.7.jar │ ├── jackson-datatype-jdk8-2.9.7.jar │ ├── jackson-datatype-jsr310-2.9.7.jar │ ├── jackson-module-parameter-names-2.9.7.jar │ ├── javax.annotation-api-1.3.2.jar │ ├── jboss-logging-3.3.2.Final.jar │ ├── jul-to-slf4j-1.7.25.jar │ ├── log4j-api-2.10.0.jar │ ├── log4j-to-slf4j-2.10.0.jar │ ├── logback-classic-1.2.3.jar │ ├── logback-core-1.2.3.jar │ ├── slf4j-api-1.7.25.jar │ ├── snakeyaml-1.19.jar │ ├── spring-aop-5.0.10.RELEASE.jar │ ├── spring-beans-5.0.10.RELEASE.jar │ ├── spring-boot-2.0.6.RELEASE.jar │ ├── spring-boot-autoconfigure-2.0.6.RELEASE.jar │ ├── spring-boot-starter-2.0.6.RELEASE.jar │ ├── spring-boot-starter-json-2.0.6.RELEASE.jar │ ├── spring-boot-starter-logging-2.0.6.RELEASE.jar │ ├── spring-boot-starter-tomcat-2.0.6.RELEASE.jar │ ├── spring-boot-starter-web-2.0.6.RELEASE.jar │ ├── spring-context-5.0.10.RELEASE.jar │ ├── spring-core-5.0.10.RELEASE.jar │ ├── spring-expression-5.0.10.RELEASE.jar │ ├── spring-jcl-5.0.10.RELEASE.jar │ ├── spring-web-5.0.10.RELEASE.jar │ ├── spring-webmvc-5.0.10.RELEASE.jar │ ├── tomcat-embed-core-8.5.34.jar │ ├── tomcat-embed-el-8.5.34.jar │ ├── tomcat-embed-websocket-8.5.34.jar │ └── validation-api-2.0.1.Final.jar ├── META-INF │ ├── MANIFEST.MF │ └── maven │ └── com.kerry │ └── springboot-demo │ ├── pom.properties │ └── pom.xml └── org └── springframework └── boot └── loader ├── archive │ ├── Archive.class │ ├── Archive$Entry.class │ ├── Archive$EntryFilter.class │ ├── ExplodedArchive$1.class │ ├── ExplodedArchive.class │ ├── ExplodedArchive$FileEntry.class │ ├── ExplodedArchive$FileEntryIterator.class │ ├── ExplodedArchiveFileEntryIteratorEntryComparator.class │ ├── JarFileArchive.class │ ├── JarFileArchive$EntryIterator.class │ └── JarFileArchive$JarFileEntry.class ├── data │ ├── RandomAccessData.class │ ├── RandomAccessDataFile$1.class │ ├── RandomAccessDataFile.class │ ├── RandomAccessDataFile$DataInputStream.class │ └── RandomAccessDataFile$FileAccess.class ├── ExecutableArchiveLauncher.class ├── jar │ ├── AsciiBytes.class │ ├── Bytes.class │ ├── CentralDirectoryEndRecord.class │ ├── CentralDirectoryFileHeader.class │ ├── CentralDirectoryParser.class │ ├── CentralDirectoryVisitor.class │ ├── FileHeader.class │ ├── Handler.class │ ├── JarEntry.class │ ├── JarEntryFilter.class │ ├── JarFile$1.class │ ├── JarFile$2.class │ ├── JarFile.class │ ├── JarFileEntries$1.class │ ├── JarFileEntries.class │ ├── JarFileEntries$EntryIterator.class │ ├── JarFile$JarFileType.class │ ├── JarURLConnection$1.class │ ├── JarURLConnection.class │ ├── JarURLConnection$JarEntryName.class │ ├── StringSequence.class │ └── ZipInflaterInputStream.class ├── JarLauncher.class ├── LaunchedURLClassLoader.class ├── LaunchedURLClassLoader$UseFastConnectionExceptionsEnumeration.class ├── Launcher.class ├── MainMethodRunner.class ├── PropertiesLauncher$1.class ├── PropertiesLauncher$ArchiveEntryFilter.class ├── PropertiesLauncher.class ├── PropertiesLauncher$PrefixMatchingArchiveFilter.class ├── util │ └── SystemPropertyUtils.class └── WarLauncher.class
二、War包结构express
. └── WEB-INF ├── classes │ ├── application.properties │ └── com │ └── kerry │ └── springbootdemo │ ├── DemoController.class │ └── SpringbootDemoApplication.class ├── lib │ ├── aopalliance-1.0.jar │ ├── classmate-1.0.0.jar │ ├── dfutil-11.2.0.1.0.jar │ ├── fastjson-1.2.4.0.jar │ ├── hibernate-validator-5.0.3.Final.jar │ ├── jackson-annotations-2.3.5.jar │ ├── jackson-core-2.3.5.jar │ ├── jackson-databind-2.3.5.jar │ ├── jboss-logging-3.1.1.GA.jar │ ├── jcl-over-slf4j-1.7.11.jar │ ├── joda-time-2.3.jar │ ├── jul-to-slf4j-1.7.11.jar │ ├── log4j-over-slf4j-1.7.11.jar │ ├── logback-classic-1.1.3.jar │ ├── logback-core-1.1.3.jar │ ├── mpaas-sso-oam-1.0.0.jar │ ├── oamasdk-api-11.1.1.5.0.jar │ ├── ojdbc6-11.2.0.1.0.jar │ ├── query-0.0.30-SNAPSHOT.jar │ ├── slf4j-api-1.7.11.jar │ ├── snakeyaml-1.13.jar │ ├── spring-aop-4.2.5.RELEASE.jar │ ├── spring-beans-4.2.5.RELEASE.jar │ ├── spring-boot-1.1.12.RELEASE.jar │ ├── spring-boot-autoconfigure-1.1.12.RELEASE.jar │ ├── spring-boot-legacy-1.0.2.RELEASE.jar │ ├── spring-boot-starter-1.1.12.RELEASE.jar │ ├── spring-boot-starter-logging-1.1.12.RELEASE.jar │ ├── spring-boot-starter-web-1.1.12.RELEASE.jar │ ├── spring-context-4.2.5.RELEASE.jar │ ├── spring-core-4.2.5.RELEASE.jar │ ├── spring-expression-4.2.5.RELEASE.jar │ ├── spring-web-4.2.5.RELEASE.jar │ ├── spring-webmvc-4.2.5.RELEASE.jar │ └── validation-api-1.1.0.Final.jar ├── weblogic.xml └── web.xml
经过解剖 Jar包和War包的结构,咱们很容易看出,War包所须要的无非就三个部分:json
那么若是咱们要本身生成War包,就须要生成这三个部分:segmentfault
web.xml模板(添加了公司倚天项目)api
<?xml version="1.0" encoding="UTF-8"?> <web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"> <context-param> <param-name>contextConfigLocation</param-name> <param-value>{application-class}</param-value> </context-param> <listener> <listener-class>org.springframework.boot.legacy.context.web.SpringBootContextLoaderListener</listener-class> </listener> <filter> <filter-name>MpaasFilter</filter-name> <filter-class>com.definesys.mpaas.query.filter.J2EEServletFilter</filter-class> <init-param> <param-name>userHeaderName</param-name><!-- store user identify --> <param-value>oam_remote_user</param-value> </init-param> </filter> <filter-mapping> <filter-name>MpaasFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> <servlet> <servlet-name>appServlet</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <init-param> <param-name>contextAttribute</param-name> <param-value>org.springframework.web.context.WebApplicationContext.ROOT</param-value> </init-param> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>appServlet</servlet-name> <url-pattern>/</url-pattern> </servlet-mapping> </web-app>
weblogic.xml模板
<?xml version="1.0" encoding="UTF-8"?> <wls:weblogic-web-app xmlns:wls="http://xmlns.oracle.com/weblogic/weblogic-web-app" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation=" http://xmlns.oracle.com/weblogic/weblogic-web-app http://xmlns.oracle.com/weblogic/weblogic-web-app/1.4/weblogic-web-app.xsd"> <wls:context-root>/{context-root}</wls:context-root> <wls:container-descriptor> <wls:prefer-application-packages> <wls:package-name>org.slf4j.*</wls:package-name> <wls:package-name>org.springframework.*</wls:package-name> </wls:prefer-application-packages> </wls:container-descriptor> </wls:weblogic-web-app>
DevOps开发模式中最受欢迎的开源工具除了Docker,就是Jenkins了。咱们使用Jenkins的做用主要包括:从GitHub上拉取源代码,装载实现自动打包部署的脚本,和提供可视化界面让用户简单操做。
Jenkins的安装方法不少,我是直接下载Jenkins的War,部署在Tomcat上运行。从Jenkins官网 https://jenkins.io/download/ 下载Jenkins.war,放在Tomcat的 webapps目录下,重启Tomcat。
登入http://localhost:8080/jenkins,进入Jenkins初始化页面,第一次启动时间可能有点长,耐心等待。进入成功后会看到以下画面,按提示路径打开密码文件,输入密码(页面中有提示密码所在位置):
得到,输入密码后,点击右下角的Continue 按钮,jenkins开始安装,短暂时间后,会弹出以下界面,选择安装的插件
通常推荐使用官方推荐默认安装的插件,肯定后,进入插件下载安装页面,等待下载安装…
登陆成功后会让你设置密码
设置密码,登陆进去后即进入Jenkins主页面
点击左上侧系统管理,进入Jenkins基本系统设置。咱们先配置“全局工具配置”:
主要是配置 JDK、Git、Maven ,配置本地安装的路径,若是本地还没有安装请先自行安装。
接下来配置“系统配置”:
此处只讲配置GitHub Servers,主要用于经过Webhooks 自动构建发布项目。 在系统配置页面找到GitHub Servers,以下图 :
勾上 Specify another hook URL for GitHub configuration ,它会默认生成一个地址,ip 是内网ip, 我这里是使用本身的github, 因此把ip 改为外网ip了,端口建议和jenkins端口保持一致。
登陆我的githup网站,选择一个项目,在Settings中的webhooks中点击 Add Webhook (我这里的页面是已经配置了一个)
这里咱们配置Payload URL,即GitHub Servers中生成的地址。而后选择触发事件,案例中是选择了push事件,即没当github收到新的push都会告知jenkins. 而后点击保存便可。(ps:保存后页面上可能有红色叹号,只要地址正确便可,可忽略,触发一次后即显示正常。)
在首页点击“New任务”,新建一个任务,选择 maven项目。若是选项中没有,说明Jenkins没有安装对应的插件,能够在“系统管理-插件管理-可选插件”中搜索 Pipeline Maven Integration 或者 Maven Integration plugin,安装对应的插件。
建立的任务中,我须要将具体的业务字段经过参数传入,并配置从GitHub上拉取代码,执行打包部署的脚本。
保存后,点击运行该任务,就能从日志中看到运行结果
根据咱们在Jenkins上任务的配置来看,在执行该任务时须要传入参数并运行脚本。从第二章War结构中咱们分析出来,若是要生成可部署的War包,须要三个部分:classes文件夹、lib文件夹、web.xml和weblogic.xml。因此该脚本要作的事情就分为如下一个步骤:
这里列出我写的打包的Shell脚本,具体的部署脚本请参考(如何优雅的在weblogic上部署spring-boot)
#!/bin/bash v_job_name=$1 v_project_name=$2 v_startup_name=$3 v_startup_path=$4 v_df_startup_path=$5 v_application_template=$6 echo 'shell begin --------------------------------------------------' #打jar包 cd /root/.jenkins/workspace/${v_job_name}/${v_project_name}/ mvn clean package cd /u01/devops echo 'git代码生成jar包' #生成job目录 rm -rf package/${v_job_name} mkdir package/${v_job_name} mkdir package/${v_job_name}/jar package/${v_job_name}/temp package/${v_job_name}/war package/${v_job_name}/war/WEB-INF #获取jar包,复制到jar文件夹 --package v_jar_path=`find /root/.jenkins/workspace/${v_job_name}/${v_project_name}/target -name ${v_project_name}"*.jar"` v_jar_name=`basename ${v_jar_path}` cp $v_jar_path package/${v_job_name}/jar #解压jar cd package/${v_job_name}/jar jar -xvf ${v_jar_name} cd ../../../ #获取class目录 v_jar_class=`find package/${v_job_name}/jar -name classes -type d` echo '获取jar包中classes' #生成启动类 #找到 启动类模板 v_startup_java=`find startup/springboot-weblogic-template/ -name "SpringbootWeblogicTemplateApplication.java"` v_startup_java_addr=`dirname ${v_startup_java}` #替换启动类 cp ${v_application_template} ${v_startup_java} #替换启动类中参数 sed -i "s/"SpringbootWeblogicTemplateApplication"/"${v_startup_name}"/g" ${v_startup_java} sed -i "s/"devops.weblogic.springbootweblogictemplate"/"${v_startup_path}"/g" ${v_startup_java} sed -i "s/"devops.weblogic"/"${v_df_startup_path}"/g" ${v_startup_java} mv $v_startup_java $v_startup_java_addr"/"${v_startup_name}".java" #编译 cd startup/springboot-weblogic-template mvn clean compile cd ../../ #找到 启动类.class v_startup_class=`find startup/springboot-weblogic-template -name ${v_startup_name}".class"` rm -f startup/target/${v_startup_name}".class" cp $v_startup_class startup/target/ #还原 启动类.java rm -f ${v_startup_java_addr}"/"${v_startup_name}".java" cp /u01/devops/startup/applicationTemplate/SpringbootWeblogicTemplateApplication.java $v_startup_java_addr echo '生成启动类的class文件' #获取web.xml、weblogic.xml cp template/web.xml package/${v_job_name}/temp/web.xml cp template/weblogic.xml package/${v_job_name}/temp/weblogic.xml sed -i "s/{context-root}/${v_project_name}/g" package/${v_job_name}/temp/weblogic.xml sed -i "s/{application-class}/${v_startup_path}"."${v_startup_name}/g" package/${v_job_name}/temp/web.xml echo '替换web.xml、weblogic.xml' #替换war包中clsss/、lib/、web.xml、weblogic.xml cp -rf $v_jar_class package/${v_job_name}/war/WEB-INF/ cp -rf template/lib package/${v_job_name}/war/WEB-INF/ cp package/${v_job_name}/temp/web.xml package/${v_job_name}/war/WEB-INF/ cp package/${v_job_name}/temp/weblogic.xml package/${v_job_name}/war/WEB-INF/ #替换启动类 v_war_startup_class=`find package/${v_job_name}/war -name ${v_startup_name}".class"` rm -f $v_startup_class cp startup/target/${v_startup_name}".class" $v_war_startup_class #生成war包 cd package/${v_job_name}/war jar -cvfM0 ${v_startup_name}.war . echo '生成war包' tree echo 'shell end --------------------------------------------------'
前文说到,打包时lib文件是本身准备的,主要是选择低一点版本的Spring Boot依赖Jar等。你也能够根据具体项目增长或调整lib中的Jar包。例如:
├── aopalliance-1.0.jar
├── aspectjweaver-1.8.13.jar
├── classmate-1.0.0.jar
├── commons-codec-1.6.jar
├── commons-fileupload-1.2.1.jar
├── fastjson-1.2.4.0.jar
├── hibernate-validator-5.0.3.Final.jar
├── jackson-annotations-2.5.0.jar
├── jackson-core-2.5.0.jar
├── jackson-databind-2.5.0.jar
├── java-jwt-3.1.0.jar
├── jboss-logging-3.1.1.GA.jar
├── jcl-over-slf4j-1.7.11.jar
├── jjwt-0.6.0.jar
├── joda-time-2.3.jar
├── jul-to-slf4j-1.7.11.jar
├── log4j-over-slf4j-1.7.11.jar
├── logback-classic-1.1.3.jar
├── logback-core-1.1.3.jar
├── mpaas-sso-oam-1.0.0.jar
├── oamasdk-api-11.1.1.5.0.jar
├── ojdbc6-11.2.0.1.0.jar
├── query-0.0.30-SNAPSHOT.jar
├── restutil-1.jar
├── slf4j-api-1.7.11.jar
├── snakeyaml-1.13.jar
├── spring-aop-4.2.5.RELEASE.jar
├── spring-beans-4.2.5.RELEASE.jar
├── spring-boot-1.1.12.RELEASE.jar
├── spring-boot-autoconfigure-1.1.12.RELEASE.jar
├── spring-boot-legacy-1.0.2.RELEASE.jar
├── spring-boot-starter-1.1.12.RELEASE.jar
├── spring-boot-starter-logging-1.1.12.RELEASE.jar
├── spring-boot-starter-web-1.1.12.RELEASE.jar
├── spring-context-4.2.5.RELEASE.jar
├── spring-core-4.2.5.RELEASE.jar
├── spring-expression-4.2.5.RELEASE.jar
├── spring-web-4.2.5.RELEASE.jar
├── spring-webmvc-4.2.5.RELEASE.jar
└── validation-api-1.1.0.Final.jar
我在Jenkins的传入参数中,是有设置启动类模板选项的,虽然大部分部署在WebLogic的启动类都是这样:
package devops.weblogic.springbootweblogictemplate; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.boot.builder.SpringApplicationBuilder; import org.springframework.boot.context.web.SpringBootServletInitializer; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; import org.springframework.web.WebApplicationInitializer; @EnableAutoConfiguration @Configuration @ComponentScan public class SpringbootWeblogicTemplateApplication extends SpringBootServletInitializer implements WebApplicationInitializer { public static void main(String[] args) { SpringApplication.run(SpringbootWeblogicTemplateApplication.class, args); } @Override protected SpringApplicationBuilder configure(SpringApplicationBuilder builder) { return builder.sources(SpringbootWeblogicTemplateApplication.class).showBanner(false); } }
可是有些代码中启动类要有其余的设置。例如:有文件上传的程序须要部署在WebLogic上时,spring-boot自带的org.springframework.web.multipart.MultipartFile
和Multipart产生冲突,这时须要在申明Bean:
package devops.weblogic.springbootweblogictemplate; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.boot.autoconfigure.web.MultipartAutoConfiguration; import org.springframework.boot.builder.SpringApplicationBuilder; import org.springframework.boot.context.web.SpringBootServletInitializer; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; import org.springframework.web.WebApplicationInitializer; import org.springframework.web.multipart.MultipartResolver; import org.springframework.web.multipart.commons.CommonsMultipartResolver; @EnableAutoConfiguration(exclude = {MultipartAutoConfiguration.class}) @Configuration @ComponentScan(basePackages = {"devops.weblogic", "com.definesys.mpaas"}) public class SpringbootWeblogicTemplateApplication extends SpringBootServletInitializer implements WebApplicationInitializer { public static void main(String[] args) { SpringApplication.run(SpringbootWeblogicTemplateApplication.class, args); } @Override protected SpringApplicationBuilder configure(SpringApplicationBuilder builder) { return builder.sources(SpringbootWeblogicTemplateApplication.class).showBanner(false); } @Bean(name = "multipartResolver") public MultipartResolver multipartResolver() { CommonsMultipartResolver resolver = new CommonsMultipartResolver(); resolver.setDefaultEncoding("UTF-8"); resolver.setResolveLazily(true); resolver.setMaxInMemorySize(409600); resolver.setMaxUploadSize(50 * 1024 * 1024); return resolver; } }