前言
上一章節,主要是介紹了下
Dockerfile的一些常用命令的說明。我們知道,利用Dockerfile可以構建一個新的鏡像,比如運行Java環境,就需要一個JDK環境的鏡像,但直接使用公共的鏡像時,一般上大小都比較大。所以本章節就主要結合Dockerfile文件及commit方式,構建屬於自己的鏡像,同時對鏡像進行壓縮和優化,同時也是對Dockerfile知識的一個實踐。
利用Dockerfile構建自定義鏡像
作為一個
java后端開發,這里就直接以構建一個Oracle官方jre環境來示例。
選定基礎鏡像
由於在Linux中,
JVM主要是調用系統的C語言庫,Oracle的官方JRE,使用的是libc,也就是glibc,這意味着你要運行任何Java程序,都需要先裝好glibc。所以我們直接去https://hub.docker.com找一個基於glibc的基礎鏡像(當然了,大家也可直接選定比如CensOS這些Linux的發行版本了)。

我們之類直接選擇默認排在第一個的alpine-glibc作為我們的基礎鏡像,比較這個大小也才12M左右呀!
題外話:大家作為實驗性質時,為了獲取更小的基礎鏡像,可以選擇alpine這個基礎鏡像,比較這個只有5M大小,夠精簡了!

准備JRE版本
這里我們直接去Oracle官網選擇
jre版本,這里選擇的是jre-8u181-linux-x64版本(由於對linux命令不是很熟悉,為了不必要的時間浪費,這里直接下載了鏡像了,熟悉的各位可以直接使用wget進行下載的)。

編寫Dockerfile
# 基礎鏡像
FROM frolvlad/alpine-glibc
LABEL MAINTAINER oKong <499452441@qq.com>
# 將JRE添加至鏡像中,add 命令在源文件為壓縮文件時,會自動解壓的
ADD jre-8u181-linux-x64.tar.gz /opt/docker/java/jre8
# 設置JAVA環境變量
# 這里需要注意下,解壓后有個目錄的,為jre1.8.0_181,一開始沒注意,啟動時報了:exec: "java": executable file not found in $PATH: unknown 后才發現。
ENV JAVA_HOME /opt/docker/java/jre8/jre1.8.0_181
ENV CLASSPATH=$JAVA_HOME/bin
ENV PATH=.:$JAVA_HOME/bin:$PATH
# 這里無實際意義,只是在容器啟動時,輸出jre版本信息,驗證是否安裝成功
CMD ["java","-version"]
利用build命令,構建鏡像,同時指定tag:
docker build -t lqdev.cn/jre8:0.1 .
注意:后面有個.的。
Sending build context to Docker daemon 81.19MB
Step 1/7 : FROM frolvlad/alpine-glibc
---> d3bc626306a1
Step 2/7 : LABEL MAINTAINER oKong <499452441@qq.com>
---> Running in e788d29cd1e1
Removing intermediate container e788d29cd1e1
---> 5d95db4ae169
Step 3/7 : ADD jre-8u181-linux-x64.tar.gz /opt/docker/java/jre8
---> 0f4bb83df722
Step 4/7 : ENV JAVA_HOME /opt/docker/java/jre8/jre1.8.0_181
---> Running in 57a1e1ef00ed
Removing intermediate container 57a1e1ef00ed
---> 6f2b543a91b7
Step 5/7 : ENV PATH ${PATH}:${JAVA_HOME}/bin
---> Running in 2d75c88f97fb
Removing intermediate container 2d75c88f97fb
---> 92a7a0f9926c
Step 6/7 : WORKDIR /opt/docker/java/jre8/jre1.8.0_181
---> Running in 7b9a69efc980
Removing intermediate container 7b9a69efc980
---> 158c08c995c3
Step 7/7 : CMD ["java","-version"]
---> Running in 9ab517f8292a
Removing intermediate container 9ab517f8292a
---> 9c8606ac315a
Successfully built 9c8606ac315a
Successfully tagged lqdev.cn/jre8:0.1
然后查看下鏡像列表,
docker images

