javascript
DevOps - Spring Boot自动部署到WebLogic
前言
想想,如果Spring Boot的項目在本地IDEA可以直接通過內嵌tomcat的jar運行,而只要往GitHub上提交代碼,就能自動幫你打包成war包部署在項目組的WebLogic服務器上,該減少多少非開發的工作量。這也是DevOps中很重要的一環。本文檔就是針對該設想,做出技術上的嘗試。首先申明這純屬技術上的嘗試研究,不會主動應用在客戶的生產環境架構中。
當我接觸了Spring Boot,就果斷拋棄了Spring MVC。一則我們可以通過各種IDEA非常簡單的生成一個Spring Boot的項目,不像Spring MVC還有麻煩的XML配置文件;二則因為Spring Boot內嵌Tomcat,本地運行時直接運行啟動類就行了,不需要再準備一個Tomcat 服務器,打War包部署。但同時也遇到了其他的問題:
- 本地運行啟動類時根據POM文件,是將其打包成Jar 包運行,無法直接部署在外部服務器上。
- Spring Boot部署在Tomcat上容易,而在重量級服務器上,例如WebLogic上就不那么友好了。而且一些企業應用中服務器還不一定是最新版本的,部署上去會遇到兼容性問題。
我所在的項目使用的服務器就是WebLogic 11g,在受益于Spring Boot開發的靈活性與多元性同時,又浪費了很多無效的時間在解決WebLogic上部署的問題:添加weblogic.xml文件,Jar包沖突等等。最近周末業余時間我做了一些嘗試,簡單實現了以上的需求,現在進入正題吧。
需要準備的基本配置很簡單:GitHub賬號,Jenkins和WebLogic 11g服務器就夠了。如果是企業內網,訪問公網的Maven中央倉庫,可能還需要Nexus搭建Maven私服。內網的具體方案本文檔會討論,但考慮到保密,不會引用具體的實踐操作。
War包分析
上文說到,Spring Boot項目我本地是直接運行Jar包,而部署在WebLogic上的是War包。Jar包和War包的結構有什么關聯和區別呢?這里我新建了一個最簡單的Spring Boot項目,分別生成了Jar和可以部署在WebLogic上的War包:
1、jar包結構
.├── 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.class2、War包結構
.└── 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包所需要的無非就三個部分:
那么如果我們要自己生成War包,就需要生成這三個部分:
web.xml模板(添加了公司倚天項目)
<?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-appxmlns: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-apphttp://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>Jenkins環境搭建
DevOps開發模式中最受歡迎的開源工具除了Docker,就是Jenkins了。我們使用Jenkins的作用主要包括:從GitHub上拉取源代碼,裝載實現自動打包部署的腳本,和提供可視化界面讓用戶簡單操作。
安裝Jenkins
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上拉取代碼,執行打包部署的腳本。
保存后,點擊運行該任務,就能從日志中看到運行結果
Shell腳本
根據我們在Jenkins上任務的配置來看,在執行該任務時需要傳入參數并運行腳本。從第二章War結構中我們分析出來,如果要生成可部署的War包,需要三個部分:classes文件夾、lib文件夾、web.xml和weblogic.xml。所以該腳本要做的事情就分為以下一個步驟:
這里列出我寫的打包的Shell腳本,具體的部署腳本請參考(如何優雅的在weblogic上部署spring-boot)
#!/bin/bashv_job_name=$1 v_project_name=$2 v_startup_name=$3 v_startup_path=$4 v_df_startup_path=$5 v_application_template=$6echo '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包'treeecho 'shell end --------------------------------------------------'備注說明
lib文件夾
前文說到,打包時lib文件是自己準備的,主要是選擇低一點版本的Spring Boot依賴Jar等。你也可以根據具體項目增加或調整lib中的Jar包。例如:
- 因為使用了aop才新增了aopalliance-1.0.jar和aspectjweaver-1.8.13.jar
- 因為使用了jwt才新增了jjwt-0.6.0.jar和java-jwt-3.1.0.jar,并將jackson-annotations-2.3.5.jar、jackson-core-2.3.5.jar、jackson-databind-2.3.5.jar升到了jackson-annotations-2.5.0.jar、jackson-core-2.5.0.jar、jackson-databind-2.5.0.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);}@Overrideprotected SpringApplicationBuilder configure(SpringApplicationBuilder builder) {return builder.sources(SpringbootWeblogicTemplateApplication.class).showBanner(false);} }但是有些代碼中啟動類要有其他的設置。例如:有文件上傳的程序需要部署在WebLogic上時,spring-boot自帶的org.springframework.web.multipart.MultipartFile
和Multipart產生沖突,這時需要在申明Bean:
內網問題
總結
以上是生活随笔為你收集整理的DevOps - Spring Boot自动部署到WebLogic的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: STM32F407的时钟配置
- 下一篇: 互联网分布式微服务云平台规划分析--服务