使用 Dockerfile 的 ENTRYPOINT 和 CMD 指令

如果您曾經需要在啟動時在 Docker 容器中運行一個或兩個命令,這個教程適合您。 使用 DockerfileENTRYPOINTCMD 指令,您可以運行任意多的啟動命令。

在本教程中,您將學習如何使用 ENTRYPOINTCMD 指令在 Dockerfile 中運行啟動命令,並了解它們之間的區別。

先決條件

由於本教程將進行實際演示,請確保您已經準備好以下內容:

  • A Windows 10 PC – Windows 10 v10.0.19042 was used in this tutorial.
  • Docker Desktop – 本教程使用 Docker Desktop v3.3.1。

創建 Dockerfile

在您運行 Docker 容器啟動命令之前,您必須首先創建一個 Dockerfile。 Dockerfile 是一個文本文件,包含一系列命令來構建容器、Docker 鏡像,並確定如何創建 Docker 鏡像。

1. 首先,以管理員身份打開 PowerShell

2. 創建一個新的文件夾來存儲Dockerfile和所有本教程將使用的相關文件,並切換到該目錄。本教程使用~/docker

mkdir ~/docker
cd docker

3. 現在,使用以下命令創建一個名為Dockerfile的空白文本文件。

cd > Dockerfile

或者,如果您使用的是Linux或Mac OS,可以使用以下命令創建一個Dockerfile。

touch Dockerfile

4. 最後,將以下內容添加到Dockerfile中。

FROM ubuntu:20.04

您現在已經創建了一個即將成為Dockerfile的文件!

構建Docker映像

現在您已經創建了Dockerfile,您必須構建一個Docker映像以執行Dockerfile ENTRYPOINT和CMD指令中編寫的命令。一種構建映像的方法是使用build命令

~/docker目錄中,運行以下命令。下面的命令通過指定當前工作目錄(.)在~/docker中的Dockerfile中創建一個名為demo-t demo)的Docker映像。

docker build -t demo .
Building a Docker Image

運行Docker容器

在您構建了Docker映像之後,您將需要一個容器來運行Docker映像,該容器將執行來自Dockerfile ENTRYPOINT和CMD指令的命令。

運行Docker容器,調用run命令以在Docker映像(demo)上創建可寫容器層。以下示例使用-it參數與容器進行互動,以查看樣本輸出。

docker run -it demo
Running a Docker Container

Exec vs. Shell Form

當您開始使用Dockerfile並找出如何運行啟動命令時,可能會遇到兩種不同的定義這些命令的方法。每種方法都會調用命令,但做法略有不同。

當Docker執行命令時,可以直接調用exec或通過容器的shell(在Linux上為/bin/sh -c,在Windows上為cmd /S /C)調用shell

您會注意到通過exec執行的命令具有一條指令,後面是要調用的可執行文件,然後是一個或多個命令行參數,如下所示。