說明已經構建成功了,我們來運行下:
docker run -it -d --name myfirstjre lqdev.cn/jre8:0.1
查看下容器列表:
docker ps -a

由於Dockerfile中使用CMD命令覆蓋了原本的/bin/sh,容器已啟動就停止了。所以我們看下日志,就知道是否jre安裝成功。
docker logs -f c6873a97ff49

輸出了版本信息了,說明已經安裝成功了。
實踐:運行SpringBoot的jar包
現在我們就可以基於這個jre鏡像進行實際的jar包部署了。
Dockerfile文件方式:
# 基於我們自定義構建的鏡像
FROM lqdev.cn/jre8:0.1
VOLUME /opt/tmp
# 這是jar 可自行選擇,這里直接使用了原先講解springboot十四章節:基於Docker部署時的jar包
ADD chapter-14-0.0.1-SNAPSHOT.jar app.jar
# -Djava.security.egd=file:/dev/./urandom 可解決tomcat可能啟動慢的問題
# 具體可查看:https://www.cnblogs.com/mightyvincent/p/7685310.html
ENTRYPOINT ["java","-Djava.security.egd=file:/dev/./urandom","-jar","/app.jar"]
# 對外端口
EXPOSE 8080
然后構建新鏡像,並運行,同時查看日志輸出
docker run -it -d --name springboot lqdev.cn/springboot:0.1
日志控制台:

運行的容器列表:

說明我們已經成功了,jar也啟動了。現在訪問下部署的jar服務:

利用commit命令構建自定義鏡像
在第三章講解
Docker常用命令時,有說到,利用commit可從容器中構建一個新的鏡像。所以這里簡單講解下利用此命令進行自動鏡像的構建過程。
構建思路:我們啟動一個基礎鏡像,同時運行,然后我們進入容器,下載所需要的jre版本,並配置其環境變量。之后退出容器進行保存操作。這里就不直接下載了,我們直接拷貝jre到容器里
啟動基礎鏡像:
docker run -it -d --name commitImages frolvlad/alpine-glibc
拷貝jre到容器中。
docker cp /opt/docker/java/jre-8u181-linux-x64.tar.gz b0d354b9453a:/opt/docker/java/jre8
這里會提示,說目錄不存在,可利用exec命令,進入容器創建目錄下,這里就不演示了。
No such container:path: b0d354b9453a:/opt/docker/java
現在我們進入容器:
docker exec -it b0d354b9453a /bin/sh
進入,/opt/docker/java/jre8目錄,進行常規linux下的jre安裝:
cd /opt/docker/java/jre8
# 解壓
tar -xzvf jre-8u181-linux-x64.tar.gz
# 配置環境變量,vi /etc/profile 末尾加入
export JAVA_HOME=/opt/docker/java/jre8/jre1.8.0_181
export CLASSPATH=$JAVA_HOME/bin
export PATH=.:$JAVA_HOME/bin:$PATH
# 生效配置
source /etc/profile
# 驗證是否成功
java -version
輸出:
java version "1.8.0_181"
Java(TM) SE Runtime Environment (build 1.8.0_181-b13)
Java HotSpot(TM) 64-Bit Server VM (build 25.181-b13, mixed mode)
# 這里有個坑,生效配置后退出容器后又失效了,搜索了后,把環境變量放在### ~/.bashrc 或者在~/.bashrc里面加一句source /etc/profile 但是還是未生效。。。。我放棄了。。 直接寫個sh腳本啟動 時加入吧。。
然后我們退出容器,利用commit命令進行構建新鏡像:
docker commit b0d354b9453a lqdev.cn/jre8:0.2
然后查看:

