Docker入門之Dockerfile篇

前面我們使用docker commit只做了一個自己的鏡像,但是這個主要適用於學習以及一些特殊的用途,實際應用一般不會通過這種方式製作鏡像.因爲docker commit只是把變動的文件中紙做成了鏡像,比如之前文章中創建test鏡像的時候,記錄的是創建的/test這個文件夾,但是變動單而過程無法記錄,沒有辦法追蹤到我是通過mkdir /test這個命令創建的/test目錄.這樣的話就非常不利於追蹤歷史和維護,如果要是涉及到編譯安裝軟件等等過程,那就更加讓人崩潰了.其實對我們來說更重要的是想記錄mkdir /test這條命令,因爲只要有這條命令我們就是肯定可以得到/test目錄這個結果.
FROM和RUN
因此 Docker 推出了 Dockerfile 來解救猿類;
Dockerfile 就是一個名叫 Dockerfile 的普通的文本文件;
用來記錄製作鏡像的命令;
我們找個空白文件夾建一個 Dockerfile 文件;
我們之前製作鏡像的時候分爲 3 步;
1.先是拉取鏡像;

docker pull ubuntu

那在 Dockerfile 文件中則是通過 FROM 關鍵字;

FROM ubuntu

2.接着是執行了 mkdir /test 命令;
那在 Dockerfile 文件中則是通過 RUN 關鍵字;

FROM ubuntu

RUN mkdir /test

3.執行 docker commit 製作鏡像
這一步不體現在 Dockerfile 中了;
需要使用 docker build 命令;

docker build -t baijunyao/test:v2

-t baijunyao/test:v2 : 定義鏡像的名稱和版本
. : build 的上下文環境目錄
build 完成後 push 鏡像就跟之前一樣;
這裏就略過不講了;
在這裏插入圖片描述
敲黑板劃重點注意我標註的箭頭了;
接着我要拿它來講解上面說的讓人不太理解的上下文環境目錄;
Docker 跟 MySQL 之類的一樣也分爲客戶端和服務端;
幹活的是服務端;
客戶端主要起調用的作用;
在執行 docker build 的時候是要把文件都發給服務端引擎來 dockerd 處理;
所以這個 . 就是指定上下文目錄的;
意思是把當前目錄的所有文件都發送給 dockerd ;
爲了驗證上面所說;
我們把 /bin/bash 文件複製到當前目錄再運行看看;

cp /bin/bash
docker build -t baijunyao//test:v2

在這裏插入圖片描述
可以看到發送的數據明顯變多了;
但是其實 build 鏡像的時候並不需要在這個 bash 文件;
這種發送全部文件的行爲明顯是不合理的;
要解決這個問題可以藉助 .dockerignore ;
看名字就明顯一股濃濃的山寨 .gitignore 的氣息;
這正是 Docker 的高明之處;
什麼 pull 、push、commit 等等通通使用我們已經熟知的 Git 概念;
極大的減少了我們的學習阻礙;
接着創建 .dockerignore 文件並加入 bash ;

/bash

再次執行 build 命令;
在這裏插入圖片描述
另外針對上面的 RUN 順便提一嘴;
如果要 RUN 多個命令可以用 && 連接;
比如說常見的 apt 安裝;

apt update && apt install busybox

CMD
CMD關鍵字用來指定容器啓動後執行的默認命令,修改下 Dockerfile 定義一個 CMD 輸出 “Dockerfile CMD Commmand” ;
Dockerfile

FROM ubuntu

RUN mkdir /test

CMD echo "Dockerfile CMD Commmand"

build 鏡像;

docker build -t baijunyao/test:v3 .

啓動容器:

docker run baijunyao/test:v3

可以看到在run完後會出輸出這句話:
在這裏插入圖片描述
但是注意上面我說過是默認命令;
既然叫默認值那一般是可以替換的;
如果我們在啓動容器的時候執行了其他命令;
那就不會再執行 CMD 默認命令了;

docker run baijunyao/test:v3 /bin/echo "test"

在這裏插入圖片描述
可以看到 CMD 的默認命令沒有執行;
不同於 RUN 可以定義多個;
CMD 只可以定義一個;
如果定義多個那麼後面的會覆蓋前面的 CMD;
ENTRYPOINT
那如果想定義一個不會被覆蓋的命令就可以使用 ENTRYPOINT ;
Dockerfile

FROM ubuntu

RUN mkdir /test

CMD echo "Dockerfile CMD Commmand"

ENTRYPOINT echo "Dockerfile ENTRYPOINT Commmand"

我們來重新 build ;

