Docker 鏡像構(gòu)建之 Dockerfile

在 Docker 中構(gòu)建鏡像最常用的方式,就是使用 Dockerfile。Dockerfile 是一個(gè)用來(lái)構(gòu)建鏡像的文本文件,文本內(nèi)容包含了一條條構(gòu)建鏡像所需的指令和說(shuō)明。官方文檔:https://docs.docker.com/engine/reference/builder/

Dockerfile 常用指令

FROM

語(yǔ)法:FROM <image>:<tag>

指明構(gòu)建的新鏡像是來(lái)自于哪個(gè)基礎(chǔ)鏡像,如果沒(méi)有選擇 tag,那么默認(rèn)值為 latest。

FROM centos:7

如果不以任何鏡像為基礎(chǔ),那么寫(xiě)法為:FROM scratch。官方說(shuō)明:scratch 鏡像是一個(gè)空鏡像,可以用于構(gòu)建 busybox 等超小鏡像,可以說(shuō)是真正的從零開(kāi)始構(gòu)建屬于自己的鏡像。

MAINTAINER(deprecated)

語(yǔ)法:MAINTAINER <name>

指明鏡像維護(hù)者及其聯(lián)系方式(一般是郵箱地址)。官方說(shuō)明已過(guò)時(shí),推薦使用 LABEL。

MAINTAINER mrhelloworld <mrhelloworld@126.com>

LABEL

語(yǔ)法:LABEL <key>=<value> <key>=<value> <key>=<value> ...

功能是為鏡像指定標(biāo)簽。也可以使用 LABEL 來(lái)指定鏡像作者。

LABEL maintainer="mrhelloworld.com"

RUN

語(yǔ)法:RUN <command>

構(gòu)建鏡像時(shí)運(yùn)行的 Shell 命令,比如構(gòu)建的新鏡像中我們想在 /usr/local 目錄下創(chuàng)建一個(gè) java 目錄。

RUN mkdir -p /usr/local/java

ADD

語(yǔ)法:ADD <src>... <dest>

拷貝文件或目錄到鏡像中。src 可以是一個(gè)本地文件或者是一個(gè)本地壓縮文件,壓縮文件會(huì)自動(dòng)解壓。還可以是一個(gè) url,如果把 src 寫(xiě)成一個(gè) url,那么 ADD 就類(lèi)似于 wget 命令,然后自動(dòng)下載和解壓。

ADD jdk-11.0.6_linux-x64_bin.tar.gz /usr/local/java

COPY

語(yǔ)法:COPY <src>... <dest>

拷貝文件或目錄到鏡像中。用法同 ADD,只是不支持自動(dòng)下載和解壓。

COPY jdk-11.0.6_linux-x64_bin.tar.gz /usr/local/java

EXPOSE

語(yǔ)法:EXPOSE <port> [<port>/<protocol>...]

暴露容器運(yùn)行時(shí)的監(jiān)聽(tīng)端口給外部,可以指定端口是監(jiān)聽(tīng) TCP 還是 UDP,如果未指定協(xié)議,則默認(rèn)為 TCP。

EXPOSE 80 443 8080/tcp

如果想使得容器與宿主機(jī)的端口有映射關(guān)系,必須在容器啟動(dòng)的時(shí)候加上 -P 參數(shù)。

ENV

語(yǔ)法:ENV <key> <value> 添加單個(gè),ENV <key>=<value> ... 添加多個(gè)。

設(shè)置容器內(nèi)環(huán)境變量。

ENV JAVA_HOME /usr/local/java/jdk-11.0.6/

CMD

語(yǔ)法:

  • CMD ["executable","param1","param2"],比如:CMD ["/usr/local/tomcat/bin/catalina.sh", "start"]
  • CMD ["param1","param2"],比如:CMD [ "echo", "$JAVA_HOME" ]
  • CMD command param1 param2,比如:CMD echo $JAVA_HOME

啟動(dòng)容器時(shí)執(zhí)行的 Shell 命令。在 Dockerfile 中只能有一條 CMD 指令。如果設(shè)置了多條 CMD,只有最后一條 CMD 會(huì)生效。

