天天看點

Docker(三):Dockerfile 指令詳解

FROM 指令用于指定其後建構新鏡像所使用的基礎鏡像。FROM 指令必是 Dockerfile 檔案中的首條指令,啟動建構流程後,Docker 将會基于該鏡像建構新鏡像,FROM 後的指令也會基于這個基礎鏡像。

FROM文法格式為:

通過 FROM 指定的鏡像,可以是任何有效的基礎鏡像。FROM 有以下限制:

FROM 必須 是 Dockerfile 中第一條非注釋指令

在一個 Dockerfile 檔案中建立多個鏡像時,FROM 可以多次出現。隻需在每個新指令 FROM 之前,記錄送出上次的鏡像 ID。

tag 或 digest 是可選的,如果不使用這兩個值時,會使用 latest 版本的基礎鏡像

在鏡像的建構過程中執行特定的指令,并生成一個中間鏡像。格式:

RUN 指令将在目前 image 中執行任意合法指令并送出執行結果。指令執行送出後,就會自動執行 Dockerfile 中的下一個指令。

層級 RUN 指令和生成送出是符合 Docker 核心理念的做法。它允許像版本控制那樣,在任意一個點,對 image 鏡像進行定制化建構。

RUN 指令建立的中間鏡像會被緩存,并會在下次建構中使用。如果不想使用這些緩存鏡像,可以在建構時指定 <code>--no-cache</code> 參數,如:<code>docker build --no-cache</code>。

格式:

和 RUN 指令一樣,也有兩種格式,一種類似于指令行,一種類似于函數調用。COPY 指令将從建構上下文目錄中 的檔案/目錄複制到新的一層的鏡像内的<code>&lt;目标路徑&gt;</code>位置。比如:

<code>&lt;源路徑&gt;</code>可以是多個,甚至可以是通配符,其通配符規則要滿足 Go 的 filepath.Match 規則,如:

<code>&lt;目标路徑&gt;</code>可以是容器内的絕對路徑,也可以是相對于工作目錄的相對路徑(工作目錄可以用 WORKDIR 指令來指定)。目标路徑不需要事先建立,如果目錄不存在會在複制檔案前先行建立缺失目錄。

此外,還需要注意一點,使用 COPY 指令,源檔案的各種中繼資料都會保留。比如讀、寫、執行權限、檔案變更時間等。這個特性對于鏡像定制很有用。特别是建構相關檔案都在使用 Git 進行管理的時候。

ADD 指令和 COPY 的格式和性質基本一緻。但是在 COPY 基礎上增加了一些功能。比如<code>&lt;源路徑&gt;</code>可以是一個 URL,這種情況下,Docker 引擎會試圖去下載下傳這個連結的檔案放到<code>&lt;目标路徑&gt;</code>去。

在建構鏡像時,複制上下文中的檔案到鏡像内,格式:

注意

如果 docker 發現檔案内容被改變,則接下來的指令都不會再使用緩存。關于複制檔案時需要處理的/,基本跟正常的 copy 一緻

格式有兩種:

這個指令很簡單,就是設定環境變量而已,無論是後面的其它指令,如 RUN,還是運作時的應用,都可以直接使用這裡定義的環境變量。

這個例子中示範了如何換行,以及對含有空格的值用雙引号括起來的辦法,這和 Shell 下的行為是一緻的。

為建構的鏡像設定監聽端口,使容器在運作時監聽。格式:

EXPOSE 指令并不會讓容器監聽 host 的端口,如果需要,需要在 docker run 時使用 <code>-p</code>、<code>-P</code> 參數來釋出容器端口到 host 的某個端口上。

VOLUME用于建立挂載點,即向基于所建構鏡像創始的容器添加卷:

一個卷可以存在于一個或多個容器的指定目錄,該目錄可以繞過聯合檔案系統,并具有以下功能:

卷可以容器間共享和重用

容器并不一定要和其它容器共享卷

修改卷後會立即生效

對卷的修改不會對鏡像産生影響

卷會一直存在,直到沒有任何容器在使用它

VOLUME 讓我們可以将源代碼、資料或其它内容添加到鏡像中,而又不并送出到鏡像中,并使我們可以多個容器間共享這些内容。

WORKDIR用于在容器内設定一個工作目錄:

通過WORKDIR設定工作目錄後,Dockerfile 中其後的指令 RUN、CMD、ENTRYPOINT、ADD、COPY 等指令都會在該目錄下執行。

如,使用WORKDIR設定工作目錄:

在以上示例中,pwd 最終将會在 <code>/a/b/c</code> 目錄中執行。在使用 docker run 運作容器時,可以通過<code>-w</code>參數覆寫建構時所設定的工作目錄。

USER 用于指定運作鏡像所使用的使用者:

使用USER指定使用者時,可以使用使用者名、UID 或 GID,或是兩者的組合。以下都是合法的指定試:

使用USER指定使用者後,Dockerfile 中其後的指令 RUN、CMD、ENTRYPOINT 都将使用該使用者。鏡像建構完成後,通過 docker run 運作容器時,可以通過 <code>-u</code> 參數來覆寫所指定的使用者。

CMD用于指定在容器啟動時所要執行的指令。CMD 有以下三種格式:

省略可執行檔案的 exec 格式,這種寫法使 CMD 中的參數當做 ENTRYPOINT 的預設參數,此時 ENTRYPOINT 也應該是 exec 格式,具體與 ENTRYPOINT 的組合使用,參考 ENTRYPOINT。

