前言
之前的文章說過 由 PHP 轉到 Java 之后,非常不適應的一點就是代碼部署過程耗時長,調試不便,雖然可以使用 debug,但有時候還是需要修改代碼,重新部署測試機系統,整個流程需要:
- 使用 mvn 命令將項目打成 war 包,耗時 1 min;
- 從開發機向測試機上傳 war 包,公司內使用無線局域網,上傳速度峰值只有 1M 不到,而且很不穩定,面對 100M+ 的 war 包,有點力不從心,此步驟耗時 2.5 min;
- 服務端重啟 docker 進程,耗時 1 min;
再加上需要兩台機器切換操作,步驟之間不連貫,需要在邊上看着進度,以及時操作下一步。可以說,等到想要的代碼上傳到測試機運行,花兒都謝了。
作為一個懶人,迫切地需要簡化一下流程,雖然可能達不到像 PHP 一樣秒傳文件立即生效,也要盡量快且方便地部署測試包,別操這么多心。本文就介紹我是怎么一步步優化測試部署流程的。
文章歡迎轉載,請尊重作者勞動成果,帶上原文鏈接:http://www.cnblogs.com/zhenbianshu/p/8733103.html
nc 時代
剛入職時,對 Java 的部署相關一臉懵逼,有同事給了一個腳本和兩條命令,是為最原始的“自動部署系統”:
- 先在測試機上執行腳本,腳本會啟用一個 nc 接收進程,監聽某一個端口,命令為
nc -4l xxPort > ROOT.war
; - 自己在開發機上執行一條 mvn 命令,將項目打包,命令為
mvn clean package project
; - 再在開發機上執行 nc 上傳命令,連接測試機 IP 和端口,以打好的 war 包為輸入流
nc testIp xxPort < test-1.0.0.war
; - 傳輸完 war 包后,腳本會自動重啟 docker 機,重啟完成后就可以進行測試了。
nc 是 NetCat 的簡稱,這個小工具用於同步兩台服務器間的文件,使用時,先在接收端監聽一個端口並指定輸出文件,再在發送端連接 IP 和端口,並指定輸入流, nc 命令很簡單,網絡上資料也很多,這里不再多提了。
這個腳本雖然比全部手動好了一些,能幫我少輸兩個命令(nc 服務端、重啟命令),可是時間上並沒有縮短,可是烏龜似的上傳速度真的不能忍,這時我開始想着怎么加速上傳。
rsync “加速”上傳
其實一開始我是想從硬件方面解決這個問題的,即使用網線。為此,買了一個網線轉接頭和一段網線,可是通過同事的設備測試發現轉接頭和網線都沒問題,可是接到一塊就不匹配(圍笑)。
窮則思變,接着我考慮從軟件方面解決這個問題。問了幾個同事后,發現有的同事在用 rsync 同步文件,可是 rsync 同步文件的單位不是文件
嗎?看了同事演示的上傳后,感覺心態崩塌,不好好讀文檔的后果啊,走了好多彎路。
這里簡要介紹一下 rsync 的使用:
服務端
服務端需要啟動一個 rsync daemom 進程監聽某一端口,默認配置文件在 /etc/rsyncd.conf
,以 module
為單位進行用戶認證、權限校驗、目標文件夾等配置,一個常見的 rsyncd 配置如下:
# general conf
port=873 # 監聽端口
max connections=500
log file=/var/log/rsyncd.log
pid file=/var/run/rsyncd.pid # pid 文件
# module 可多個
[zbs]
path = /data1/zbs # zbs模塊的根目錄
read only=no
use chroot=no
uid=root
gid=root
auth users=zbs // 要進行用戶認證的用戶名
secrets file=/etc/rsyncd.scrt # 用戶名對應的密碼存放文件,每行一個,都是以 "zbs:password" 的形式
ignore errors
exclude = .git/ # 排除掉 .git 文件夾
客戶端
而在客戶端,我們只需要使用 rsync [-option] fileOrDir rsync://{user}@{host}:{port}/{moduleName}/dir
就可以將本地文件同步到服務端了。
至於密碼,可以使用 --password-file=/path/to/pwdFile
的形式,也可以在調用 rsync 命令之前設置環境變量:export RSYNC_PASSWORD=XXXX
;
至於 rsync 的同步算法, 推薦陳皓大神的文章:RSYNC 的核心算法
rsync 解決了上傳速度的問題,但是又引入了新的問題:我必須等着上傳結束,並且上傳結束后還要登陸測試機手動重啟 docker 服務,挺不方便的。
修改 rsync,添加回調選項
這時我開始打 rsync 源碼的主意了,rsync 是一個開源軟件,我考慮幫它加一個參數,讓它幫我在文件上傳結束后自動執行一些命令。
說做就做,從 rsync官網 下載到 rsync 的源碼開始查看並動手修改。rsync 的源碼代碼量還是挺大的,不過修改它我們不需要通讀,只修改讀取參數並使用就行了。我將這個問題分為兩個步驟:
- 讀取到 callback 參數的值;
- 上傳結束后調用 callback 參數的值;
首先在 proto.h
文件里添加函數聲明: char *lp_callback(int module_id);
讀取參數的相關代碼在 load_param.c
文件內,首先添加變量聲明、設置默認值,最后添加參數調用函數。
服務端文件同步的代碼在 clientserver.c
文件內,主體是 rsync_module
函數,前面的一系列操作如用戶認證、權限校驗等我們可以不必管,找到最后一步,在其調用下一次同步函數前 添加如下代碼(解釋在注釋中):
char * callback = lp_callback(i); // 讀取 callback 參數
if (callback != NULL && strlen(callback) != 0) {
char cmd[strlen(callback) + 2];
strcpy(cmd, callback);
strcat(cmd, " &"); // system 命令會阻塞,需要在命令上添加 & 讓它后台執行
system(cmd); // 使用 stdlib 的 system 執行 callback 命令
}
修改后的源碼見:Github-zhenbianshu-rsyncCallback
這樣,我給自己上傳用的 module 添加一個腳本作為 callback,在每次上傳完后,都會執行這個 callback 腳本,腳本里我可以配置上服務的重啟,自動部署就實現了。
docker-compose tomcat 自動部署
其實 tomcat 是可以自動部署的,需要配置 server.xml
的 Host 元素,將 autoDeploy 屬性置為 true,文檔:Tomcat Web Application Deployment。
可是我們的服務是基於 docker-compose 進行部署的,如果修改 server.xml 還需要將文件映射到 docker image 里。
其中 docker 可以這么配置:
FROM tomcat:7-jre8
COPY server.xml /usr/local/tomcat/conf/
docker-compose 可以在 yml 配置文件里添加如下配置:
image:
tomcat-base
volumes:
- ./path/server.xml:/usr/local/tomcat/conf/server.xml
- ./path/webapps:/data1/project/webapps
這樣,每當上傳了新的 war 包,tomcat 就會自動監測到並重新部署服務;
此時,還有一個需求, war 包同步完成,重啟完成后我不知道,得隨時關注 tomcat 的服務日志,以盡快得知重啟結果,及時測試,如果服務重啟完就立即告訴我就最好了。
添加通知
此時,我修改的 rsync 就有了作用了,使用 callback 參數在測試機啟動一個腳本以監測 tomcat 的服務日志,服務重啟完成后會輸出 Server startup in xxx ms
,如果監測到有新的 log 輸出,則發送一個通知告訴我。
callback 參數配置的腳本類似於:
#!/bin/bash
docker-compose stop -t 0
`rm /data1/project/webapps/ROOT -rf`
sleep 1
docker-compose start
sleep 5 # 這里等待一會,使大量 tomcat 日志覆蓋掉上一次重啟的結果日志
date=`date "+%Y-%m-%d"`
catalina_log="/data1/project/logs/catalina.$date.log"
while : # 重復檢測日志最后一行,直到輸出了重啟成功的標識
do
finish=`tail -n 1 $catalina_log | grep 'Server startup'`
if [ -n "$finish" ]
then
break
fi
sleep 0.1
done
`curl -u "user:password" -d "uid=5715965217&text=succ" "messages/new.json"` # 調用接口發送通知
其實在測試機啟動一個守護進程用來實時監測日志也是可以的,但是需要處理日志的新舊邏輯。
至於通知,有很多工具可以使用,微博、QQ、微信、短信等通訊工具都提供有對外的 http 接口,這個可以依各人喜好選擇使用。
小結
最后把開發機上的 mvn 打包命令和 rsync 同步命令也包裝成腳本,如下所示:
#!/bin/bash
mvn clean -DskipTests=true package -Pwar -am -pl project
file=`find /path/to/project/target -name "*.war"`
export RSYNC_PASSWORD=123456
rsync -avz $file rsync://zbs@IP:PORT/zbs/ROOT.war
再給腳本添加一個 alias 別名 alias test="myshell.sh"
,真正的一鍵部署就完成了,在部署測試環境時,只需要在項目目錄下輸入一條命令 test
就開始自動部署了,這時候可以放手去喝杯水或做些其他事,等收到消息通知后,回來繼續測試即可。
部門正在搭配 git 系統做自動部署測試系統,非常期待 push 過代碼后就可以迅速測試的場景。果然,懶才是第一生產力啊~
關於本文有什么問題可以在下面留言交流,如果您覺得本文對您有幫助,可以點擊下面的 推薦
支持一下我,博客一直在更新,歡迎 關注
。