Jenkins部署SpringBoot的Docker鏡像步驟詳解
前面我們介紹了K8S+Docker+Maven插件打包部署SpringCloud微服務項目,在實際應用過程中,很多項目沒有用到K8S和微服務,但是用到了Docker和SpringBoot,所以,我們這邊介紹,如果使用Jenkins+jib-maven-plugin插件打包部署SpringBoot項目的Docker鏡像。
網上有多種Docker打包插件使用說明,講解最多的是Spotify開源的,Spotify官方已不再推薦使用docker-maven-plugin插件進行打包,而是推薦其最新的docker打包插件dockerfile-maven-plugin,但是dockerfile-maven-plugin也已經很久沒有更新了,在使用方面也有局限性,比如:只支持在本機Docker的鏡像build、tag、push。
經過幾個插件的對比,發現Google開源的Jib插件功能更強大,它可以不寫Dockerfile,不需要在本地安裝Docker環境就能實現Docker打包,而且一直在更新,所以這里選擇這個插件作為我們的Docker打包插件。Jib構建Java的Docker和OCI映像,不需要在本機安裝Docker daemon,也不需要深入掌握Docker最佳實踐。它可以作為Maven和Gradle的插件以及Java庫使用。
下面介紹Jib( jib-maven-plugin插件 )如何將SpringBoot應用程序分層打包Docker鏡像,充分利用Docker的鏡像分層復用機制,解決網絡限制和占用大量磁盤空間的問題。
Jib( jib-maven-plugin插件 )構建的三個參數:
- buildTar:本地構建,不需要Docker daemon就可以將鏡像生成tar文件,保存在工程的target目錄下。
- dockerBuild:將構建的鏡像存到當前環境的Docker daemon。
- build:將構建的鏡像推送到遠程倉庫,官方倉庫或者Harbor私有倉庫。
一、SpringBoot項目jib-maven-plugin插件配置說明
我們這里主要使用buildTar命令,將Docker鏡像文件打包在本地,然后通過Jenkins插件發布到服務器運行,這樣可以不需要搭建使用私有Docker鏡像倉庫Harbor。
- 打包命令:
-Dimage可以在打包時自定義鏡像名稱和版本,這個可以在Jenkins腳本時使用,如果不配置默認使用pom.xml里面配置的鏡像名稱和版本,如果不設置版本,默認版本是latest。
clean package -Ptest -Dimage=gitegg:1.0.1 jib:buildTar -f pom.xml
- docker鏡像載入命令:
docker load用來載入鏡像包,但是不能對載入的鏡像重命名;docker import用來載入容器包,但兩者都會恢復為鏡像,可以為鏡像指定新名稱;經過測試,jib-maven-plugin插件生成的Docker鏡像文件jib-image.tar只能通過docker load載入,并且在pom.xml配置中format不能選擇OCI(K8S時使用OCI),否則載入不了。
docker load --input jib-image.tar
- 可以將jib-maven-plugin插件的打包命令綁定到Maven的生命周期:
<executions>
<execution>
<phase>install</phase>
<goals>
<goal>build</goal>
</goals>
</execution>
</executions>
- 將Docker鏡像推送到私有鏡像倉庫Harbor,當然這里不需要,但是可以配置鏡像名稱和鏡像版本,當在運行打包命令時,沒有加參數-Dimage,就會取此處的配置。
<to>
<image>私有倉庫地址/項目名稱/${project.artifactId}:${project.version}</image>
<auth>
<username>私有倉庫用戶名</username>
<password>私有倉庫密碼</password>
</auth>
</to>
- volumes 創建容器內的目錄,當SpringBoot項目記錄日志等需要自定義的目錄地址時,此處需要定義容器內的目錄;SpringBoot使用的內嵌Tomcat容器默認使用/tmp作為工作目錄,這里可以不配置/tmp的valumes。
<!-- 容器內的目錄 -->
<volumes>
<value>/tmp</value>
<value>/log</value>
</volumes>
- workingDirectory 容器內的工作目錄,容器啟動時執行的命令會在容器內的此目錄下執行。
<workingDirectory>/gitegg</workingDirectory>
- format 使用OCI構建OCI容器映像。這里直接注釋掉不要使用,否則docker load時不能載入,在K8S里面可以使用,所以這里不使用這個format。
<!--K8S時,使用OCI,單獨只用docker load會報錯,所以這里不使用這個format-->
<format>OCI</format>
下面是SpringBoot項目中pom.xml的jib-maven-plugin插件配置信息:
······
<properties>
······
<!-- jib-maven-plugin插件版本,代碼打包docker -->
<jib.maven.plugin.version>3.3.2</jib.maven.plugin.version>
······
</properties>
······
<!-- Docker 打包插件 -->
<plugin>
<groupId>com.google.cloud.tools</groupId>
<artifactId>jib-maven-plugin</artifactId>
<version>${jib.maven.plugin.version}</version>
<!-- 綁定到Maven的install生命周期 ,此處如果不使用https,會有問題,需要設置sendCredentialsOverHttp=true-->
<executions>
<execution>
<phase>install</phase>
<goals>
<goal>build</goal>
</goals>
</execution>
</executions>
<configuration>
<!--允許非https-->
<allowInsecureRegistries>true</allowInsecureRegistries>
<!-- 相當于Docerkfile中的FROM -->
<from>
<image>openjdk:11-jre</image>
</from>
<to>
<image>${docker.harbor.addr}/${docker.harbor.project}/${project.artifactId}:${project.version}</image>
<auth>
<username>${docker.harbor.username}</username>
<password>${docker.harbor.password}</password>
</auth>
</to>
<container>
<!--jvm內存參數-->
<jvmFlags>
<jvmFlag>-Xms512m</jvmFlag>
<jvmFlag>-Xmx8g</jvmFlag>
</jvmFlags>
<!-- 容器內的目錄-->
<volumes>
<value>/tmp</value>
<value>/log</value>
</volumes>
<workingDirectory>/gitegg</workingDirectory>
<environment>
<TZ>Asia/Shanghai</TZ>
</environment>
<!--使用該參數保證鏡像的創建時間與系統時間一致-->
<creationTime>USE_CURRENT_TIMESTAMP</creationTime>
<!--K8S時,使用OCI,非K8S時,load會報錯-->
<!-- <format>OCI</format>-->
</container>
</configuration>
</plugin>
二、服務器Docker運行環境配置
前面我們講了如何安裝Docker,以及相關配置,所以這里不再贅述,直接按照自己的服務器實際情況進行安裝配置Docker即可,這里講解如何為運行SpringBoot的Fat Jar做環境準備,以及說明鏡像包傳輸到服務器之后執行的部署腳本。
1、部署及備份目錄準備
- 新建 /opt/tmp 目錄,用于Jenkins打包后,通過 Publish Over SSH插件將包傳輸到服務器的臨時目錄。
- 新建 /opt/bak 目錄,用于存儲所有部署過的包備份,方便后續版本回滾。此目錄可能會占用很大空間,所以需要選擇一個磁盤空間大的掛載目錄。
- 新建 /opt/script 目錄,用于Jenkins將包傳輸完成之后,執行安裝、備份操作的相關命令腳本。
- 新建 /data/container/docker_server/tmp 目錄,用于映射容器內/tmp目錄,/tmp目錄是SpringBoot內置Tomcat默認運行目錄。
- 新建 /data/container/docker_server/logs目錄,用于映射容器內/var/log目錄,/var/log目錄是項目配置的日志存放目錄,映射到宿主機之后,方便查看不同級別的日志。
mkdir -p /opt/tmp /opt/bak /opt/script /data/container/docker_server/tmp /data/container/docker_server/logs
chmod -R 777 /opt/tmp /opt/bak /opt/script /data/container/docker_server/tmp /data/container/docker_server/logs
2、部署腳本編寫說明
- 定義入參,可以通過Jenkins任務將參數傳入腳本中,我們定義了下面5個參數:
container_name=gitegg-server : 容器名稱image_name=gitegg-server : 鏡像名稱version=latest : 鏡像版本image_port=8182 : 宿主主機端口映射server_port=8080 : 容器內服務端口 - 對參數進行檢查,是否未傳入參數,這里根據自己的實際情況判斷,比如必須傳入哪些參數,就設置參數的個數不能小于幾。
echo "param validate"
if [ $# -lt 1 ]; then
echo "you must use like this : ./publish_docker_server.sh <container_name> <image_name> <version> [image port] [server port]"
exit
fi
- 入參賦值,如果有參數傳入,則取服務參數,如果沒有參數傳入則取默認值
if [ "$1" != "" ]; then
container_name="$1"
fi
echo "container_name=" $container_name
if [ "$2" != "" ]; then
image_name="$2"
fi
if [ "$3" != "" ]; then
version="$3"
fi
echo "version=" $version
if [ "$4" != "" ]; then
image_port="$4"
fi
echo "image_port=" $image_port
if [ "$5" != "" ]; then
server_port="$5"
fi
echo "server_port=" $server_port
- 停止并刪除容器
echo "執行docker ps"
docker ps
if [[ "$(docker inspect $container_name 2> /dev/null | grep $container_name)" != "" ]];
then
echo $container_name "容器存在,停止并刪除"
echo "docker stop" $container_name
docker stop $container_name
echo "docker rm" $container_name
docker rm $container_name
else
echo $container_name "容器不存在"
fi
- 停止并刪除鏡像
# 刪除鏡像
echo "執行docker images"
docker images
if [[ "$(docker images -q $image_name 2> /dev/null)" != "" ]];
then
echo $image_name '鏡像存在,刪除鏡像'
docker rmi $(docker images -q $image_name 2> /dev/null) --force
else
echo $image_name '鏡像不存在'
fi
- 備份本次安裝鏡像包
#bak image
echo "bak image" $image_name
BAK_DIR=/opt/bak/docker/$image_name/`date +%Y%m%d`
mkdir -p "$BAK_DIR"
cp "/opt/tmp/jib-image.tar" "$BAK_DIR"/"$image_name"_`date +%H%M%S`.tar
- 執行安裝鏡像包命令
echo "docker load" $image_name
docker load --input /opt/tmp/jib-image.tar
- 執行運行命令
echo "docker run" $image_name
docker run -d -p $image_port:$server_port --name=$container_name --restart=always -v /data/container/docker_server/tmp:/tmp -v /data/container/docker_server/logs:/var/log $image_name
- 刪除安裝文件,因為前面已經備份過了,所以這里將臨時安裝文件刪除
echo "remove tmp " $image_name
rm -rf /opt/tmp/jib-image.tar
- 打印執行完成的命令
echo "Docker Server is starting,please try to access $container_name conslone url"
3. 完整的安裝部署腳本
container_name=gitegg-server
image_name=gitegg-server
version=latest
image_port=8181
server_port=8080
echo "param validate"
if [ $# -lt 1 ]; then
echo "you must use like this : ./publish_docker_server.sh <container_name> <image_name> <version> [image port] [server port]"
exit
fi
if [ "$1" != "" ]; then
container_name="$1"
fi
echo "container_name=" $container_name
if [ "$2" != "" ]; then
image_name="$2"
fi
if [ "$3" != "" ]; then
version="$3"
fi
echo "version=" $version
if [ "$4" != "" ]; then
image_port="$4"
fi
echo "image_port=" $image_port
if [ "$5" != "" ]; then
server_port="$5"
fi
echo "server_port=" $server_port
echo "執行docker ps"
docker ps
if [[ "$(docker inspect $container_name 2> /dev/null | grep $container_name)" != "" ]];
then
echo $container_name "容器存在,停止并刪除"
echo "docker stop" $container_name
docker stop $container_name
echo "docker rm" $container_name
docker rm $container_name
else
echo $container_name "容器不存在"
fi
# 刪除鏡像
echo "執行docker images"
docker images
if [[ "$(docker images -q $image_name 2> /dev/null)" != "" ]];
then
echo $image_name '鏡像存在,刪除鏡像'
docker rmi $(docker images -q $image_name 2> /dev/null) --force
else
echo $image_name '鏡像不存在'
fi
#bak image
echo "bak image" $image_name
BAK_DIR=/opt/bak/docker/$image_name/`date +%Y%m%d`
mkdir -p "$BAK_DIR"
cp "/opt/tmp/jib-image.tar" "$BAK_DIR"/"$image_name"_`date +%H%M%S`.tar
echo "docker load" $image_name
docker load --input /opt/tmp/jib-image.tar
echo "docker run" $image_name
docker run -d -p $image_port:$server_port --name=$container_name --restart=always -v /data/container/docker_server/tmp:/tmp -v /data/container/docker_server/logs:/var/log $image_name
echo "remove tmp " $image_name
rm -rf /opt/tmp/jib-image.tar
echo "Docker Server is starting,please try to access $container_name conslone url"
三、新建Jenkins配置打包任務,部署SpringBoot項目Docker鏡像
1、Dashboard > 新建任務,輸入任務名稱,選擇“構建一個maven項目”,點擊確定。
2、項目配置,進入到任務配置頁
- 丟棄舊的構建 :這里在保持構建的最大個數填寫5,當然可以根據自己情況填寫,否則舊的構建包會一直存在占用磁盤空間。
- 源碼管理:配置git代碼地址、用戶名密碼和版本分支,如果是需要用戶名密碼的git庫,那么下面需要選擇訪問的用戶名密碼,這里一定要使用用戶名密碼方式,使用token的方式無法選中。可以在下方add,也可以在Jenkins全局Credentials 中添加,方便其它任務使用。
- 構建觸發器:可選可不選,這個根據自己的需求選擇,任務在什么情況下出發構建。
- 構建環境:這里不需要選擇,在介紹構建NodeJS項目時,需要選中NodeJS環境。
- Build:這里填寫Maven打包命令,可以添加參數選擇打包環境test或prod,定義鏡像名稱和版本等。
clean package -Dmaven.test.skip=true -Ptest -Dimage=gitegg jib:buildTar -f pom.xml
- Post Steps:將打包后的文件發送到服務器,并執行設置好的腳本,這里選擇Run only if build succeeds,當構建成功時Post。
- Exec command:將打好的包發布到環境之后,在環境上執行的部署腳本命令。publish_docker_server.sh 是腳本文件、第一個gitegg 是容器名稱、第二個gitegg 是鏡像名稱、latest 是鏡像版本、8181 是宿主機端口號、8080 是容器內服務口號。
/opt/script/publish_docker_server.sh gitegg gitegg latest 8181 8080
3、運行構建任務
- 立即構建
- 查看構建日志:點擊立即構建之后,下方會出現進度條,點擊進度條就可以進入構建日志界面。
- 構建成功后,下方會給出構建成功提示,此時登錄遠程服務器查看文件是否部署成功即可。
4、Docker鏡像部署成功之后,可以使用以下Docker常用命令查看運行情況
- 查看鏡像
docker images
- 查看有哪些容器在運行
docker ps
- 停止容器
docker stop 容器id
- 刪除容器、刪除鏡像
docker rm 容器id
docker rmi 鏡像id
- 查看容器運行日志
docker logs -f 容器id
- 進入到容器
docker exec -it 容器id /bin/bash