爲了實現CI/CD,先來定製一個Docker鏡像【實戰精華篇】

背景

計劃把手頭的項目逐步改造爲基於Docker容器的方式發佈,同時,項目中已經採用了雲廠商提供的CI/CD自動化發佈流水線。因此,爲配合CI/CD操作,需要先針對項目構建一些發佈的腳本,通過腳本來自動化操作Docker鏡像定製、Docker的啓動和停止。

在閱讀和實踐本篇文章之前,如果你還未搭建Docker的環境,可參考上篇文章《Linux安裝Docker完整教程》,先把整個環境搭建起來,同時熟悉一下Docker的基本操作命令。

這篇文章就配合具體的實踐案例來爲大家講講如何定製一個Docker鏡像,並通過腳本來執行鏡像的構建、項目的發佈、容器的啓動與停止等。

什麼是Dockerfile?

Dockerfile是一個用來構建鏡像的文本文件,文本內容包含了一條條構建鏡像所需的指令和說明。常見的指令比如有:FROM、RUN、ADD、COPY、CMD、ENV等。

在鏡像構建時,需要注意的一點是:鏡像的構建是一層層構建的,前一層是後一層的基礎。每一層構建完就不會再發生改變,後一層上的任何改變只發生在自己這一層。

像上面提到的指令,每一次操作都會構建一層。比如刪除前一層的文件,在最終容器運行時,雖然看不到這個文件,但是實際上該文件會一直跟隨鏡像。因此,在構建鏡像時,需要額外小心,每一層儘量只包含該層需要添加的東西,任何額外的東西應該在該層構建結束前清理掉。

另外,爲了減少構建層的數量,在編寫Dockerfile文件時儘量將多層的指令合併成一層執行,比如兩個RUN命令可以通過&&將其合併成一條。

不建議的鏡像製作方式

製作Docker鏡像通常有兩種方式:基於docker commit和基於Dockerfile的形式。

Docker提供了一個 docker commit 命令,可以將容器的存儲層保存下來成爲鏡像。換句話說,就是在原有鏡像的基礎上,再疊加上容器的存儲層,並構成新的鏡像。後續運行這個新鏡像時,就會擁有原有容器最後的文件變化。

docker commit的方式除了學習之外,還可以用於一些特殊的場景,比如被入侵後保存現場等。但是不要使用 docker commit 定製鏡像,定製鏡像應該使用 Dockerfile 來完成。

這是因爲在使用docker commit製作鏡像時,除了我們想要修改的內容(文件)之外,該命令還會修改一些其他的文件,而且所有對鏡像的操作都是黑箱操作,生成的鏡像也被稱爲黑箱鏡像

除了製作鏡像的人知道執行過什麼命令、怎麼生成的鏡像,別人根本無從得知。即使製作鏡像的人,一段時間後可能也無法記清具體的操作。這種黑箱鏡像的維護工作是非常痛苦的。

另外,如果使用 docker commit 製作鏡像,以及後期修改的話,每一次修改都會讓鏡像更加臃腫一次,所刪除的上一層的東西並不會丟失,會一直如影隨形的跟着這個鏡像,即使根本無法訪問到。這會讓鏡像更加臃腫。

因此,這裏我們不採用 docker commit 的方式製作鏡像,如果大家感興趣的話,可以在網絡上查詢一下該方式的製作流程。本文重點介紹基於 Dockerfile 的方式來製作鏡像,下面就以實例展示一下如何構建一個Docker鏡像。

Dockerfile指令編寫

在/opt目錄下創建一個業務目錄/opt/channel/docker(這裏部署的項目爲渠道項目,取名channel),在該目錄下存放Dockerfile、待發布的jar包等資源文件。

$ cd /opt/channel/docker
$ touch Dockerfile

上述指令先進入/opt/channel/docker目錄、創建了一個空的Dockerfile(文本)文件。

編輯Dockerfile內容如下:

