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 國際」許可協議
。
🤗 您的點贊
和轉發
是對我最大的支持。
📢 掃碼關注 哈嘍沃德先生
「文檔 + 視頻」每篇文章都配有專門視頻講解,學習更輕鬆噢 ~