Docker 中 CMD 和 ENTRYPOINT 的区别

Isaac Tony 2023年1月30日
  1. Docker 中的 CMD 命令
  2. Docker 中的 ENTRYPOINT 命令
  3. 结论
Docker 中 CMD 和 ENTRYPOINT 的区别

在不同环境中管理软件和依赖项时,Docker 容器已成为标准。在使用实际应用程序时,你需要在构建应用程序容器映像之前创建一个 docker 文件。

docker 文件只是一个只读文本文档,其中包含一组在组装镜像时将调用的指令。这些命令包括 RUNCMDENTRYPOINT

在本文中,我们将讨论这些命令的用法。大多数可能处于学习 docker 初始阶段的开发人员倾向于交替使用这些命令,这可能会给你带来一些问题。

Docker 中的 CMD 命令

此命令指定执行 docker RUN 命令时执行的指令。但是,这需要在不指定任何参数的情况下执行 docker RUN 命令。

指定参数时,此命令将被覆盖。另一方面,如果没有指定命令行参数,则执行 CMD 命令。

CMD 命令对于你的 docker 容器正常运行不是必需的,因为 ECHO 命令可以在运行时用于相同目的。但是,当每次容器启动时运行可执行文件时,CMD 命令会很方便。

为了演示如何使用 CMD 命令在运行时运行可执行文件,我们将创建一个简单的 docker 容器,其中包含一个打印出消息的简单 Flask 程序。请注意,这可以用任何语言复制,不一定是 Python。

我们将从创建我们的主应用程序开始,它应该如下所示那样简单。

from flask import Flask

app = Flask(__name__)


def hello():
    print("Hello, this is a simple Flask application")


hello()

在同一个文件夹中,我们将使用命令 touch Dockerfile 创建我们的 Dockerfile。

Dockerfile 只指定了基础镜像、工作目录和应该安装的包。

在最后一行,你应该注意 CMD 命令。在这种情况下,我们使用 CMD 命令在容器启动时执行 app.py 文件。

#  base image
FROM python

# Set your working directory
WORKDIR /var/www/

# Copy the necessary files
COPY ./app.py /var/www/app.py
COPY ./requirements.txt /var/www/requirements.txt

# Install the necessary packages
RUN pip install -r /var/www/requirements.txt

# Run the app
CMD python3 app.py

requirements.txt 应如下所示。

click==8.0.4
Flask==2.0.3
gunicorn==20.1.0
itsdangerous==2.1.0
Jinja2==3.0.3
MarkupSafe==2.1.0
Werkzeug==2.0.3

现在我们已经准备好了一切,我们现在可以继续构建 docker 镜像了。在此之前,我们需要确保我们位于存储程序的同一文件夹中。

在我们的例子中,我们将在构建映像之前将 cd 放入 my-app 文件夹,如下所示。

~/my-app$ docker build -t isaactonyloi_image .

输出:

 => [internal] load build context                                                                                                          0.9s
 => => transferring context: 320B                                                                                                          0.1s
 => [2/5] WORKDIR /var/www/                                                                                                                5.1s
 => [3/5] COPY ./app.py /var/www/app.py                                                                                                    3.2s
 => [4/5] COPY ./requirements.txt /var/www/requirements.txt                                                                                3.2s
 => [5/5] RUN pip install -r /var/www/requirements.txt                                                                                    53.9s
 => exporting to image                                                                                                                     6.9s
 => => exporting layers                                                                                                                    5.8s
 => => writing image sha256:5847e4777754d9d576accd929076bfbee633ca71f049ebe1af6e9bae161f3e96                                               0.1s
 => => naming to docker.io/library/isaactonyloi_image                                                                                      0.2s
isaac@DESKTOP-HV44HT6:~/my-app$

我们已经成功地基于早期的 docker 文件构建了我们的镜像。我们可以在下面验证。

~/my-app$ docker images
REPOSITORY           TAG       IMAGE ID       CREATED         SIZE
isaactonyloi_image   latest    5847e4777754   7 minutes ago   929MB

我们终于可以使用基于此镜像的 docker run 命令创建我们的 docker 容器。另外,请注意,我们将在不传递任何参数来执行 CMD 命令的情况下执行此操作。

~/my-app$ docker run isaactonyloi_image
Hello, this is a simple Flask application

除此之外,CMD 命令还允许我们创建可以在运行时轻松覆盖的参数。

我们在下面的示例中对 CMD 命令进行了更改。其他文件保持不变,我们已经重建了一个新镜像。

# base image
FROM python

# Set your working directory
WORKDIR /var/www/

# Copy the necessary filesls
COPY ./app.py /var/www/app.py
COPY ./requirements.txt /var/www/requirements.txt

# Install the necessary packages
RUN pip install -r /var/www/requirements.txt

