天天看點

生産環境項目程式部署方式的改進和深度思考

首先,以Java項目為例,部署方式有很多,我總結如下:

  • Tomcat等:所有檔案放在war包中部署
  • WAS/Weblogic:通常外挂lib,每次隻更新項目自身代碼(ear包或war包)
  • JBOSS/EAP:不同于其他Web Container,JBOSS自創了VFS
  • Executable Fat Jar(自帶Web Container):

        所謂Fat Jar就是所有打包在一個jar中,涉及 jar:jar:file:/path 問題, 需要hack,一些特殊項目可能無法這樣做(spring-boot-maven-plugin是一個常用主流強大的Fat Jar工具,然而Github上還是有一堆的issues)【從某種角度來看,JBOSS的VFS(參見官方文檔)就是為了解決類似這些資源通路問題而設計的重量級解決方案】

  • Executable Jar(自帶Web Container)+ 外挂其他檔案:

        為了彌補Fat Jar的不足,引入外挂檔案,一方面解決特殊問題,另一方面易于局部更新

  • Docker鏡像(自帶Web Container):

        隻需要将應用的包放到指定目錄即可。【顯然,這是比上面所有方案更重量級的方案,直接套一個終極外殼,比JBOSS套一個VFS還要強得多】

我的分析如下:

    從打包和部署的角度來說,Fat Jar很臃腫(所有項目檔案打包在一起),動辄50M以上甚至100多M。我以前長期使用WAS/Weblogic,一般把公共的jar lib分類,然後項目直接加載即可,應用包更輕量(實際上我們頻繁上線,但是基礎lib幾乎不怎麼更新,lib更新的次數占總上線次數的5%不到)。

    Fat jar更新不友善,假設我想更新一個lib包(xx-util.jar)或者js、html檔案,那就得重新打Fat Jar包,同理Docker鏡像也是如此。

    回過頭來,我們先不看技術方案,先來想清楚我們的訴求:

(以下不分順序)

  • 部署友善(主要考慮全自動化部署的場景)
  • 部署靈活(易于配置和局部更新,容易備份和復原)

    首先,大前提,本文針對DevOps自運維團隊,不考慮交給運維部門這種低級的運維方式。

    項目和伺服器完全自主可控,隻需要考慮的是怎麼高效和安全。根據我多年檔案維護的經驗,建議将檔案分類:冷熱和大小。将經常更新的檔案集中在一起(zip、war、jar包中),每次整體更新,而不經常更新的檔案(第三方庫、依賴檔案)隻做增量同步更新(增删改)。

    針對一般Java項目,第三方依賴包,放置在外部,配置檔案,放置在外部,其他檔案打包在一起。每次更新時,先各個部分分别對比,如無更新,則跳過這部分。對比之前,先拉一個檔案清單,是對比清單而不直接對比檔案。

    簡單實作方式,就是寫一個腳本,自動擷取兩邊各部分的清單,自動對比出要更新的檔案清單,然後tar歸檔一個更新包、一個備份包,然後更新所有檔案,更新有三種:新增、替換、删除,還原時反過來:删除、替換、新增。

    外加一點,大量而細小(2k~2w個,平均每個10KB以内)的檔案打包成一個大檔案維護——雖然通常的項目都不存在這種情況,但可能有一些奇葩,例如NodeJS的node_modules依賴,可能有這種檔案(輕松上2萬)。為了提高效率,可以特殊處理。一種方案,在拉清單對比之前,先将node_modules下面的檔案夾各自歸檔(用相同工具和算法)。這樣處理之後,項目的檔案數可能就從2萬變成了2千,在對比和維護時就簡單了許多。另外,需要吐槽一句,NPM的設計簡直就是垃圾,可以學習一下Maven,設定本地倉庫(local repo)且将公共的依賴庫打包(zip、jar)并分類/分版本集中管理,然後項目中用到哪些庫就引用(軟引用、符号連結)就行了,根本不需要每個項目單獨維護一份檔案,而且檔案還是零散的、沒有歸檔壓縮的,這導緻所有前端項目的依賴檔案成千上萬甚至十萬,即便是SSD硬碟,要處理這麼多小檔案,也很累。

    另外,檔案的備份和存取,可以單獨寫一個管理系統,後端對接分布式對象存儲。例如備份和擷取:

curl 172.168.12.2

/api/backup

-F

file

=@

/tmp/a9d8/zoa-main

.

tar

curl 172.168.12.2

/api/getfile

-X POST -d

"name=zoa-main.tar&version=v20170210s01"

    備份邏輯系統會自動處理,當然curl的時候也可以傳參數去控制。這樣一來,其實一個腳本就可以完全實作自動化,而且部署非常靈活。當然,整個部署過程,也可以由智能運維系統自動控制,操作人輸入參數、點一下按鈕就行了,也可以提前準備好,定時自動執行。

    再回到前面說的Java項目問題,顯然,我贊成Tomcat + 檔案夾部署,或者Fat jar+外挂檔案,對自動化來說,兩者沒什麼差別,是以我優先選擇Tomcat + 檔案夾部署方式。

    有的人可能會說,全量包部署更好,全量包部署更簡單一些,不需要這麼麻煩的對比和備份。而且全量包主要的缺點是體積較大,但是100多MB也不算大,内網傳輸很快的。至于友善些、安全性,都是差不多的,都需要靠規章和自動化系統去保障。

    我的回答是,沒錯,其實兩者差别不大,但即便如此,我還是不建議使用全量包。說全量包部署更簡單,那是基于傳統手工運維的思路,對自動化運維來說,隻能說初期實作要費一番精力,後面其實都是自動化的,都是點一下按鈕就執行,簡單程度是一樣。體積較大,雖然100多MB算不上什麼,但是系統多了,而且備份次數多了,節省的時間和空間還是很可觀的。安全性上,全量包和增量包,都有可能出錯,但是增量包出錯一般隻是局部影響,而全量包出錯可能影響很多部分。

Docker部署專題

    前面說了,Docker部署很有特點,一是因為Docker鏡像自帶作業系統,二是因為Docker獨特的檔案系統、鏡像分層,可以複用檔案。是以,使用Docker部署,有很大的優化空間。

    根據上述指導思想,需要考慮檔案冷熱和大小因素,重點考慮上述“Tomcat + 檔案夾部署,或者Fat jar+外挂檔案”的方式部署項目(而非全量包)。将不經常變動的檔案封裝在基礎層中,例如

COPY target/base-lib/  /app/lib/

然後再拷貝 易變動 的部分:

COPY target/app-lib/  /app/lib/

COPY target/*.jar  /app/