目前主流的互聯網公司採用的架構設計應該都比較超前,微服務,容器和雲原生等基礎架構應該都有采用和進行二次開發。在微服務架構設計中,項目發佈時需要將項目打包成容器鏡像,這一步是最基本的操作。
下面我們介紹下,如何將SpringBoot項目打包成Docker容器鏡像,通過筆記的形式記錄下來方便以後查閱。將SpringBoot項目打包成Docker容器鏡像主要分為兩部分:
- 添加Maven插件
- 定義Dockerfile
下面通過一個詳細的例子來介紹下具體的內容。
添加Maven插件
在maven的pom.xml中添加我們需要的插件,這裡我們採用dockerfile-maven-plugin,當然也可以選擇其他的插件,不同的插件的使用方法可能不太一樣
<code><
plugin
><
groupId
>com.spotifygroupId
><
artifactId
>dockerfile-maven-pluginartifactId
><
executions
><
execution
><
id
>defaultid
><
goals
><
goal
>buildgoal
><
goal
>pushgoal
>goals
>execution
>executions
><
configuration
><
repository
>${docker.repostory}/${project.artifactId}
repository
><
tag
>${project.version}tag
><
buildArgs
><
JAR_FILE
>target/${project.build.finalName}.jarJAR_FILE
>buildArgs
>configuration
>plugin
>/<code>
dockerfile-maven-plugin插件主要使用了兩個goal:
- build是指構建Docker容器鏡像;
- push是將構建的Docker容器鏡像推送到公有或者私有鏡像倉庫中;
dockerfile-maven-plugin插件的configuration說明如下:
- repository定義一個公有或者私有的鏡像倉庫;
- tag是Docker鏡像的版本號;
- buildArgs是向Dockerfile傳遞參數,該節點下面的子節點標籤可以自定義,因為該節點標籤的數據類型是Map;
- JAR_FILE是自定義標籤,定義了Maven打包後jar文件的位置,該參數會在Dockerfile中用到;
定義Dockerfile
接下來我們就需要定義Dockerfile了。那麼,Dockerfile是什麼呢?
Dockerfile 是一個用來構建鏡像的文本文件,文本內容包含了一條條構建鏡像所需的指令和說明。
Dockerfile常用的指令有如下幾種:
COPY
複製指令,從上下文目錄中複製文件或者目錄到容器裡指定路徑。格式:
<code>COPY [--chown=<
user
>:<
group
>]<
源路徑1
>...<
目標路徑
> COPY [--chown=<
user
>:<
group
>] ["<
源路徑1
>",... "<
目標路徑
>"]/<code>
[--chown=:]:可選參數,用戶改變複製到容器內文件的擁有者和屬組。
:源文件或者源目錄,這裡可以是通配符表達式,其通配符規則要滿足 Go 的 filepath.Match 規則。例如:
<code>COPY
hom* /mydir/
COPY
hom?.txt /mydir/
/<code>
:容器內的指定路徑,該路徑不用事先建好,路徑不存在的話,會自動創建。
ADD
ADD 指令和 COPY 的使用格式一致(同樣需求下,官方推薦使用 COPY)。功能也類似,不同之處如下:
- ADD 的優點:在執行 為 tar 壓縮文件的話,壓縮格式為 gzip, bzip2 以及 xz 的情況下,會自動複製並解壓到 。
- ADD 的缺點:在不解壓的前提下,無法複製 tar 壓縮文件。會令鏡像構建緩存失效,從而可能會令鏡像構建變得比較緩慢。具體是否使用,可以根據是否需要自動解壓來決定。
CMD
類似於 RUN 指令,用於運行程序,但二者運行的時間點不同:
- CMD 在docker run 時運行。
- RUN 是在 docker build。
作用:為啟動的容器指定默認要運行的程序,程序運行結束,容器也就結束。CMD 指令指定的程序可被 docker run 命令行參數中指定要運行的程序所覆蓋。
注意:如果 Dockerfile 中如果存在多個 CMD 指令,僅最後一個生效。格式:
<code>CMD
CMD
[""
,""
,""
,...]CMD
[""
,""
,...] # 該寫法是為 ENTRYPOINT 指令指定的程序提供默認參數/<code>
推薦使用第二種格式,執行過程比較明確。第一種格式實際上在運行的過程中也會自動轉換成第二種格式運行,並且默認可執行文件是 sh。
ENTRYPOINT
類似於 CMD 指令,但其不會被 docker run 的命令行參數指定的指令所覆蓋,而且這些命令行參數會被當作參數送給 ENTRYPOINT 指令指定的程序。
但是, 如果運行 docker run 時使用了 --entrypoint 選項,此選項的參數可當作要運行的程序覆蓋 ENTRYPOINT 指令指定的程序。
優點:在執行 docker run 的時候可以指定 ENTRYPOINT 運行所需的參數。
注意:如果 Dockerfile 中如果存在多個 ENTRYPOINT 指令,僅最後一個生效。格式:
<code>ENTRYPOINT
[
""
,""
,""
,...]/<code>
可以搭配 CMD 命令使用:一般是變參才會使用 CMD ,這裡的 CMD 等於是在給 ENTRYPOINT 傳參,以下示例會提到。
示例:
假設已通過 Dockerfile 構建了 nginx:test 鏡像:
<code>FROM
nginx ENTRYPOINT ["nginx"
,"-c"
] CMD ["/etc/nginx/nginx.conf"
] /<code>
1、不傳參運行
<code>docker run nginx:
test
/<code>
容器內會默認運行以下命令,啟動主進程。
<code>nginx
-c /etc/nginx/nginx.conf/<code>
2、傳參運行
<code>docker run nginx:
test
-c /etc/nginx/new.conf/<code>
容器內會默認運行以下命令,啟動主進程(/etc/nginx/new.conf:假設容器內已有此文件)
<code>nginx -c /etc/nginx/new
.conf/<code>
ENV
設置環境變量,定義了環境變量,那麼在後續的指令中,就可以使用這個環境變量。格式:
<code>ENV<
key
><
value
> ENV<
key1
>=<
value1
><
key2
>=<
value2
>.../<code>
以下示例設置 NODE_VERSION = 7.2.0 , 在後續的指令中可以通過 $NODE_VERSION 引用:
<code>ENV
NODE_VERSION7
.2
.0
RUN curl -SLO"https://nodejs.org/dist/v
$NODE_VERSION
/node-v$NODE_VERSION
-linux-x64.tar.xz" \ && curl -SLO"https://nodejs.org/dist/v
$NODE_VERSION
/SHASUMS256.txt.asc"/<code>
ARG
構建參數,與 ENV 作用一致。不過作用域不一樣。ARG 設置的環境變量僅對 Dockerfile 內有效,也就是說只有 docker build 的過程中有效,構建好的鏡像內不存在此環境變量。
構建命令 docker build 中可以用 --build-arg = 來覆蓋。格式:
<code>ARG<
參數名
>[=<
默認值
>]/<code>
VOLUME
定義匿名數據卷。在啟動容器時忘記掛載數據卷,會自動掛載到匿名卷。
作用:
- 避免重要的數據,因容器重啟而丟失,這是非常致命的。
- 避免容器不斷變大。
格式:
<code>VOLUME
[
""
,""
...]VOLUME
/<code>
在啟動容器 docker run 的時候,我們可以通過 -v 參數修改掛載點。
EXPOSE
僅僅只是聲明端口。
作用:
- 幫助鏡像使用者理解這個鏡像服務的守護端口,以方便配置映射。
- 在運行時使用隨機端口映射時,也就是 docker run -P 時,會自動隨機映射 EXPOSE 的端口。
格式:
<code>EXPOSE<
端口1
> [<
端口2
>...]/<code>
WORKDIR
指定工作目錄。用 WORKDIR 指定的工作目錄,會在構建鏡像的每一層中都存在。(WORKDIR 指定的工作目錄,必須是提前創建好的)。
docker build 構建鏡像過程中的,每一個 RUN 命令都是新建的一層。只有通過 WORKDIR 創建的目錄才會一直存在。格式:
<code>WORKDIR<
工作目錄路徑
>/<code>
USER
用於指定執行後續命令的用戶和用戶組,這邊只是切換後續命令執行的用戶(用戶和用戶組必須提前已經存在)。格式:
<code>USER<
用戶名
>[:<
用戶組
>]/<code>
我們自定義的Dockerfile內容結構大致如下:
<code>FROM
openjdk:8
MAINTAINER xiaobaoqiang 163.com> ARG JAR_FILE ADD${JAR_FILE}
"/root/nacos-discovery-provider.jar"
ENTRYPOINT ["java"
,"-jar"
,"/root/nacos-discovery-provider.jar"
] EXPOSE8081
/<code>
- FROM是指從哪個基礎鏡像開始構建我們自己的鏡像,這裡我們使用openjdk版本1.8;
- MAINTAINER是指製作該鏡像的作者;
- ARG構建參數,這裡我們使用了JAR_FILE,該參數是從Maven的dockerfile-maven-plugin插件傳遞過來的;
- ADD將Mavne打包後的jar文件複製到Docker容器中;
- ENTRYPOINT是指容器啟動時需要運行的程序指令;
- EXPOSE是指容器啟動後需要暴露的端口,該參數一般情況下與springboot配置文件中的server.port保持一致;
驗證
所有的準備工作和配置文件已經完成,下面通過maven打包的形式來驗證下:
<code>mvn cleanpackage
/<code>
看到如上日誌信息,則表示我們打包構建Docker鏡像成功。
<code>[root@instance01
~]#
docker
images
|
grep
nacos-discovery-provider
10.0
.0
.10
:5000/nacos-discovery-provider
0.0
.1
65dcbdd91921
2
minutes
ago
554MB
/<code>
通過docker命令可以查看我們剛構建的容器鏡像。
通過這種Mavne插件和Dockerfile再結合jenkins就可以完全實現CI/CD持續集成持續發佈功能,大大縮短我們在敏捷項目中的發佈時間,提升我們的工作效率。
不積跬步,無以至千里;不積小流,無以成江海!
關鍵字: ENTRYPOINT Dockerfile 構建