Dockerfile指令詳解

谢友海發表於2024-10-22

Dockerfile 是一個用來構建映象的文字檔案,文字內容包含了一條條構建映象所需的指令和說明。

首先透過一張圖來了解 Docker 映象、容器和 Dockerfile 三者之間的關係。

透過上圖可以看出使用 Dockerfile 定義映象,執行映象啟動容器。

一、Dockerfile概念

Docker 映象是一個特殊的檔案系統,除了提供容器執行時所需的程式、庫、資源、配置等檔案外,還包含了一些為執行時準備的一些配置引數(如匿名卷、環境變數、使用者等)。映象不包含任何動態資料,其內容在構建之後也不會被改變。

映象的定製實際上就是定製每一層所新增的配置、檔案。如果我們可以把每一層修改、安裝、構建、操作的命令都寫入一個指令碼,用這個指令碼來構建、定製映象,那麼之前提及的無法重複的問題、映象構建透明性的問題、體積的問題就都會解決。這個指令碼就是 Dockerfile。

Dockerfile 是一個文字檔案,其內包含了一條條的指令(Instruction),每一條指令構建一層,因此每一條指令的內容,就是描述該層應當如何構建。有了Dockerfile,當我們需要定製自己額外的需求時,只需在 Dockerfile 上新增或者修改指令,重新生成 image 即可,省去了敲命令的麻煩。

二、Dockerfile指令

1、FROM 指定基礎映象

FROM 指令用於指定其後構建新映象所使用的基礎映象。FROM 指令必是 Dockerfile 檔案中的首條命令,啟動構建流程後,Docker 將會基於該映象構建新映象,FROM 後的命令也會基於這個基礎映象。

FROM語法格式為:

FROM <image>

FROM <image>:<tag>

FROM <image>:<digest>

透過 FROM 指定的映象,可以是任何有效的基礎映象。FROM 有以下限制:

  • FROM 必須 是 Dockerfile 中第一條非註釋命令。
  • 在一個 Dockerfile 檔案中建立多個映象時,FROM 可以多次出現。只需在每個新命令 FROM 之前,記錄提交上次的映象 ID。
  • tag 或 digest 是可選的,如果不使用這兩個值時,會使用 latest 版本的基礎映象。

2、RUN 執行命令

在映象的構建過程中執行特定的命令,並生成一箇中間映象。

#shell格式
RUN <command>

#exec格式
RUN ["executable", "param1", "param2"]
  • RUN 命令將在當前 image 中執行任意合法命令並提交執行結果。命令執行提交後,就會自動執行 Dockerfile 中的下一個指令。
  • 層級 RUN 指令和生成提交是符合 Docker 核心理念的做法。它允許像版本控制那樣,在任意一個點,對 image 映象進行定製化構建。
  • RUN 指令建立的中間映象會被快取,並會在下次構建中使用。如果不想使用這些快取映象,可以在構建時指定 --no-cache 引數,如:docker build --no-cache。

3、COPY 複製檔案

COPY <源路徑>... <目標路徑>
COPY ["<源路徑1>",... "<目標路徑>"]

和 RUN 指令一樣,也有兩種格式,一種類似於命令列,一種類似於函式呼叫。COPY 指令將從構建上下文目錄中 <源路徑> 的檔案/目錄複製到新的一層的映象內的<目標路徑>位置。比如:

COPY package.json /usr/src/app/

<源路徑>可以是多個,甚至可以是萬用字元,其萬用字元規則要滿足 Go 的 filepath.Match 規則,如:

COPY hom* /mydir/
COPY hom?.txt /mydir/

<目標路徑>可以是容器內的絕對路徑,也可以是相對於工作目錄的相對路徑(工作目錄可以用 WORKDIR 指令來指定)。目標路徑不需要事先建立,如果目錄不存在會在複製檔案前先行建立缺失目錄。

此外,還需要注意一點,使用 COPY 指令,原始檔的各種後設資料都會保留。比如讀、寫、執行許可權、檔案變更時間等。這個特性對於映象定製很有用。特別是構建相關檔案都在使用 Git 進行管理的時候。

4、ADD 更高階的複製檔案

ADD 指令和 COPY 的格式和性質基本一致。

在 Docker 官方的 Dockerfile 最佳實踐文件 中要求,儘可能的使用 COPY,因為 COPY 的語義很明確,就是複製檔案而已,而 ADD 則包含了更復雜的功能,其行為也不一定很清晰。最適合使用 ADD 的場合,就是所提及的需要自動解壓縮的場合。

