在 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í)更輕松噢 ~