與 RUN 指令的差別:RUN 在建構的時候執行,并生成一個新的鏡像,CMD 在容器運作的時候執行,在建構時不進行任何操作。

ENTRYPOINT 用于給容器配置一個可執行程式。也就是說,每次使用鏡像建立容器時,通過 ENTRYPOINT 指定的程式都會被設定為預設程式。ENTRYPOINT 有以下兩種形式:

ENTRYPOINT 與 CMD 非常類似,不同的是通過<code>docker run</code>執行的指令不會覆寫 ENTRYPOINT,而<code>docker run</code>指令中指定的任何參數,都會被當做參數再次傳遞給 ENTRYPOINT。Dockerfile 中隻允許有一個 ENTRYPOINT 指令,多指定時會覆寫前面的設定,而隻執行最後的 ENTRYPOINT 指令。

<code>docker run</code>運作容器時指定的參數都會被傳遞給 ENTRYPOINT ,且會覆寫 CMD 指令指定的參數。如,執行<code>docker run &lt;image&gt; -d</code>時,-d 參數将被傳遞給入口點。

也可以通過<code>docker run --entrypoint</code>重寫 ENTRYPOINT 入口點。如:可以像下面這樣指定一個容器執行程式:

完整建構代碼:

使用docker build建構鏡像,并将鏡像指定為 itbilu/test:

建構完成後,使用itbilu/test啟動一個容器:

在運作容器時,我們使用了 <code>-g "daemon off;"</code>,這個參數将會被傳遞給 ENTRYPOINT,最終在容器中執行的指令為 <code>/usr/sbin/nginx -g "daemon off;"</code>。

LABEL用于為鏡像添加中繼資料,元數以鍵值對的形式指定:

使用LABEL指定中繼資料時,一條LABEL指定可以指定一或多條中繼資料,指定多條中繼資料時不同中繼資料之間通過空格分隔。推薦将所有的中繼資料通過一條LABEL指令指定,以免生成過多的中間鏡像。

如,通過LABEL指定一些中繼資料:

指定後可以通過docker inspect檢視:

ARG用于指定傳遞給建構運作時的變量:

如,通過ARG指定兩個變量:

以上我們指定了 site 和 build_user 兩個變量,其中 build_user 指定了預設值。在使用 docker build 建構鏡像時,可以通過 <code>--build-arg &lt;varname&gt;=&lt;value&gt;</code> 參數來指定或重設定這些變量的值。

這樣我們建構了 itbilu/test 鏡像,其中site會被設定為 itbilu.com,由于沒有指定 build_user,其值将是預設值 IT 筆錄。

ONBUILD用于設定鏡像觸發器:

當所建構的鏡像被用做其它鏡像的基礎鏡像,該鏡像中的觸發器将會被鑰觸發。

如,當鏡像被使用時,可能需要做一些處理:

STOPSIGNAL用于設定停止容器所要發送的系統調用信号:

所使用的信号必須是核心系統調用表中的合法的值,如:SIGKILL。

SHELL用于設定執行指令(shell式)所使用的的預設 shell 類型:

SHELL在Windows環境下比較有用,Windows 下通常會有 cmd 和 powershell 兩種 shell,可能還會有 sh。這時就可以通過 SHELL 來指定所使用的 shell 類型:

建構Nginx運作環境

建構tomcat 環境

Dockerfile檔案

<code>tomcat7.sh</code>指令檔案

容器輕量化。從鏡像中産生的容器應該盡量輕量化,能在足夠短的時間内停止、銷毀、重新生成并替換原來的容器。

使用 <code>.gitignore</code>。在大部分情況下,Dockerfile 會和建構所需的檔案放在同一個目錄中,為了提高建構的性能,應該使用 <code>.gitignore</code> 來過濾掉不需要的檔案和目錄。

為了減少鏡像的大小,減少依賴,僅安裝需要的軟體包。

一個容器隻做一件事。解耦複雜的應用,分成多個容器,而不是所有東西都放在一個容器内運作。如一個 Python Web 應用,可能需要 Server、DB、Cache、MQ、Log 等幾個容器。一個更加極端的說法:One process per container。

減少鏡像的圖層。不要多個 Label、ENV 等标簽。

對續行的參數按照字母表排序,特别是使用<code>apt-get install -y</code>安裝包的時候。

使用建構緩存。如果不想使用緩存,可以在建構的時候使用參數<code>--no-cache=true</code>來強制重新生成中間鏡像。

<a href="https://docs.docker.com/engine/reference/builder/#usage">Dockerfile reference</a>

<a href="https://www.jianshu.com/p/cbce69c7a52f">使用Dockerfile建構Docker鏡像</a>

<a href="https://itbilu.com/linux/docker/VyhM5wPuz.html">Docker鏡像建構檔案Dockerfile及相關指令介紹</a>

<a href="https://github.com/qianlei90/Blog/issues/35">深入Dockerfile(一): 文法指南</a>

<a href="https://yeasy.gitbooks.io/docker_practice/content/">Docker — 從入門到實踐</a>

本文版權歸作者和部落格園共有,歡迎轉載,但未經作者同意必須保留此段聲明,且在文章頁面明顯位置給出原文連接配接,否則保留追究法律責任的權利。

本文如對您有幫助,還請多幫 <b>【推薦】</b> 下此文。

如果喜歡我的文章,請關注我的公衆号