# Run the app
CMD ["echo", "Hello, Developer"]

这是我们根据对 Dockerfile 的更改重建的新映像。

~/my-app$ docker images
REPOSITORY           TAG       IMAGE ID       CREATED          SIZE
new_image            latest    73f323be0d2f   25 minutes ago   929MB

在不传递任何参数的情况下再次创建新的 docker 容器时,我们应该在 CMD 命令下收到消息。

isaac@DESKTOP-HV44HT6:~/my-app$ docker run new_image
Hello, Developer

但是,当我们在运行时传递参数时,CMD 命令将自动被覆盖,并且新参数应该优先。因此,它为我们提供了在运行时添加新参数的灵活性,如下所示。

~/my-app$ docker run new_image hostname
da0a832888cb

上面的输出显示 CMD 命令没有被新的主机名参数执行和覆盖。

Docker 中的 ENTRYPOINT 命令

docker ENTRYPOINT 命令与 CMD 命令有相似之处,但并不完全相同。

当使用 CMD 命令时,我们可以通过在运行时传递参数轻松地覆盖它,但 ENTRYPOINT 命令并非如此。

因此,ENTRYPOINT 可用于在运行时不覆盖入口点指令。

如下所示,我们可以通过简单地将 Dockerfile 中的 CMD 命令替换为 ENTRYPOINT 命令来探索此命令的工作原理。我们将根据对 docker 文件的更改构建一个新镜像。

isaac@DESKTOP-HV44HT6:~/my-app$ docker build -t tonyloi_newimage .
[+] Building 42.7s (10/10) FINISHED

输出:

 => [internal] load build definition from Dockerfile                                                                                                                          2.0s
 => => transferring dockerfile: 365B                                                                                                                                          0.7s
 => [internal] load .dockerignore                                                                                                                                             1.6s
 => => transferring context: 2B                                                                                                                                               0.4s
 => [internal] load metadata for docker.io/library/python:latest                                                                                                             35.4s
 => [1/5] FROM docker.io/library/python@sha256:c90e15c86e2ebe71244a2a51bc7f094554422c159ce309a6faadb6debd5a6df0                                                               0.3s
 => [internal] load build context                                                                                                                                             1.2s
 => => transferring context: 63B                                                                                                                                              0.1s
 => CACHED [2/5] WORKDIR /var/www/                                                                                                                                            0.0s
 => CACHED [3/5] COPY ./app.py /var/www/app.py                                                                                                                                0.0s
 => CACHED [4/5] COPY ./requirements.txt /var/www/requirements.txt                                                                                                            0.0s
 => CACHED [5/5] RUN pip install -r /var/www/requirements.txt                                                                                                                 0.0s
 => exporting to image                                                                                                                                                        2.1s
 => => exporting layers                                                                                                                                                       0.0s
 => => writing image sha256:15fb8e4e3ff58ed529b11342bba75b029fd4323beb24aac70ca36b178d04cb34                                                                                  0.2s
 => => naming to docker.io/library/tonyloi_newimage                                                                                                                           0.1s
isaac@DESKTOP-HV44HT6:~/my-app$

此输出证明我们已成功构建了新映像。现在我们可以基于这个镜像创建一个新的 docker 容器。

你可以选择使用镜像名称或镜像 ID 创建容器,这两者都可以使用 docker images 命令访问。这也将显示我们之前创建的镜像。

~/my-app$ docker images
REPOSITORY           TAG       IMAGE ID       CREATED          SIZE
tonyloi_newimage     latest    15fb8e4e3ff5   48 minutes ago   929MB
new_image            latest    73f323be0d2f   48 minutes ago   929MB
isaactonyloi_image   latest    5847e4777754   48 minutes ago   929MB

如果我们在不添加任何参数的情况下构建 docker 容器,我们应该得到如下输出。

isaac@DESKTOP-HV44HT6:~/my-app$ docker run tonyloi_newimage
Hello, Developer

如果我们在基于此镜像创建另一个容器时尝试传递参数,你会注意到,与 CMD 命令的情况不同,这些新参数不会覆盖 ENTRYPOINT 命令。

我们可以在下面验证这一点。

isaac@DESKTOP-HV44HT6:~/my-app$ docker run tonyloi_newimage Welcome to ADC
Hello, Developer Welcome to ADC

结论

我们可以说这两个命令非常相似; 但是,它们的区别在于 CMD 命令可以在运行时被覆盖,而 ENTRYPOINT 命令不能。

此外,某些情况可能需要同时使用这两个命令。

作者: Isaac Tony
Isaac Tony avatar Isaac Tony avatar

Isaac Tony is a professional software developer and technical writer fascinated by Tech and productivity. He helps large technical organizations communicate their message clearly through writing.

LinkedIn

相关文章 - Docker Command