想一想,若是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; } }