Jenkins自動化部署SpringBoot多模組專案
大家好,我是一航;
上週的時候,跟大家分享了透過Jenkins 一鍵部署SpringBoot專案: 還在手動發包?手把手教你 Jenkins 自動化部署SpringBoot
這一週時間,也有不少朋友透過微信在和我交流Jenkins環境搭建的一些問題,期間就有一個朋友反饋到多模組部署的一個問題,說我寫的
jenkins_restart.sh
指令碼,
在多模組部署的時候,沒辦法檢測到未更新的模組;
什麼意思呢?
舉個例子,加入一個專案,分了10個小模組,類似於下圖:
本次修改,只是模組①修復了1個Bug,其他9個都沒有變動,那麼編譯打包整個專案之後,也只需要更新模組①即可,其他的9個模組完全可以不做任何操作,要做到這一需求,就需要在這10個模組中找出那些模組更新了,那些沒有更新;上篇文章中採用的方案是: 計算 jar 包的MD5,如果MD5值一樣,說明沒有更新;
但這是一個方案,是有問題,下面就一起來分析一下問題原因;
以及如何解決多模組的自動部署問題?
前文中我寫的指令碼,是經過仔細測試的,但這位朋友說到這個問題時,我還很仔細的說沒有問題,能檢測到;
經過反覆溝通之後,讓我有點不自信;按著這位朋友說的問題點,測試了一番;確實存在這個問題, 就算程式碼沒有做任何的改動,Maven打出來的Jar包MD5值都不一樣,只是當時指令碼測試的策略不對;我就只在第一次編譯的時候打了10個模組的包,之後只是測試指令碼,為了追求速度,就沒有再去編譯各個模組了,導致後面所有的指令碼測試,都是用的第一次打出來的Jar,所以MD5值都一樣;因此整個過程,絲毫沒發現有啥問題。
1 問題復現
MD5 判斷檔案是否改變,思路似乎沒有任何問題;程式碼既然沒做任何改變,所有檔案結構目錄也相同,那按理說打出來的Jar包的MD5值應該是一樣的,但為什麼會有問題呢?為了驗證這個問題,對專案連續打兩次包,分別得到兩個相同大小的
a.jar
和
b.jar
;然後做了MD5計算,發現確實不一樣:
然後Beyound對兩個包進行比較,發現 除了修改時間不同,檔案內容也都是一摸一樣的;
這是為啥呢?
Zip測試及原因分析
Java打出來的Jar包格式是以zip檔案格式作為基礎,為了方便,我們用Zip包做一下測試;
準備了2個相同內容的測試檔案
a.txt
和
b.txt
,裡面儲存相同的內容:
123
;先對txt檔案進行MD5值計算,然後將兩個檔案打包成zip之後,再計算MD5值;
可以看出,不壓縮前,
a.txt
和
b.txt
的MD5是一樣的;壓縮之後的zip包,MD5值就不同了;同時我們再看一下檔案大小,不壓縮前,檔案只有4位元組大小,可壓縮之後反而變成更大的164位元組;只能說明
壓縮的時候,還被新增了其他的資訊;
經過查閱,在這篇文章中找到了原因:https://adoyle.me/blog/why-zip-file-checksum-changed.html
Zip在壓縮的時候,會將將檔案的 access time寫入到壓縮包中,壓縮包裡面雖然儲存的檔案內容雖然是一致的,但由於時間不同,導致最終壓縮包的MD5值也就不一致;因此,jar 包所面臨的問題就屬於類似的情況。
2 解決方案
既然知道包裡面的檔案都是一樣的,只是由於壓縮帶來的問題,我們完全可以換個思路來解決,將Jar包解壓之後,判斷各個檔案是否發生變化,同樣也能夠校驗出來,過程如下:
-
只用
unzip
命令解壓Jar包unzip app.jar -d /tmp/jar_unzip_tmp
-
透過
find
命令查詢解壓目錄下的所有檔案並計算MD5值find /tmp/jar_unzip_tmp -type f -print | xargs md5sum > ./jar_files
# 上面的這條命令等價於下面這個 for迴圈
# for file in `find /tmp/jar_unzip_tmp`
# do
# if [ -f $file ]; then
# echo $file
# `md5sum $file >> ./jar_files`
# fi
# done得到的
jar_files
;左側表示檔案的MD5值,右側為檔案的路徑;如果檔案內容發生變化,左側MD5就會不同,如果是結構/目錄發生變化,右側的詳細路徑就會不一樣; -
計算詳情列表(jar_files)對應的MD5值
如果程式碼發生變化、目錄結構發生變化,得到的檔案詳情列表就是產生差異,那根據詳情列表得到的MD5值也就不同了
-
沒有或者與前一次不一樣
發生變化,需要更新重啟
-
MD5校驗一致
未發生變化,跳過
-
3 Jenkins 多模組自動構建
本文的主要目的是:最佳化多模組的自動化構建,能感知變化,只自動部署已經修改的模組;
透過上面的原因分析以及解決方案梳理,下面就來調整一下相關的指令碼;
以下的內容是基於上一篇文章《 還在手動發包?手把手教你 Jenkins 自動化部署SpringBoot》的改進,如果還沒有看過前文,麻煩稍微花點時間閱讀一下,再繼續往下看;
SSH方式最佳化
SSH的方式,主要的修改是在
jenkins_restart.sh
指令碼上,當Jar被傳到執行服務,執行
jenkins_restart.sh
指令碼啟動各個模組的時候,解壓檢測,變化的就重啟,沒變的就跳過
-
指令碼
指令碼的每行都加了註釋,沒什麼特別的地方,實現的也就是上面 解決方案的四個步驟
# !/bin/sh
# JDK的環境變數
export JAVA_HOME=/usr/local/jdk-11.0.14
export PATH=$JAVA_HOME/bin:$PATH
# 基礎路徑,由引數傳入
# 多模組的時候,需要在路徑中使用*統配一下多模組
# 比如/opt/ehang-spring-boot是多模組,下面由module1和module2
# 那麼執行shell的時候使用:sh restart.sh /opt/ehang-spring-boot/\* 注意這裡的*需要轉義一下
JAR_BATH=$1
echo "基礎路徑:"$JAR_BATH
JAR_PATH=${JAR_BATH}/target/*.jar
# 臨時的解壓目錄
JAR_UNZIP_PATH=/tmp/jar_unzip_tmp
# 獲取所有的JAR 開始遍歷
for JAR_FILE in $JAR_PATH
do
if [ -f $JAR_FILE ]
then
echo ">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>"
echo "JAR路徑:"$JAR_FILE
# JAR_FILE_MD5= ${JAR_FILE}.md5
# 解壓目錄的檔案列表詳情及MD5
JAR_FILES_INFO=${JAR_FILE}_files
# 詳情列表檔案的MD5詳細
JAR_FILES_INFO_MD5=${JAR_FILES_INFO}.md5
# 刪除解壓後的臨時資料夾 避免之前的快取導致解壓失敗
rm -rf $JAR_UNZIP_PATH
# 解壓檔案
unzip $JAR_FILE -d $JAR_UNZIP_PATH
# 遍歷解壓目錄,計算每個檔案的MD5值及路徑 輸出到詳情列表檔案中
find $JAR_UNZIP_PATH -type f -print | xargs md5sum > $JAR_FILES_INFO
# 上面的這條命令等價於下面這個 for迴圈
# for file in `find $JAR_UNZIP_PATH`
# do
# if [ -f $file ]; then
# echo $file
# `md5sum $file >> $JAR_FILES_INFO`
# fi
# done
# 用於標記是否需要重啟的標識
RESTART=false
# 判斷MD5檔案是否存在,存在就校驗MD5值
if [ -f $JAR_FILES_INFO_MD5 ]; then
# 校驗MD5
md5sum --status -c $JAR_FILES_INFO_MD5
# = 0表示校驗成功 =1 表示校驗失敗
if [ $? = 1 ];then
echo "MD5校驗失敗,安裝包已經更新!"
RESTART=true
else
echo "與前一次的MD5匹配成功,說明安裝包沒有更新!"
fi
else
echo "沒有MD5值,說明是第一次啟動"
RESTART=true
fi
# 獲取程式號 判斷當前服務是否啟動;如果Jar沒變,但是服務未啟動,也需要執行啟動指令碼
PROCESS_ID=`ps -ef | grep $JAR_FILE | grep -v grep | awk '{print $2}'`
# 如果不需要重啟,但是程式號沒有,說明當前jar沒有啟動,同樣也需要啟動一下
if [ $RESTART == false ] && [ ${#PROCESS_ID} == 0 ] ;then
echo "沒有發現程式,說明服務未啟動,需要啟動服務"
RESTART=true
fi
# 如果是需要啟動
if [ $RESTART == true ]; then
# kill掉原有的程式
ps -ef | grep $JAR_FILE | grep -v grep | awk '{print $2}' | xargs kill -9
#如果出現Jenins Job執行完之後,程式被jenkins殺死,可嘗試放開此配置項
#BUILD_ID=dontKillMe
#啟動Jar
nohup java -jar $JAR_FILE > ${JAR_FILE}.log 2>&1 &
# =0 啟動成功 =1 啟動失敗
if [ $? == 0 ];then
echo "restart success!!! process id:" `ps -ef | grep $JAR_FILE | grep -v grep | awk '{print $2}'`
else
echo "啟動失敗!"
fi
# 將最新的MD5值寫入到快取檔案
md5sum $JAR_FILES_INFO > $JAR_FILES_INFO_MD5
fi
echo "<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<"
echo ""
fi
done -
測試
Docker 方式最佳化
Docker 的映象 和 ZIP壓縮包有著類似的問題,就算是同一個jar、同一個Dockerfile,連續兩次執行
docker build
構建出來的映象,他的映象ID也是不一樣的;
Docker相比於SSH方式,在操作步驟上,就會存在一些差異,SSH方式是在啟動Jar之前去做校驗;但如果使用Docker的方式,在Jenkins映象構建之前,就需要判斷那些Jar發生了變化,然後只對有變化的Jar包去構建映象,沒有改變的,跳過映象構建;因此,Docker方式主要調整的就是映象構建的指令碼
docker-image-build.sh
;其他指令碼和前文的一樣;
-
指令碼調整
# 該指令碼是用於單jar(業務和依賴繫結)的檢測,打包,部署
# docker的名稱
MODULE_DOCKER_IMAGE_NAME=ehang-sping-boot-hello-world
# 專案目錄
MODULE_BATH_PATH=./spring-boot-001-hello-world
# docker的配置檔案目錄
MODULE_DOCKER_CONFIG_PATH=${MODULE_BATH_PATH}/docker
# jar_check_md5 透過jar的md5值直接檢測
# jar_unzip_check_md5 透過對jar包解壓 校驗檔案詳情的MD5
# check_md5 彙總上面兩個方法的校驗
# 直接透過jar校驗
jar_check_md5() {
# jar 包的路徑
JAR_FILE=$1
if [ ! -f $JAR_FILE ]; then
# 如果校驗的jar不存在 返回失敗
return 1
fi
JAR_MD5_FILE=${JAR_FILE}.md5
echo "Jenkins Docker映象構建校驗 JAR的MD5檔案:"$JAR_MD5_FILE
if [ -f $JAR_MD5_FILE ]; then
md5sum --status -c $JAR_MD5_FILE
RE=$?
md5sum $JAR_FILE > $JAR_MD5_FILE
return $RE
else
md5sum $JAR_FILE > $JAR_MD5_FILE
fi
return 1
}
# 將Jar解壓之後校驗
jar_unzip_check_md5() {
# jar 包的路徑
UNZIP_JAR_FILE=$1
if [ ! -f $UNZIP_JAR_FILE ]; then
# 如果校驗的jar不存在 返回失敗
return 1
fi
# jar的名稱
UNZIP_JAR_FILE_NAME=`basename -s .jar $UNZIP_JAR_FILE`
echo "Jenkins Docker映象構建校驗 JAR包名稱:"$UNZIP_JAR_FILE_NAME
# jar所在的路徑
UNZIP_JAR_FILE_BASE_PATH=${UNZIP_JAR_FILE%/${UNZIP_JAR_FILE_NAME}*}
echo "Jenkins Docker映象構建校驗 JAR包路徑:"$UNZIP_JAR_FILE_BASE_PATH
# 解壓的臨時目錄
JAR_FILE_UNZIP_PATH=${UNZIP_JAR_FILE_BASE_PATH}/jar_unzip_tmp
echo "Jenkins Docker映象構建校驗 解壓路徑:"$JAR_FILE_UNZIP_PATH
# 用於快取解壓後檔案詳情的目錄
UNZIP_JAR_FILE_LIST=${UNZIP_JAR_FILE_BASE_PATH}/${UNZIP_JAR_FILE_NAME}.files
echo "Jenkins Docker映象構建校驗 jar檔案詳情路徑:"$UNZIP_JAR_FILE_LIST
# 快取解壓後檔案詳情的MD5
UNZIP_JAR_FILE_LIST_MD5=${UNZIP_JAR_FILE_BASE_PATH}/${UNZIP_JAR_FILE_NAME}.files.md5
echo "Jenkins Docker映象構建校驗 jar檔案詳情MD5校驗路徑:"$UNZIP_JAR_FILE_LIST
rm -rf $JAR_FILE_UNZIP_PATH
mkdir -p $JAR_FILE_UNZIP_PATH
# 解壓檔案到臨時目錄
unzip $UNZIP_JAR_FILE -d $JAR_FILE_UNZIP_PATH
# 遍歷解壓目錄,計算每個檔案的MD5值及路徑 輸出到詳情列表檔案中
find $JAR_FILE_UNZIP_PATH -type f -print | xargs md5sum > $UNZIP_JAR_FILE_LIST
rm -rf $JAR_FILE_UNZIP_PATH
if [ ! -f $UNZIP_JAR_FILE_LIST_MD5 ]; then
# 如果校驗檔案不存在 直接返回校驗失敗
md5sum $UNZIP_JAR_FILE_LIST > $UNZIP_JAR_FILE_LIST_MD5
return 1
fi
# 根據上一次生成的MD5校驗
md5sum --status -c $UNZIP_JAR_FILE_LIST_MD5
RE=$?
# 生成最新的檔案列表的MD5
md5sum $UNZIP_JAR_FILE_LIST > $UNZIP_JAR_FILE_LIST_MD5
# 返回校驗結果
return $RE
}
check_md5() {
# jar 包的路徑
JAR_FILE=$1
if [ -f $JAR_FILE ]; then
# 直接透過jar校驗
jar_check_md5 $JAR_FILE
if [ $? = 0 ];then
echo "Jenkins Docker映象構建校驗 透過Jar的MD5校驗成功"
return 0
else
echo "Jenkins Docker映象構建校驗 透過Jar的MD5校驗失敗"
fi
# 透過解壓jar 校驗是否更新
jar_unzip_check_md5 $JAR_FILE
if [ $? = 0 ];then
echo "Jenkins Docker映象構建校驗 透過解壓的MD5校驗成功"
return 0
else
echo "Jenkins Docker映象構建校驗 透過解壓的MD5校驗失敗"
fi
fi
return 1
}
\cp -r ${MODULE_BATH_PATH}/target/*.jar ${MODULE_DOCKER_CONFIG_PATH}
APP_UPDATE=false
for APP_JAR_FILE in ${MODULE_DOCKER_CONFIG_PATH}/*.jar
do
echo $APP_JAR_FILE
if [ -f $APP_JAR_FILE ];then
echo "Jenkins Docker映象構建校驗lib 依賴Jar:"$APP_JAR_FILE
check_md5 $APP_JAR_FILE
if [ $? = 0 ];then
echo "Jenkins Docker映象構建校驗lib!成功,沒有發生變化"$APP_JAR_FILE
else
APP_UPDATE=true
echo "Jenkins Docker映象構建校驗lib!失敗,已經更新"$APP_JAR_FILE
fi
fi
done
if [ $APP_UPDATE = true ]; then
# 構建映象
docker build -t registry.cn-guangzhou.aliyuncs.com/ehang_jenkins/${MODULE_DOCKER_IMAGE_NAME}:latest ${MODULE_DOCKER_CONFIG_PATH}/.
# 將映象推送到阿里雲
docker push registry.cn-guangzhou.aliyuncs.com/ehang_jenkins/${MODULE_DOCKER_IMAGE_NAME}:latest
fi這是一段Maven構建完之後,用於檢測Jar是否發生更新的指令碼,稍微有點點長,我們來簡單的分塊解讀一下
下面是幾個公共方法:
-
直接透過Jar的MD5值檢測
# 直接透過jar校驗
jar_check_md5() {
# jar 包的路徑
JAR_FILE=$1
if [ ! -f $JAR_FILE ]; then
# 如果校驗的jar不存在 返回失敗
return 1
fi
JAR_MD5_FILE=${JAR_FILE}.md5
echo "Jenkins Docker映象構建校驗 JAR的MD5檔案:"$JAR_MD5_FILE
if [ -f $JAR_MD5_FILE ]; then
md5sum --status -c $JAR_MD5_FILE
RE=$?
md5sum $JAR_FILE > $JAR_MD5_FILE
return $RE
else
md5sum $JAR_FILE > $JAR_MD5_FILE
fi
return 1
} -
解壓Jar,然後根據檔案詳情的MD5值檢驗是否改變
# 將Jar解壓之後校驗
jar_unzip_check_md5() {
# jar 包的路徑
UNZIP_JAR_FILE=$1
if [ ! -f $UNZIP_JAR_FILE ]; then
# 如果校驗的jar不存在 返回失敗
return 1
fi
# jar的名稱
UNZIP_JAR_FILE_NAME=`basename -s .jar $UNZIP_JAR_FILE`
echo "Jenkins Docker映象構建校驗 JAR包名稱:"$UNZIP_JAR_FILE_NAME
# jar所在的路徑
UNZIP_JAR_FILE_BASE_PATH=${UNZIP_JAR_FILE%/${UNZIP_JAR_FILE_NAME}*}
echo "Jenkins Docker映象構建校驗 JAR包路徑:"$UNZIP_JAR_FILE_BASE_PATH
# 解壓的臨時目錄
JAR_FILE_UNZIP_PATH=${UNZIP_JAR_FILE_BASE_PATH}/jar_unzip_tmp
echo "Jenkins Docker映象構建校驗 解壓路徑:"$JAR_FILE_UNZIP_PATH
# 用於快取解壓後檔案詳情的目錄
UNZIP_JAR_FILE_LIST=${UNZIP_JAR_FILE_BASE_PATH}/${UNZIP_JAR_FILE_NAME}.files
echo "Jenkins Docker映象構建校驗 jar檔案詳情路徑:"$UNZIP_JAR_FILE_LIST
# 快取解壓後檔案詳情的MD5
UNZIP_JAR_FILE_LIST_MD5=${UNZIP_JAR_FILE_BASE_PATH}/${UNZIP_JAR_FILE_NAME}.files.md5
echo "Jenkins Docker映象構建校驗 jar檔案詳情MD5校驗路徑:"$UNZIP_JAR_FILE_LIST
rm -rf $JAR_FILE_UNZIP_PATH
mkdir -p $JAR_FILE_UNZIP_PATH
# 解壓檔案到臨時目錄
unzip $UNZIP_JAR_FILE -d $JAR_FILE_UNZIP_PATH
# 遍歷解壓目錄,計算每個檔案的MD5值及路徑 輸出到詳情列表檔案中
find $JAR_FILE_UNZIP_PATH -type f -print | xargs md5sum > $UNZIP_JAR_FILE_LIST
rm -rf $JAR_FILE_UNZIP_PATH
if [ ! -f $UNZIP_JAR_FILE_LIST_MD5 ]; then
# 如果校驗檔案不存在 直接返回校驗失敗
md5sum $UNZIP_JAR_FILE_LIST > $UNZIP_JAR_FILE_LIST_MD5
return 1
fi
# 根據上一次生成的MD5校驗
md5sum --status -c $UNZIP_JAR_FILE_LIST_MD5
RE=$?
# 生成最新的檔案列表的MD5
md5sum $UNZIP_JAR_FILE_LIST > $UNZIP_JAR_FILE_LIST_MD5
# 返回校驗結果
return $RE
} -
check_md5
彙總了前面兩種校驗方式
check_md5() {
# jar 包的路徑
JAR_FILE=$1
if [ -f $JAR_FILE ]; then
# 直接透過jar校驗
jar_check_md5 $JAR_FILE
if [ $? = 0 ];then
echo "Jenkins Docker映象構建校驗 透過Jar的MD5校驗成功"
return 0
else
echo "Jenkins Docker映象構建校驗 透過Jar的MD5校驗失敗"
fi
# 透過解壓jar 校驗是否更新
jar_unzip_check_md5 $JAR_FILE
if [ $? = 0 ];then
echo "Jenkins Docker映象構建校驗 透過解壓的MD5校驗成功"
return 0
else
echo "Jenkins Docker映象構建校驗 透過解壓的MD5校驗失敗"
fi
fi
return 1
} -
判斷APP的jar是否更新了
APP_UPDATE=false
for APP_JAR_FILE in ${MODULE_DOCKER_CONFIG_PATH}/*.jar
do
echo $APP_JAR_FILE
if [ -f $APP_JAR_FILE ];then
echo "Jenkins Docker映象構建校驗lib 依賴Jar:"$APP_JAR_FILE
check_md5 $APP_JAR_FILE
if [ $? = 0 ];then
echo "Jenkins Docker映象構建校驗lib!成功,沒有發生變化"$APP_JAR_FILE
else
APP_UPDATE=true
echo "Jenkins Docker映象構建校驗lib!失敗,已經更新"$APP_JAR_FILE
fi
fi
done -
構建映象
如果更新了,構建映象
if [ $APP_UPDATE = true ]; then
# 構建映象
docker build -t registry.cn-guangzhou.aliyuncs.com/ehang_jenkins/${MODULE_DOCKER_IMAGE_NAME}:latest ${MODULE_DOCKER_CONFIG_PATH}/.
# 將映象推送到阿里雲
docker push registry.cn-guangzhou.aliyuncs.com/ehang_jenkins/${MODULE_DOCKER_IMAGE_NAME}:latest
fi
Jenkin是在構建之前,會在各個專案的目錄下,建立了一個臨時的
tmp
資料夾,用來臨時彙總配置、指令碼、jar、解壓目錄等-
app.jar
當前模組的最新Jar包
-
Dockerfile
構建當前模組映象的Dockerfile
-
docker-image-build.sh
構建映象並推送到遠端映象倉庫的指令碼,主要指令碼之一
-
jar_files
快取本次jar中檔案列表資訊(MD5、檔案路徑)
-
jar_files.md5
快取 上一個jar包的
jar_files
對應的MD5值資訊,校驗是否發生變化的重要檔案 -
jar_unzip_tmp
app.jar 解壓儲存的臨時檔案,主要為了方便輸出
jar_files
,用完就刪掉了 -
lib
將會在下一篇文章講解Maven構建壓縮時用到;本文忽略
-
-
測試
-
第一次構建
-
未更新
-
已更新
-
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/70035356/viewspace-2996035/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- 使用Jenkins自動化部署Java專案JenkinsJava
- jenkins自動化專案部署實戰Jenkins
- jenkins + GitHub 實現專案自動化部署JenkinsGithub
- 基於Jenkins自動化部署spring boot專案JenkinsSpring Boot
- Jenkins+Docker+Gitee+SpringBoot自動化部署JenkinsDockerGiteeSpring Boot
- GitLab+Jenkins自動化部署.Net專案-Windows版GitlabJenkinsWindows
- Jenkins + Docker + Gitee自動化部署SpringBoot應用JenkinsDockerGiteeSpring Boot
- 使用jenkins進行前端專案自動部署Jenkins前端
- 前端專案自動化部署——超詳細教程(Jenkins、Github Actions)前端JenkinsGithub
- springboot docker jenkins 自動化部署並上傳映象Spring BootDockerJenkins
- 基於Jenkins實現php專案的自動化測試、自動打包和自動部署JenkinsPHP
- 微服務下的持續整合-Jenkins自動化部署GitHub專案微服務JenkinsGithub
- Jenkins部署碼雲SpringBoot專案JenkinsSpring Boot
- 自動化專案Jenkins持續整合Jenkins
- Jenkins自動化前端專案構建Jenkins前端
- vue 自動化部署 jenkins 篇VueJenkins
- Docker 之 Jenkins自動化部署DockerJenkins
- ASP.Net Core使用Jenkins配合pm2自動化部署專案ASP.NETJenkins
- 陷入jenkins自動化部署的坑Jenkins
- 解放雙手 | Jenkins + gitlab + maven 自動打包部署專案JenkinsGitlabMaven
- 阿里出品!SpringBoot應用自動化部署神器,IDEA版Jenkins?阿里Spring BootIdeaJenkins
- 如何使用jenkins自動生成springboot專案的docker映象JenkinsSpring BootDocker
- Springboot建立maven多模組專案Spring BootMaven
- GitLab + Jenkins + ACK 自動化部署方案GitlabJenkins
- 使用Jenkins持續整合前端專案並自動化部署到Nginx伺服器Jenkins前端Nginx伺服器
- SpringBoot - 多模組專案的搭建教程Spring Boot
- 基於Jenkins Pipeline自動化部署Jenkins
- Jenkins + Docker + ASP.NET Core自動化部署JenkinsDockerASP.NET
- 乞丐版自動化部署 jenkins 環境搭建Jenkins
- 使用 Git 實現 專案的自動化部署Git
- 使用pm2自動化部署node專案
- 第二十三章:SpringBoot專案多模組打包與部署Spring Boot
- Git Webhook自動部署專案GitWebHook
- 使用 Jenkins + Ansible 實現自動化部署 NginxJenkinsNginx
- Gogs+Jenkins+Docker 自動化部署.NetCoreGoJenkinsDockerNetCore
- 自動化測試之:Jenkins安裝與部署Jenkins
- 如何構建多模組的SpringBoot專案Spring Boot
- 前端多專案模組化實踐前端