CMD ehco $JAVA_HOME

如果創(chuàng)建容器的時(shí)候指定了命令,則 CMD 命令會(huì)被替代。假如鏡像叫 centos:7,創(chuàng)建容器時(shí)命令是:docker run -it --name centos7 centos:7 echo "helloworld" 或者 docker run -it --name centos7 centos:7 /bin/bash,就不會(huì)輸出 $JAVA_HOME 的環(huán)境變量信息了,因?yàn)?CMD 命令被 echo "helloworld"、/bin/bash 覆蓋了。

ENTRYPOINT

語(yǔ)法:

  • ENTRYPOINT ["executable", "param1", "param2"],比如:ENTRYPOINT ["/usr/local/tomcat/bin/catalina.sh", "start"]
  • ENTRYPOINT command param1 param2,比如:ENTRYPOINT ehco $JAVA_HOME

啟動(dòng)容器時(shí)執(zhí)行的 Shell 命令,同 CMD 類(lèi)似,不會(huì)被 docker run 命令行指定的參數(shù)所覆蓋。在 Dockerfile 中只能有一條 ENTRYPOINT 指令。如果設(shè)置了多條 ENTRYPOINT,只有最后一條 ENTRYPOINT 會(huì)生效。

ENTRYPOINT ehco $JAVA_HOME
  • 如果在 Dockerfile 中同時(shí)寫(xiě)了 ENTRYPOINT 和 CMD,并且 CMD 指令不是一個(gè)完整的可執(zhí)行命令,那么 CMD 指定的內(nèi)容將會(huì)作為 ENTRYPOINT 的參數(shù);
  • 如果在 Dockerfile 中同時(shí)寫(xiě)了 ENTRYPOINT 和 CMD,并且 CMD 是一個(gè)完整的指令,那么它們兩個(gè)會(huì)互相覆蓋,誰(shuí)在最后誰(shuí)生效

WORKDIR

語(yǔ)法:WORKDIR /path/to/workdir

為 RUN、CMD、ENTRYPOINT 以及 COPY 和 AND 設(shè)置工作目錄。

WORKDIR /usr/local

VOLUME

指定容器掛載點(diǎn)到宿主機(jī)自動(dòng)生成的目錄或其他容器。一般的使用場(chǎng)景為需要持久化存儲(chǔ)數(shù)據(jù)時(shí)。

# 容器的 /var/lib/mysql 目錄會(huì)在運(yùn)行時(shí)自動(dòng)掛載為匿名卷,匿名卷在宿主機(jī)的 /var/lib/docker/volumes 目錄下
VOLUME ["/var/lib/mysql"]

一般不會(huì)在 Dockerfile 中用到,更常見(jiàn)的還是在 docker run 的時(shí)候通過(guò) -v 指定數(shù)據(jù)卷。

構(gòu)建鏡像

Dockerfile 文件編寫(xiě)好以后,真正構(gòu)建鏡像時(shí)需要通過(guò) docker build 命令。

docker build 命令用于使用 Dockerfile 創(chuàng)建鏡像。

# 使用當(dāng)前目錄的 Dockerfile 創(chuàng)建鏡像
docker build -t mycentos:7 .
# 通過(guò) -f Dockerfile 文件的位置創(chuàng)建鏡像
docker build -f /usr/local/dockerfile/Dockerfile -t mycentos:7 .
  • -f:指定要使用的 Dockerfile 路徑;
  • --tag, -t:鏡像的名字及標(biāo)簽,可以在一次構(gòu)建中為一個(gè)鏡像設(shè)置多個(gè)標(biāo)簽。

關(guān)于 . 理解

我們?cè)谑褂?docker build 命令去構(gòu)建鏡像時(shí),往往會(huì)看到命令最后會(huì)有一個(gè) . 號(hào)。它究竟是什么意思呢?

很多人以為是用來(lái)指定 Dockerfile 文件所在的位置的,但其實(shí) -f 參數(shù)才是用來(lái)指定 Dockerfile 的路徑的,那么 . 號(hào)究竟是用來(lái)做什么的呢?

