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.class

2、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包所需要的無非就三個部分:

  1. classes文件夾:存放了編譯後的Java代碼
  2. lib文件夾:運行在服務器上時依賴的Jar包
  3. web.xml、weblogic.xml:web.xml是War包中必須,而weblogic.xml則是部署在WebLogic服務器上需要的。

那麼如果我們要自己生成War包,就需要生成這三個部分:

  1. classes文件夾,可以很明顯看到Jar包結構中也有一樣的文件夾,而且class文件的數量也一樣。那麼我們得出,可以從Jar包中直接獲取第一部分--classes文件夾。(除了啓動類的class,後續會主要講解)
  2. lib文件夾在Jar包結構中也能找到,但是Jar包路徑中lib文件夾下依賴的Jar包太多了,和War包路徑中lib文件夾對應不起來。而實際上Spring Boot項目生成的War包中依賴的Jar包基本上是固定的那些,而且爲了適用於WebLogic 11g,對於一些Jar包的版本是有要求的。所以我這裏使用自己準備的lib文件夾。
  3. web.xml和weblogic.xml,在參考了同事寫的文章之後(鏈接)。發現可以自己生成這兩個配置文件,只需要修改對應項目的路徑和名稱就行。我這裏粘貼同事使用的兩個文件的模板,具體業務系統中只需要替換掉對應的變量{application-class}和{context-root}即可:

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-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>

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初始化頁面,第一次啓動時間可能有點長,耐心等待。進入成功後會看到如下畫面,按提示路徑打開密碼文件,輸入密碼(頁面中有提示密碼所在位置):
img
獲得,輸入密碼後,點擊右下角的Continue 按鈕,jenkins開始安裝,短暫時間後,會彈出如下界面,選擇安裝的插件
img
一般推薦使用官方推薦默認安裝的插件,確定後,進入插件下載安裝頁面,等待下載安裝…
img
登錄成功後會讓你設置密碼
img
設置密碼,登錄進去後即進入Jenkins主頁面
img

環境配置

點擊左上側系統管理,進入Jenkins基本系統設置。我們先配置“全局工具配置”:

clipboard.png

clipboard.png
主要是配置 JDK、Git、Maven ,配置本地安裝的路徑,如果本地尚未安裝請先自行安裝。

接下來配置“系統配置”:
此處只講配置GitHub Servers,主要用於通過Webhooks 自動構建發佈項目。 在系統配置頁面找到GitHub Servers,如下圖 :

clipboard.png

勾上 Specify another hook URL for GitHub configuration ,它會默認生成一個地址,ip 是內網ip, 我這裏是使用自己的github, 所以把ip 改成外網ip了,端口建議和jenkins端口保持一致。
登錄個人githup網站,選擇一個項目,在Settings中的webhooks中點擊 Add Webhook (我這裏的頁面是已經配置了一個)

clipboard.png

clipboard.png
這裏我們配置Payload URL,即GitHub Servers中生成的地址。然後選擇觸發事件,案例中是選擇了push事件,即沒當github收到新的push都會告知jenkins. 然後點擊保存即可。(ps:保存後頁面上可能有紅色歎號,只要地址正確即可,可忽略,觸發一次後即顯示正常。)

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章