ENTRYPOINT ["executables", "parameter1", "parameter2", ...]
CMD ["executables", "parameter1", "parameter2:, ...]

相反,在shell形式中編寫命令不需要將命令包裝在方括號中,如下所示。

ENTRYPOINT <command> "parameter1"
CMD <command> "parameter1"

如果您未為CMD指定參數,Docker將始終以exec形式執行命令,例如CMD <command>

如果您剛開始,區分這兩種命令調用可能不太重要,但隨著您變得更加高級,您很快就會看到每種方法的優勢和劣勢。

運行啟動命令

讓我們現在深入研究這個教程,並透過幾個範例來了解在 Dockerfile 的 ENTRYPOINT 和 CMD 指令中運行啟動命令的方式。

1. 在你偏好的文本編輯器中打開之前創建的 Dockerfile。

2. 將示例 Dockerfile 內容複製並粘貼到你的 Dockerfile 中,如下所示,然後保存。

這個 Dockerfile 使用 ubuntu:20.04 作為基礎映像來創建一個層。 然後它告訴 Docker 使用 execshell 形式,對 Dockerfile 的 CMDENTRYPOINT 指令分別傳遞 Hello world 參數。

FROM ubuntu:20.04
# CMD 指令
CMD ["echo", "Hello world"] # Exec Form
CMD echo "Hello world"      # Shell Form
# ENTRYPOINT 指令
ENTRYPOINT ["echo", "Hello world"] # Exec Form
ENTRYPOINT echo "Hello world"      # Shell Form

3. 在 ~/docker 目錄中,運行 docker build 命令來構建新的映像,並將其命名為 demo。 以下命令 標記 了映像為 demo,並在當前工作目錄(.)中尋找 Dockerfile。

docker build -t demo .
Building a Docker image with docker build

4. 現在,運行一個容器使用這個映像,然後運行一個基於之前創建的 Docker 映像的 Docker 容器。 現在你會看到容器返回了從 Dockerfile 中提供的 CMD 指令中得到的 Hello world

docker run -it demo
Running a Docker container with docker run

在 Dockerfile 中使用變量

有时您可能事先不知道要传递给命令的确切命令行参数。您需要在运行时暴露给命令的参数。与静态分配命令参数不同,您可以使用变量捕获并传递这些参数给命令。

您只能在shell形式中使用Dockerfile变量。Docker不支持通过exec形式调用的命令中的变量。

再次在您喜欢的文本编辑器中打开Dockerfile,将其中的所有内容替换为以下一系列命令并保存。

这次您会注意到,Dockerfile使用环境变量,并使用ENV显示。在下面的示例中,Dockerfile定义了一个名为name的环境变量,其值为friend。创建后,可以通过$name引用此环境变量。

当Docker基于此Dockerfile运行容器时,它将调用echo命令并传递参数Welcome, friend

FROM ubuntu:20.04
ENV name friend

CMD echo "Welcome, $name"
# 或
## ENTRYPOINT echo "Welcome, $name"

现在,创建Docker镜像并再次运行容器,可选择提供shellform的标签名称。您会注意到Docker调用了echo命令并返回了预期的输出。

Building a Docker Image (shellform) and Running a Docker Container

结合Dockerfile的ENTRYPOINT和CMD指令

多數情況下,您將在CMD或ENTRYPOINT指令中調用啟動命令。畢竟,您可以使用每種方法調用許多命令。但是,您還可以通過這兩種指令“添加到它”來調用單個命令。

建立在前面的示例之上,也許您有一個Dockerfile,看起來像下面的示例一樣。按原樣,如果您創建一個映像並運行該映像的容器,Docker將調用echo命令並返回Hello

FROM ubuntu:20.04
ENTRYPOINT ["echo", "Hello"]

也許您有另一個參數,您想傳遞給echo命令,但不是立即。也許您想在Dockerfile中進一步執行此操作。通過調用CMD指令而不使用命令,您可以這樣做。

當您通過ENTRYPOINT指令指定要運行的命令,然後是CMD指令時,Docker自動假設傳遞給CMD的值是一個參數,而不是一個命令。

現在,添加一個不帶命令的CMD指令引用,只是一個名為world的參數,如下所示。

FROM ubuntu:20.04
ENTRYPOINT ["echo", "Hello"]
CMD ["world"]

由於其通過逗號分開逐個指定值的“數組樣式”行為,應始終以執行形式編寫組合指令,如所示。

構建映像並從映像運行容器後,您會看到Docker只返回一個含義,而不是兩行輸出(Helloworld),這意味著只有一個echo命令調用。

Building a Docker Image (demo3) and Running a Docker Container

結論

你現在應該對通過Dockerfile指令CMDENTRYPOINT運行Docker容器啟動命令有了良好的理解。每個指令都略有不同,但實現了相同的任務,甚至可以一起使用。

你能想到一種情況,你更喜歡使用CMD而不是ENTRYPOINT來運行啟動命令嗎?

Source:
https://adamtheautomator.com/dockerfile-entrypoint/