Docker 在運(yùn)行時(shí)分為 Docker 引擎(服務(wù)端守護(hù)進(jìn)程)客戶端工具,我們?nèi)粘J褂酶鞣N docker 命令,其實(shí)就是在使用 客戶端工具Docker 引擎 進(jìn)行交互。

當(dāng)我們使用 docker build 命令來(lái)構(gòu)建鏡像時(shí),這個(gè)構(gòu)建過(guò)程其實(shí)是在 Docker 引擎 中完成的,而不是在本機(jī)環(huán)境。如果在 Dockerfile 中使用了一些 ADD 等指令來(lái)操作文件,如何讓 Docker 引擎 獲取到這些文件呢?

這里就有了一個(gè) 鏡像構(gòu)建上下文 的概念,當(dāng)構(gòu)建的時(shí)候,由用戶指定構(gòu)建鏡像時(shí)的上下文路徑,而 docker build 會(huì)將這個(gè)路徑下所有的文件都打包上傳給 Docker 引擎,引擎內(nèi)將這些內(nèi)容展開(kāi)后,就能獲取到上下文中的文件了。

舉個(gè)栗子:我的宿主機(jī) jdk 文件在 /root 目錄下,Dockerfile 文件在 /usr/local/dockerfile 目錄下,文件內(nèi)容如下:

ADD jdk-11.0.6_linux-x64_bin.tar.gz /usr/local/java

那么構(gòu)建鏡像時(shí)的命令就該這樣寫(xiě):

docker build -f /usr/local/dockerfile/Dockerfile -t mycentos:7 /root

再舉個(gè)栗子:我的宿主機(jī) jdk 文件和 Dockerfile 文件都在 /usr/local/dockerfile 目錄下,文件內(nèi)容如下:

ADD jdk-11.0.6_linux-x64_bin.tar.gz /usr/local/java

那么構(gòu)建鏡像時(shí)的命令則這樣寫(xiě):

docker build -f /usr/local/dockerfile/Dockerfile -t mycentos:7 .

Dockerfile 實(shí)踐

接下來(lái)我們通過(guò)基礎(chǔ)鏡像 centos:7,在該鏡像中安裝 jdk 和 tomcat 以后將其制作為一個(gè)新的鏡像 mycentos:7。

創(chuàng)建目錄。

mkdir -p /usr/local/dockerfile

編寫(xiě) Dockerfile 文件。

vi Dockerfile

Dockerfile 文件內(nèi)容如下:

# 指明構(gòu)建的新鏡像是來(lái)自于 centos:7 基礎(chǔ)鏡像
FROM centos:7
# 通過(guò)鏡像標(biāo)簽聲明了作者信息
LABEL maintainer="mrhelloworld.com"
# 設(shè)置工作目錄
WORKDIR /usr/local
# 新鏡像構(gòu)建成功以后創(chuàng)建指定目錄
RUN mkdir -p /usr/local/java && mkdir -p /usr/local/tomcat
# 拷貝文件到鏡像中并解壓
ADD jdk-11.0.6_linux-x64_bin.tar.gz /usr/local/java
ADD apache-tomcat-9.0.37.tar.gz /usr/local/tomcat
# 暴露容器運(yùn)行時(shí)的 8080 監(jiān)聽(tīng)端口給外部
EXPOSE 8080
# 設(shè)置容器內(nèi) JAVA_HOME 環(huán)境變量
ENV JAVA_HOME /usr/local/java/jdk-11.0.6/
ENV PATH $PATH:$JAVA_HOME/bin
# 啟動(dòng)容器時(shí)啟動(dòng) tomcat 并查看 tomcat 日志信息
ENTRYPOINT /usr/local/tomcat/apache-tomcat-9.0.37/bin/startup.sh && tail -f /usr/local/tomcat/apache-tomcat-9.0.37/logs/catalina.out

構(gòu)建鏡像。

docker build -f /usr/local/dockerfile/Dockerfile -t mycentos:7 /root/