docker build -t baijunyao/test:v4 .

運行容器:

docker run baijunyao/test:v4

在這裏插入圖片描述
截圖會出乎你的意料;
“Dockerfile CMD Commmand” 或者說 “test” 都沒有輸出;
“test” 會覆蓋掉 “Dockerfile CMD Commmand” 是可以想到的;
但是連 “test” 都不輸出這就有點說不過去了;
這裏其實是模式的不同;
RUN 、 CMD、ENTRYPOINT 三種關鍵字都有兩種模式;
1.shell 模式 這就是上面我們一直用的模式;
跟在命令行輸入命令一樣;
2.exec 模式
把上面的 shell 改成 exec 模式的話就如下所示;

['/bin/eho','test']

結論來了在 shell 模式下 ENTRYPOINT 會直接覆蓋掉 CMD 的命令;
那我們用 exec 模式改寫下;
Dockerfile

FROM ubuntu

RUN mkdir /test

CMD ["/bin/echo", "Dockerfile CMD Commmand"]

ENTRYPOINT ["/bin/echo", "Dockerfile ENTRYPOINT Commmand"]

再次build運行

docker build -t baijunyao/test:v5 .
docker run baijunyao/test:v5

在這裏插入圖片描述
結果再次出乎意料;
CMD 的命令並沒有正常輸出;
原因是在 exec 模式下;
CMD 命令會被作爲 ENTERPOINT 的參數;
因此上面的命令等於下面這樣;

ENTRYPOINT ["/bin/echo", "Dockerfile ENTRYPOINT Commmand", "/bin/echo test"]

這真是爲難人了;
那有木有一種方案可以讓我正常的符合直覺的同時使用 CMD 和 ENTERPOINT ;
有;但是需要我先把下個關鍵字講了;
COPY
COPY關鍵字用來將宿主機的文件 copy 到鏡像中;
我們上面的 ENTERPOINT 代碼轉移到文件中;
enterpoint.sh

#!/bin/bash

echo "Dockerfile ENTRYPOINT Commmand"

exec "$@"

注意這裏多加了一行 exec “$@” ;
它 的作用是執行接到的參數;
給予寫權限;

chmod +x enterpoint.sh

接着改寫下 Dockerfile 文件;
Dockerfile

FROM ubuntu

RUN mkdir /test

COPY enterpoint.sh /root/docker/

CMD echo "Dockerfile CMD Commmand"

ENTRYPOINT ["/root/docker/enterpoint.sh"]

上面這幾條命令也很容易理解了;
先把宿主機上的 enterpoint.sh 文件複製到容器的 /root/docker/ 目錄下;

再次 build 運行

docker build -t baijunyao/test:v6 .
docker run baijunyao/test:v6

在這裏插入圖片描述
終於如願以償;
這裏需要再次強調上下文環境;
我們 build 命令使用的是 . ;
也就是說我們不能獲取當前目錄外面的內容;
我們來試着把 /bin/bash 文件複製到鏡像中;
Dockerfile

FROM ubuntu

RUN mkdir /test

COPY enterpoint.sh /root/docker/
COPY /bin/bash /root/docker/

CMD echo "Dockerfile CMD Commmand"

ENTRYPOINT ["/root/docker/enterpoint.sh"]

果然是報錯的.
在這裏插入圖片描述
ADD
ADD 和 COPY 差不多;
比較常見的場景是可以用來解壓縮文件;
除了需要解壓文件;
這個關鍵字就記住一句話就行了;
官方推薦用 COPY ;
並不建議用 ADD;
ENV
ENV 用來定義變量;
用空格或者 = 定義;
如果值中有空格就加引號;
比如:

ENV PATH /root/baijunyao
ENV PATH=/root/baijunyao
ENV NAME "Junyao Bai"
ENV NAME="Junyao Bai"

使用變量的時候加上 $ 即可;

FROM ubuntu

ENV DOCKER_PATH=/root/docker

RUN mkdir $DOCKER_PATH

COPY enterpoint.sh $DOCKER_PATH/

CMD echo "Dockerfile CMD Commmand"

ENTRYPOINT ["/root/docker/enterpoint.sh"]

另外需要注意的是有幾個系統的變量不能作爲 key;
比如說 PATH 、 HOME ;
這類變量可以直接在 Dockerfile 中使用;
在這裏插入圖片描述
EXPOSE
EXPOSE 的主要作用就是告訴使用者鏡像的守護端口;
比如說 MySQL 的 Dockerfile 就會定義 EXPOSE 爲 3306 來告訴用戶端口號;

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章