Docker 鏡像構建之 Dockerfile

在 Docker 中構建鏡像最常用的方式,就是使用 Dockerfile。Dockerfile 是一個用來構建鏡像的文本文件,文本內容包含了一條條構建鏡像所需的指令和說明。官方文檔://docs.docker.com/engine/reference/builder/

  

Dockerfile 常用指令

  

FROM

  

  語法:FROM <image>:<tag>

  指明構建的新鏡像是來自於哪個基礎鏡像,如果沒有選擇 tag,那麼默認值為 latest。

FROM centos:7

  如果不以任何鏡像為基礎,那麼寫法為:FROM scratch。官方說明:scratch 鏡像是一個空鏡像,可以用於構建 busybox 等超小鏡像,可以說是真正的從零開始構建屬於自己的鏡像。

  

MAINTAINER(deprecated)

  

  語法:MAINTAINER <name>

  指明鏡像維護者及其聯繫方式(一般是郵箱地址)。官方說明已過時,推薦使用 LABEL。

MAINTAINER mrhelloworld <[email protected]>

  

LABEL

  

  語法:LABEL <key>=<value> <key>=<value> <key>=<value> ...

  功能是為鏡像指定標籤。也可以使用 LABEL 來指定鏡像作者。

LABEL maintainer="mrhelloworld.com"

  

RUN

  

  語法:RUN <command>

  構建鏡像時運行的 Shell 命令,比如構建的新鏡像中我們想在 /usr/local 目錄下創建一個 java 目錄。

RUN mkdir -p /usr/local/java

  

ADD

  

  語法:ADD <src>... <dest>

  拷貝文件或目錄到鏡像中。src 可以是一個本地文件或者是一個本地壓縮文件,壓縮文件會自動解壓。還可以是一個 url,如果把 src 寫成一個 url,那麼 ADD 就類似於 wget 命令,然後自動下載和解壓。

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

  

COPY

  

  語法:COPY <src>... <dest>

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

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

  

EXPOSE

  

  語法:EXPOSE <port> [<port>/<protocol>...]

  暴露容器運行時的監聽端口給外部,可以指定端口是監聽 TCP 還是 UDP,如果未指定協議,則默認為 TCP。

EXPOSE 80 443 8080/tcp

如果想使得容器與宿主機的端口有映射關係,必須在容器啟動的時候加上 -P 參數。

  

ENV

  

  語法:ENV <key> <value> 添加單個,ENV <key>=<value> ... 添加多個。

  設置容器內環境變量。

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

  

CMD

  

  語法:

  • 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

  啟動容器時執行的 Shell 命令。在 Dockerfile 中只能有一條 CMD 指令。如果設置了多條 CMD,只有最後一條 CMD 會生效。

CMD ehco $JAVA_HOME

  如果創建容器的時候指定了命令,則 CMD 命令會被替代。假如鏡像叫 centos:7,創建容器時命令是:docker run -it --name centos7 centos:7 echo "helloworld" 或者 docker run -it --name centos7 centos:7 /bin/bash,就不會輸出 $JAVA_HOME 的環境變量信息了,因為 CMD 命令被 echo "helloworld"/bin/bash 覆蓋了。

  

ENTRYPOINT

  

  語法:

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

  啟動容器時執行的 Shell 命令,同 CMD 類似,不會被 docker run 命令行指定的參數所覆蓋。在 Dockerfile 中只能有一條 ENTRYPOINT 指令。如果設置了多條 ENTRYPOINT,只有最後一條 ENTRYPOINT 會生效。

ENTRYPOINT ehco $JAVA_HOME
  • 如果在 Dockerfile 中同時寫了 ENTRYPOINT 和 CMD,並且 CMD 指令不是一個完整的可執行命令,那麼 CMD 指定的內容將會作為 ENTRYPOINT 的參數;
  • 如果在 Dockerfile 中同時寫了 ENTRYPOINT 和 CMD,並且 CMD 是一個完整的指令,那麼它們兩個會互相覆蓋,誰在最後誰生效

  

WORKDIR

  

  語法:WORKDIR /path/to/workdir

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

WORKDIR /usr/local

  

VOLUME

  

  指定容器掛載點到宿主機自動生成的目錄或其他容器。一般的使用場景為需要持久化存儲數據時。

# 容器的 /var/lib/mysql 目錄會在運行時自動掛載為匿名卷,匿名卷在宿主機的 /var/lib/docker/volumes 目錄下
VOLUME ["/var/lib/mysql"]