[root@localhost ~]# docker build -f /usr/local/dockerfile/Dockerfile -t mycentos:7 /root/
Sending build context to Docker daemon  191.4MB
Error response from daemon: Dockerfile parse error line 1: unknown instruction: NTOS:7
[root@localhost ~]# vi /usr/local/dockerfile/Dockerfile 
[root@localhost ~]# vi /usr/local/dockerfile/Dockerfile 
[root@localhost ~]# docker build -f /usr/local/dockerfile/Dockerfile -t mycentos:7 /root/
Sending build context to Docker daemon  191.4MB
Step 1/10 : FROM centos:7
 ---> 7e6257c9f8d8
Step 2/10 : LABEL maintainer="mrhelloworld.com"
 ---> Running in 4c561fed28a5
Removing intermediate container 4c561fed28a5
 ---> b536fc4e4290
Step 3/10 : WORKDIR /usr/local
 ---> Running in 50141816c10e
Removing intermediate container 50141816c10e
 ---> 030f9db632da
Step 4/10 : RUN mkdir -p /usr/local/java && mkdir -p /usr/local/tomcat
 ---> Running in d1f8f4e008c8
Removing intermediate container d1f8f4e008c8
 ---> 68773de3525a
Step 5/10 : ADD jdk-11.0.6_linux-x64_bin.tar.gz /usr/local/java
 ---> 92f410a9e1ba
Step 6/10 : ADD apache-tomcat-9.0.37.tar.gz /usr/local/tomcat
 ---> 8e0576fccd4e
Step 7/10 : EXPOSE 8080
 ---> Running in bb6c4ef8f4e1
Removing intermediate container bb6c4ef8f4e1
 ---> 01edd4710cc1
Step 8/10 : ENV JAVA_HOME /usr/local/java/jdk-11.0.6/
 ---> Running in 722c2d369a2f
Removing intermediate container 722c2d369a2f
 ---> ef5172fb1dd6
Step 9/10 : ENV PATH $PATH:$JAVA_HOME/bin
 ---> Running in eaa287937565
Removing intermediate container eaa287937565
 ---> 0347db73b904
Step 10/10 : ENTRYPOINT /usr/local/tomcat/apache-tomcat-9.0.37/bin/startup.sh && tail -f /usr/local/tomcat/apache-tomcat-9.0.37/logs/catalina.out
 ---> Running in 8f93d36f4de2
Removing intermediate container 8f93d36f4de2
 ---> 1674e0191270
Successfully built 1674e0191270
Successfully tagged mycentos:7

鏡像構(gòu)建歷史

docker history 鏡像名稱:標(biāo)簽|ID
docker history mycentos:7

使用構(gòu)建的鏡像創(chuàng)建容器

# 創(chuàng)建容器
docker run -di --name mycentos7 -p 8080:8080 mycentos:7
# 進(jìn)入容器
docker exec -it mycentos7 /bin/bash
# 測(cè)試 java 環(huán)境變量
[root@dcae87df010b /]# java -version
java version "11.0.6" 2020-01-14 LTS
Java(TM) SE Runtime Environment 18.9 (build 11.0.6+8-LTS)
Java HotSpot(TM) 64-Bit Server VM 18.9 (build 11.0.6+8-LTS, mixed mode)
# 訪問(wèn) http://192.168.10.10:8080/ 看到頁(yè)面說(shuō)明環(huán)境 OK!

太棒了,Dockerfile 構(gòu)建鏡像的方式你也學(xué)會(huì)了,再接再厲學(xué)習(xí)一下 Docker 鏡像的備份恢復(fù)遷移,go ~

本文采用 知識(shí)共享「署名-非商業(yè)性使用-禁止演繹 4.0 國(guó)際」許可協(xié)議

大家可以通過(guò) 分類(lèi) 查看更多關(guān)于 Docker 的文章。


?? 您的點(diǎn)贊轉(zhuǎn)發(fā)是對(duì)我最大的支持。

?? 關(guān)注公眾號(hào) 哈嘍沃德先生「文檔 + 視頻」每篇文章都配有專(zhuān)門(mén)視頻講解,學(xué)習(xí)更輕松噢 ~


最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

友情鏈接更多精彩內(nèi)容