另外需要注意的是,ADD 指令會令映象構建快取失效,從而可能會令映象構建變得比較緩慢。

因此在 COPY 和 ADD 指令中選擇的時候,可以遵循這樣的原則,所有的檔案複製均使用 COPY 指令,僅在需要自動解壓縮的場合使用 ADD。

在構建映象時,複製上下文中的檔案到映象內,格式:

ADD <源路徑>... <目標路徑>
ADD ["<源路徑>",... "<目標路徑>"]

5、ENV 設定環境變數

格式有兩種:

ENV <key> <value>
ENV <key1>=<value1> <key2>=<value2>...

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

ENV VERSION=1.0 DEBUG=on \
    NAME="Happy Feet"

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

6、EXPOSE

為構建的映象設定監聽埠,使容器在執行時監聽。格式:

EXPOSE <port> [<port>...]

EXPOSE 指令並不會讓容器監聽 host 的埠,如果需要,需要在 docker run 時使用 -p、-P 引數來發布容器埠到 host 的某個埠上。

7、VOLUME 定義匿名卷

定義匿名資料卷。在啟動容器時忘記掛載資料卷,會自動掛載到匿名卷。

作用:

  • 避免重要的資料,因容器重啟而丟失,這是非常致命的。
  • 避免容器不斷變大。

格式:

VOLUME ["<路徑1>", "<路徑2>"...]
VOLUME <路徑>

在啟動容器 docker run 的時候,我們可以透過 -v 引數修改掛載點。

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

  • 卷可以由容器間共享和重用
  • 容器並不一定要和其它容器共享卷
  • 修改卷後會立即生效
  • 對卷的修改不會對映象產生影響
  • 卷會一直存在,直到沒有任何容器在使用它

8、CMD 容器啟動命令

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

CMD ["executable","param1","param2"]
CMD ["param1","param2"]
CMD command param1 param2

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

注意 與 RUN 指令的區別:RUN 在構建的時候執行,並生成一個新的映象,CMD 在容器執行的時候執行,在構建時不進行任何操作。

9、ENTRYPOINT 入口點

ENTRYPOINT 指定這個容器啟動的時候要執行的命令,可以追加命令。

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

ENTRYPOINT ["executable", "param1", "param2"]
ENTRYPOINT command param1 param2

ENTRYPOINT 與 CMD 非常類似,不同的是透過docker run執行的命令不會覆蓋 ENTRYPOINT,而docker run命令中指定的任何引數,都會被當做引數再次傳遞給 ENTRYPOINT。Dockerfile 中只允許有一個 ENTRYPOINT 命令,多指定時會覆蓋前面的設定,而只執行最後的 ENTRYPOINT 指令。

docker run執行容器時指定的引數都會被傳遞給 ENTRYPOINT ,且會覆蓋 CMD 命令指定的引數。如,執行docker run <image> -d時,-d 引數將被傳遞給入口點。

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

ENTRYPOINT ["/usr/bin/nginx"]

10、USER 指定當前使用者

USER 用於指定執行映象所使用的使用者:

USER daemon

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

USER user
USER user:group
USER uid
USER uid:gid
USER user:gid
USER uid:group

使用USER指定使用者後,Dockerfile 中其後的命令 RUN、CMD、ENTRYPOINT 都將使用該使用者。映象構建完成後,透過 docker run 執行容器時,可以透過 -u 引數來覆蓋所指定的使用者。

11、WORKDIR 指定工作目錄

WORKDIR用於在容器內設定一個工作目錄:

WORKDIR /path/to/workdir

透過WORKDIR設定工作目錄後,Dockerfile 中其後的命令 RUN、CMD、ENTRYPOINT、ADD、COPY 等命令都會在該目錄下執行。 如,使用WORKDIR設定工作目錄:

WORKDIR /a
WORKDIR b
WORKDIR c
RUN pwd

在以上示例中,pwd 最終將會在 /a/b/c 目錄中執行。在使用 docker run 執行容器時,可以透過-w引數覆蓋構建時所設定的工作目錄。

12、LABEL 為映象新增後設資料

LABEL 指令用來給映象新增一些後設資料(metadata),以鍵值對的形式,語法格式如下:

LABEL <key>=<value> <key>=<value> <key>=<value> ...

比如我們可以新增映象的作者:

LABEL org.opencontainers.image.authors="runoob"

使用LABEL指定後設資料時,一條LABEL指令可以指定一條或多條後設資料,指定多條後設資料時不同後設資料之間透過空格分隔。推薦將所有的後設資料透過一條LABEL指令指定,以免生成過多的中間映象。 如,透過LABEL指定一些後設資料:

LABEL version="1.0" description="這是一個Web伺服器" by="Docker筆錄"

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

docker inspect itbilu/test
"Labels": {
    "version": "1.0",
    "description": "這是一個Web伺服器",
    "by": "Docker筆錄"
},

13、ARG 構建引數

構建引數,與 ENV 作用一致。不過作用域不一樣。ARG 設定的環境變數僅對 Dockerfile 內有效,也就是說只有 docker build 的過程中有效,構建好的映象內不存在此環境變數。

構建命令 docker build 中可以用 --build-arg <引數名>=<值> 來覆蓋。

格式:

ARG <引數名>[=<預設值>]

ARG用於指定傳遞給構建執行時的變數,例如:透過ARG指定兩個變數

ARG site
ARG build_user=IT筆錄

以上我們指定了 site 和 build_user 兩個變數,其中 build_user 指定了預設值。在使用 docker build 構建映象時,可以透過 --build-arg <varname>=<value> 引數來指定或重設定這些變數的值。

docker build --build-arg site=itiblu.com -t itbilu/test .

這樣我們構建了 itbilu/test 映象,其中site會被設定為 itbilu.com,由於沒有指定 build_user,其值將是預設值 “IT 筆錄”。

14、ONBUILD

用於延遲構建命令的執行。簡單的說,就是 Dockerfile 裡用 ONBUILD 指定的命令,在本次構建映象的過程中不會執行(假設映象為 test-build)。當有新的 Dockerfile 使用了之前構建的映象 FROM test-build ,這時執行新映象的 Dockerfile 構建時候,會執行 test-build 的 Dockerfile 裡的 ONBUILD 指定的命令。

格式:

ONBUILD <其它指令>

當所構建的映象被用做其它映象的基礎映象,該映象中的觸發器將會被觸發。 如,當映象被使用時,可能需要做一些處理:

[...]
ONBUILD ADD . /app/src
ONBUILD RUN /usr/local/bin/python-build --dir /app/src
[...]

15、STOPSIGNAL

STOPSIGNAL用於設定停止容器所要傳送的系統呼叫訊號:

STOPSIGNAL signal

所使用的訊號必須是核心系統呼叫表中的合法的值,如:SIGKILL。

16、SHELL 指令

SHELL用於設定執行命令(shell式)所使用的的預設 shell 型別:

SHELL ["executable", "parameters"]

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

FROM microsoft/windowsservercore

# Executed as cmd /S /C echo default
RUN echo default

# Executed as cmd /S /C powershell -command Write-Host default
RUN powershell -command Write-Host default

# Executed as powershell -command Write-Host hello
SHELL ["powershell", "-command"]
RUN Write-Host hello

# Executed as cmd /S /C echo hello
SHELL ["cmd", "/S"", "/C"]
RUN echo hello

三、Dockerfile檔案格式

##  Dockerfile檔案格式

# This dockerfile uses the ubuntu image
# VERSION 2 - EDITION 1
# Author: docker_user
# Command format: Instruction [arguments / command] ..
 
# 1、第一行必須指定 基礎映象資訊
FROM ubuntu
 
# 2、維護者資訊
MAINTAINER docker_user docker_user@email.com
 
# 3、映象操作指令
RUN echo "deb http://archive.ubuntu.com/ubuntu/ raring main universe" >> /etc/apt/sources.list
RUN apt-get update && apt-get install -y nginx
RUN echo "\ndaemon off;" >> /etc/nginx/nginx.conf
 
# 4、容器啟動執行指令
CMD /usr/sbin/nginx

Dockerfile 分為四部分:基礎映象資訊、維護者資訊、映象操作指令、容器啟動執行指令。一開始必須要指明所基於的映象名稱,接下來一般會說明維護者資訊;後面則是映象操作指令,例如 RUN 指令。每執行一條RUN 指令,映象新增新的一層,並提交;最後是 CMD 指令,來指明執行容器時的操作命令。

至此本文就全部介紹完了,希望對您有所幫助!

相關文章:https://www.runoob.com/docker/docker-dockerfile.html

相關文章