一般不會在 Dockerfile 中用到,更常見的還是在 docker run 的時候通過 -v 指定數據卷。

  

構建鏡像

  

  Dockerfile 文件編寫好以後,真正構建鏡像時需要通過 docker build 命令。

  docker build 命令用於使用 Dockerfile 創建鏡像。

# 使用當前目錄的 Dockerfile 創建鏡像
docker build -t mycentos:7 .
# 通過 -f Dockerfile 文件的位置創建鏡像
docker build -f /usr/local/dockerfile/Dockerfile -t mycentos:7 .
  • -f:指定要使用的 Dockerfile 路徑;
  • --tag, -t:鏡像的名字及標籤,可以在一次構建中為一個鏡像設置多個標籤。

  

關於 . 理解

  

  我們在使用 docker build 命令去構建鏡像時,往往會看到命令最後會有一個 . 號。它究竟是什麼意思呢?

  很多人以為是用來指定 Dockerfile 文件所在的位置的,但其實 -f 參數才是用來指定 Dockerfile 的路徑的,那麼 . 號究竟是用來做什麼的呢?

  Docker 在運行時分為 Docker 引擎(服務端守護進程)客戶端工具,我們日常使用各種 docker 命令,其實就是在使用 客戶端工具Docker 引擎 進行交互。

  當我們使用 docker build 命令來構建鏡像時,這個構建過程其實是在 Docker 引擎 中完成的,而不是在本機環境。如果在 Dockerfile 中使用了一些 ADD 等指令來操作文件,如何讓 Docker 引擎 獲取到這些文件呢?

  這裡就有了一個 鏡像構建上下文 的概念,當構建的時候,由用戶指定構建鏡像時的上下文路徑,而 docker build 會將這個路徑下所有的文件都打包上傳給 Docker 引擎,引擎內將這些內容展開後,就能獲取到上下文中的文件了。

  

  舉個栗子:我的宿主機 jdk 文件在 /root 目錄下,Dockerfile 文件在 /usr/local/dockerfile 目錄下,文件內容如下:

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

  那麼構建鏡像時的命令就該這樣寫:

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

  

  再舉個栗子:我的宿主機 jdk 文件和 Dockerfile 文件都在 /usr/local/dockerfile 目錄下,文件內容如下:

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

  那麼構建鏡像時的命令則這樣寫:

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

  

Dockerfile 實踐

  

  接下來我們通過基礎鏡像 centos:7,在該鏡像中安裝 jdk 和 tomcat 以後將其製作為一個新的鏡像 mycentos:7

  創建目錄。

mkdir -p /usr/local/dockerfile

  編寫 Dockerfile 文件。

vi Dockerfile

  Dockerfile 文件內容如下:

# 指明構建的新鏡像是來自於 centos:7 基礎鏡像
FROM centos:7
# 通過鏡像標籤聲明了作者信息
LABEL maintainer="mrhelloworld.com"
# 設置工作目錄
WORKDIR /usr/local
# 新鏡像構建成功以後創建指定目錄
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
# 暴露容器運行時的 8080 監聽端口給外部
EXPOSE 8080
# 設置容器內 JAVA_HOME 環境變量
ENV JAVA_HOME /usr/local/java/jdk-11.0.6/
ENV PATH $PATH:$JAVA_HOME/bin
# 啟動容器時啟動 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

  構建鏡像。

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

  

鏡像構建歷史

  

docker history 鏡像名稱:標籤|ID
docker history mycentos:7

  

使用構建的鏡像創建容器

  

# 創建容器
docker run -di --name mycentos7 -p 8080:8080 mycentos:7
# 進入容器
docker exec -it mycentos7 /bin/bash
# 測試 java 環境變量
[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)
# 訪問 //192.168.10.10:8080/ 看到頁面說明環境 OK!

太棒了,Dockerfile 構建鏡像的方式你也學會了,再接再厲學習一下 Docker 鏡像的備份恢復遷移,go ~

本文採用 知識共享「署名-非商業性使用-禁止演繹 4.0 國際」許可協議

大家可以通過 分類 查看更多關於 Docker 的文章。

  

🤗 您的點贊轉發是對我最大的支持。

📢 掃碼關注 哈嘍沃德先生「文檔 + 視頻」每篇文章都配有專門視頻講解,學習更輕鬆噢 ~