運行jar,驗證下是否正常,這里直接在啟動的時候拷貝jar到鏡像。
docker run -it -d -p 1234:8080 -v /opt/docker/java:/opt/docker/java/app.jar --name springboot2 lqdev.cn/jre8:0.2
然后進入容器運行下java 命令
# 進入容器
docker exec -it 583d0f387555 /bin/sh
# 生效配置
source etc/profile
# 運行jar
nohup java -Djava.security.egd=file:/dev/./urandom -jar /opt/docker/java/app.jar >log.txt &
退出后,訪問宿主的1234端口服務,就能看見部署成功了:

其實比較好的做法是:創建一個sh腳本,腳本里設置了環境生效命令及java命令即可,大家可自行嘗試下,這里就不演示了。
鏡像文件壓縮技巧
上一章節,我們利用Dockerfile和commit的方式生成鏡像。現在我們看下,鏡像文件大小:

兩種方式,commit還多了80M多。這里我們本着精簡的原則,對鏡像大小進行優化下。
首先,鏡像文件是按
鏡像層(Layers)進行疊加的。總的來說就是:每一條指令都會創建一個鏡像層,繼而會增加整體鏡像的大小。
選擇一個精簡的基礎鏡像
一個基礎鏡像的大小直接決定了新鏡像的大小,所以可以選擇盡量小的精簡的鏡像。本文就使用了alpine-glibc作為基礎鏡像,大小12.8M。
串聯RUN命令
多個RUN時,可通過 && 和 / 支持將命令串聯在一起
# 舉例
RUN yum -y install java-1.7.0-openjdk-devel && yum clean all
很多鏡像大部分都是通過此方式進行RUN方式編寫的。官網:https://hub.docker.com/里面的大部分鏡像都是如下寫法

刪除多余文件及命令安裝包等
比如jre包中就有很多的文檔及說明文件是非必要的,這些就可以刪除了。以下只是個參考,大家可以自行刪減,可以在Dockerfile編寫時,解壓后,使用RUN命令進行操作也可以直接把壓縮包里刪除后在拷貝:
rm -rf COPYRIGHT LICENSE README release THIRDPARTYLICENSEREADME-JAVAFX.txtTHIRDPARTYLICENSEREADME.txt Welcome.html
#刪除其他無用文件
rm -rf lib/plugin.jar \
lib/ext/jfxrt.jar \
bin/javaws \
lib/javaws.jar \
lib/desktop \
plugin \
lib/deploy* \
lib/*javafx* \
lib/*jfx* \
lib/amd64/libdecora_sse.so \
lib/amd64/libprism_*.so \
lib/amd64/libfxplugins.so \
lib/amd64/libglass.so \
lib/amd64/libgstreamer-lite.so \
lib/amd64/libjavafx*.so \
lib/amd64/libjfx*.so
對比下,確實少了很多了。

當然對於大小不關心的,也就無需理會了,畢竟現在存儲空間都很大的,也就可能傳輸的時候慢點,哈哈~
參考資料
鏡像優化的大家可看看以下幾篇文章或者自行搜索下相關資料:
- https://blog.csdn.net/qq_36763896/article/details/53293088
- https://my.oschina.net/shyloveliyi/blog/1627020
- https://blog.csdn.net/chenyufeng1991/article/details/78766123
總結
本章節主要是介紹如何利用
Dockerfile或者commit方式構建自定義鏡像。通過這兩種方式,我們就能根據自己的實際業務需要進行個性化改造、優化,最終構建一個通用鏡像。在構建自己的鏡像時,盡量還是選擇自己熟悉的、穩定的基礎環境鏡像進行構建,畢竟出了問題找起來也熟門熟路點。通常,運維部門或者實施部門,制定的鏡像屬於資產,一般不會上送至Docker遠程倉庫的,有了鏡像,我們就需要有個地方去存儲。下一章節,就重點講解下如何構建私有倉庫,管理自己的鏡像文件!
最后
若文中有錯誤或者遺漏之處,還望指出,共同進步!
老生常談
- 個人QQ:
499452441 - 微信公眾號:
lqdevOps

個人博客:http://blog.lqdev.cn