FROM java:8
COPY ./hqy-service-channel.jar ./app.jar
ENV spring.profiles.active prod
EXPOSE 8190
ENTRYPOINT ["java", "-jar","-Duser.timezone=GMT+08", "./app.jar"]

Dockerfile中涉及到FROM、COPY、ENV、EXPOSE、ENTRYPOINT五個指令,下面逐一講解。

FROM指令

所謂製作鏡像,就是在已經存在的鏡像的基礎上進行定製。基礎鏡像是必須指定的,而 FROM 就是指定基礎鏡像,因此一個 DockerfileFROM 是必備的指令,並且必須是第一條指令。

這裏的FROM java:8,也就是採用openjdk在Docker鏡像源中的鏡像,版本爲8。可以通過search命令查看一下這個鏡像:

[docker]# docker search java
NAME                                 DESCRIPTION                                     STARS     OFFICIAL   AUTOMATED
node                                 Node.js is a JavaScript-based platform for s…   11734     [OK]
tomcat                               Apache Tomcat is an open source implementati…   3368      [OK]
openjdk                              "Vanilla" builds of OpenJDK (an open-source …   3362      [OK]
java                                 DEPRECATED; use "openjdk" (or other JDK impl…   1976      [OK]

第4個name爲java的便是,爲了方便後面操作,這裏直接將鏡像pull到本地。

docker pull java:8

查看本地pull之後,本地的鏡像列表:

[root@iZ2zehx0enix3i0aiea7p0Z docker]# docker images
REPOSITORY   TAG       IMAGE ID       CREATED         SIZE
java         8         d23bdf5b1b1b   5 years ago     643MB

後續執行鏡像製作時便以該鏡像爲基礎進行構建。

COPY指令

COPY,複製指令,從上下文目錄中複製文件或者目錄到容器裏指定路徑。

COPY ./hqy-service-channel.jar ./app.jar

其中第一個參數爲源文件路徑,第二個參數爲容器內目標文件路徑。這裏是將當前目錄下的Spring Boot項目jar包hqy-service-channel.jar,複製到容器內並命名爲app.jar。在執行創建鏡像命令之前,需要把項目jar包放到Dockerfile同級目錄下。

ENV指令

ENV指令,用於設置環境變量,定義了環境變量,那麼在後續的指令中,就可以使用這個環境變量。

基本格式爲:

ENV <key> <value>
ENV <key1>=<value1> <key2>=<value2>...

第一個參數爲變量key,第二個參數爲變量值,這裏用於設置SpringBoot項目的配置文件的profile爲prod(生產配置文件)。

EXPOSE指令

EXPOSE指令,僅僅只是聲明端口。作用是幫助鏡像使用者理解這個鏡像服務的守護端口,以方便配置映射。另外,在運行時使用隨機端口映射時,也就是 docker run -P 時,會自動隨機映射 EXPOSE 的端口。

基本格式:

EXPOSE <端口1> [<端口2>...]

這裏採用了8190端口。

ENTRYPOINT指令

ENTRYPOINT指令,類似於CMD指令,但其不會被docker run的命令行參數指定的指令所覆蓋,而且這些命令行參數會被當作參數送給 ENTRYPOINT指令指定的程序。在執行docker run時可以指定ENTRYPOINT運行所需的參數。

ENTRYPOINT ["<executeable>","<param1>","<param2>",...]

這裏使用ENTRYPOINT指令來執行jar -jar啓動SpringBoot項目。

RUN指令

RUN指令雖然在實例中沒用到,但也是非常常見的一個指令,於執行後面跟着的命令行命令,有以下兩種格式。

shell 格式:

RUN <命令行命令>
# <命令行命令> 等同於,在終端操作的 shell 命令。

exec格式:

RUN ["可執行文件", "參數1", "參數2"]
# 例如:
# RUN ["./test.php", "dev", "offline"] 等價於 RUN ./test.php dev offline

經過上述一系列的操作,Dockerfile文件編寫完畢。在構建命令時值得注意的是:按照Docker最佳實踐的要求,容器不應該向其存儲層內寫入任何數據,容器存儲層要保持無狀態化。所有的文件寫入操作,都應該使用數據卷(Volume)、或者綁定宿主目錄,在這些位置的讀寫會跳過容器存儲層,直接對宿主(或網絡存儲)發生讀寫,其性能和穩定性更高。

構建鏡像

上面準備好了Dockerfile文件,再把對應的jar包放在指定的位置,可在Dockerfile文件的目錄執行構建命令,比如:

docker build -t channel .

其中-t channel指定了構建鏡像的名稱,當然也可以同時指定版本編號-t channel:v1。後面的“.”指的是當前目錄。

執行效果如下:

[docker]# docker build -t channel .
Sending build context to Docker daemon  82.31MB
Step 1/5 : FROM java:8
 ---> d23bdf5b1b1b
Step 2/5 : COPY ./hqy-service-channel.jar ./app.jar
 ---> 10cb376c7572
Step 3/5 : ENV spring.profiles.active test
 ---> Running in ca70651b21b6
Removing intermediate container ca70651b21b6
 ---> ec420f94df51
Step 4/5 : EXPOSE 8190
 ---> Running in 318e718d552a
Removing intermediate container 318e718d552a
 ---> 6746bad4a990
Step 5/5 : ENTRYPOINT ["java", "-jar","-Duser.timezone=GMT+08", "./app.jar"]
 ---> Running in 135de4d42ec8
Removing intermediate container 135de4d42ec8
 ---> 1720afb4fec7
Successfully built 1720afb4fec7
Successfully tagged channel:latest

執行docker images可查看到鏡像構建完畢:

[docker]# docker images
REPOSITORY   TAG       IMAGE ID       CREATED          SIZE
channel      latest    1720afb4fec7   51 seconds ago   725MB
java         8         d23bdf5b1b1b   5 years ago      643MB

後續便可以通過docker run命令來啓動容器了。

這裏爲了方便CI/CD操作,我們可以通過腳本來完成整個容器停止、容器移除、鏡像的移除、鏡像的重新制作以及容器的重新啓動,這樣CI/CD的系統只用調用對應的腳本即可。

示例腳本start.sh如下:

#!/bin/bash
# 停止容器
docker stop channel
echo "停止容器success!"
# 移除容器
docker rm channel
echo "移除容器success!"
# 移除鏡像
docker rmi channel
echo "移除鏡像success!"
# 製作鏡像
docker build -t channel /opt/channel/docker/
echo "製作鏡像success!"
# 啓動容器
docker run -d --name channel -p 8190:8190 -v /opt/channel/logs/:/opt/channel/logs/ channel channel:latest
echo "啓動success!"

執行上述腳本之後,查看容器執行結果:

[bin]# docker ps
CONTAINER ID   IMAGE     COMMAND                  CREATED          STATUS          PORTS                    NAMES
e9eff75cdb6f   channel   "java -jar -Duser.ti…"   30 seconds ago   Up 28 seconds   0.0.0.0:8190->8190/tcp   channel

可以看到容器已經成功啓動。當重新構建新的jar包時,只需對目錄中的jar包進行替換,然後再執行一遍start.sh命令即可。

小結

本文帶大家以具體的實例演示瞭如何製作Docker鏡像,在製作Docker鏡像過程中需要注意的事項,以及製作之後用於CI/CD的腳本編寫。大家可參考以上實例,根據自己的業務場景所需進行對應的改造。

博主簡介:《SpringBoot技術內幕》技術圖書作者,酷愛鑽研技術,寫技術乾貨文章。

公衆號:「程序新視界」,博主的公衆號,歡迎關注~

技術交流:請聯繫博主微信號:zhuan2quan


微信公衆號:程序新視界

程序新視界”,一個100%技術乾貨的公衆號

本文同步分享在 博客“程序新視界”(CSDN)。
如有侵權,請聯繫 [email protected] 刪除。
本文參與“OSC源創計劃”,歡迎正在閱讀的你也加入,一起分享。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章