高级 Dockerfile 指令
在本文中,我们将讨论更高级的Dockerfile指令。这些指令可用于创建更高级的 Docker 镜像。
例如,我们可以使用VOLUME指令将主机的文件系统绑定到 Docker 容器。这将允许我们将 Docker 容器生成和使用的数据保存到本地机器。
我们将在本文中介绍以下指令:
- ENV指令
- ARG指令
- WORKDIR指令
- COPY指令
- ADD指令
- USER指令
- VOLUME指令
- EXPOSE指令
- HEALTHCHECK指令
- ONBUILD指令
ENV 指令
Dockerfile中的ENV指令可用于设置环境变量。环境变量用于设置环境变量。环境变量是键值对,用于向容器内运行的应用程序和进程提供信息。它们可以通过在运行时提供动态值来影响程序和脚本的行为。
环境变量按照以下格式定义为键值对:
ENV <key> <value>
例如,我们可以使用ENV指令设置路径,如下所示:
ENV PATH $PATH:/usr/local/app/bin/
我们可以在同一行中设置多个环境变量,并用空格分隔。但是,在这种格式中,键和值应该用等号 ( ) 分隔=
:
ENV <key>=<value> <key=value> ...
下面我们设置配置两个环境变量,PATH环境变量的配置值为$PATH:/usr/local/app/bin
,VERSION环境变量的配置值为1.0.0
:
ENV PATH=$PATH:/usr/local/app/bin/ VERSION=1.0.0
一旦使用Dockerfile中的ENV指令设置了环境变量,该变量就会在所有后续的 Docker 镜像层中生效。甚至从此 Docker 镜像启动的 Docker 容器中也同样有效。
ARG指令
Dockerfile 中的ARG指令用于定义用户在构建时可以通过命令传递给构建器的变量。这些变量的行为类似于环境变量,可以在整个 Dockerfile 中使用,但除非使用ENVdocker build
指令明确声明,否则它们不会持久保存在最终镜像中。
ARG指令具有以下格式:
ARG <varname>
我们还可以添加多个ARG指令,如下所示:
ARG USER
ARG VERSION
这些参数也可以在 Dockerfile 中指定可选的默认值。如果用户在构建过程中未提供任何值,Docker 将使用ARG指令中定义的默认值:
ARG USER=TestUser
ARG VERSION=1.0.0
与ENV变量不同,ARG变量无法从正在运行的容器中访问。它们仅在构建过程中可用。
在 Dockerfile 中使用 ENV 和 ARG 指令
我们将创建一个Dockerfile,使用 ubuntu 作为父镜像,但我们可以在构建时更改 ubuntu 版本。我们还将指定环境名称和应用程序目录作为 Docker 镜像的环境变量。
env-arg-example
使用以下命令创建一个名为的新目录mkdir
:
mkdir env-arg-example
env-arg-example
使用以下命令导航新创建的目录cd
:
cd env-arg-example
现在,让我们创建一个新的 Dockerfile。我将使用 VS Code,但您也可以使用任何您喜欢的编辑器:
code Dockerfile
在Dockerfile中添加以下内容,保存退出:
ARG TAG=latest
FROM ubuntu:$TAG
LABEL maintainer=ananalogguyinadigitalworld@example.com
ENV ENVIRONMENT=dev APP_DIR=/usr/local/app/bin
CMD ["env"]
Dockerfile首先定义一个默认值为 的参数TAG 。然后,它使用此参数在FROM指令中指定基础镜像,最终选择带有 标记的 Ubuntu镜像。latest
latest
LABEL指令向镜像添加元数据,指示维护者的电子邮件地址。接下来,ENV指令设置两个环境变量:,其值为,并指向。容器内运行的应用程序可以使用这些变量根据环境和目录路径调整行为。ENVIRONMENT
dev
APP_DIR
/usr/local/app/bin
最后,CMD指令指定从该映像启动容器时要运行的命令,在本例中,它执行env
以显示容器内设置的所有环境变量。
现在让我们构建 Docker 镜像:
docker image build -t env-arg --build-arg TAG=23.10 .
输出应类似于以下内容:
[+] Building 34.9s (6/6) FINISHED docker:default
=> [internal] load .dockerignore 0.0s
=> => transferring context: 2B 0.0s
=> [internal] load build definition from Dockerfile 0.0s
=> => transferring dockerfile: 189B 0.0s
=> [internal] load metadata for docker.io/library/ubuntu:23.10 3.3s
=> [auth] library/ubuntu:pull token for registry-1.docker.io 0.0s
=> [1/1] FROM docker.io/library/ubuntu:23.10@sha256:fd7fe639db24c4e005643921beea92bc449aac4f4d40d60cd9ad9ab6456aec01 31.6s
=> => resolve docker.io/library/ubuntu:23.10@sha256:fd7fe639db24c4e005643921beea92bc449aac4f4d40d60cd9ad9ab6456aec01 0.0s
=> => sha256:fd7fe639db24c4e005643921beea92bc449aac4f4d40d60cd9ad9ab6456aec01 1.13kB / 1.13kB 0.0s
=> => sha256:c57e8a329cd805f341ed7ee7fcc010761b29b9b8771b02a4f74fc794f1d7eac5 424B / 424B 0.0s
=> => sha256:77081d4f1e7217ffd2b55df73979d33fd493ad941b3c1f67f1e2364b9ee7672f 2.30kB / 2.30kB 0.0s
=> => sha256:cd0bff360addc3363f9442a3e0b72ff44a74ccc0120d0fc49dfe793035242642 27.23MB / 27.23MB 30.3s
=> => extracting sha256:cd0bff360addc3363f9442a3e0b72ff44a74ccc0120d0fc49dfe793035242642 1.1s
=> exporting to image 0.0s
=> => exporting layers 0.0s
=> => writing image sha256:86b2f4c440c71f37c3f29f5dd5fe79beac30f5a6ce878bd14dc17f439bd2377d 0.0s
=> => naming to docker.io/library/env-arg 0.0s
现在,执行docker container run
命令从我们在上一步构建的 Docker 镜像启动一个新容器:
docker container run env-arg
输出应该类似于以下内容:
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
HOSTNAME=d6020a144f39
ENVIRONMENT=dev
APP_DIR=/usr/local/app/bin
HOME=/root
WORKDIR 指令
Dockerfile 中的WORKDIR指令用于设置 Dockerfile 中后续指令的当前工作目录。该指令用于定义ADD、CMD、COPY、ENTRYPOINT和RUN等命令在容器内的执行位置。
WORKDIR指令具有以下格式:
WORKDIR /path/to/workdir
如果镜像中不存在指定的目录,Docker 会在构建过程中创建该目录。此外,WORKDIRmkdir
指令有效地结合了类 Unix 系统中和命令的功能cd
。如果目录不存在,它会创建该目录,并将当前目录更改为指定的路径。
一个 Dockerfile 中可以包含多个WORKDIR指令。如果后续WORKDIR指令使用相对路径,则它们将相对于最后一个WORKDIR指令设置。
例如:
WORKDIR /one
WORKDIR two
WORKDIR three
WORKDIR drink
WORKDIR /one
将设置/one
为初始工作目录。WORKINGDIR two
然后将目录更改为/one/two
。WORKDIR three
进一步将其更改为one/two/three
。最后WORKDIR drink
将其更改为其最终形式one/two/three/drink
。
COPY 指令
构建 Docker 镜像时,通常会将本地开发环境中的文件复制到镜像本身中。这些文件包括应用程序源代码、配置文件以及应用程序在容器内正常运行所需的其他资源。Dockerfile中的COPY指令可以实现此目的,它允许我们指定本地文件系统中的哪些文件或目录应复制到正在构建的镜像中。
COPY命令的语法如下:
COPY <source> <destination>
指定<source>
本地文件系统上相对于构建上下文的文件或目录的路径。<destination>
指定文件或目录在 Docker 镜像文件系统中应复制到的路径。
在以下示例中,我们使用COPY指令将index.html
文件从本地文件系统复制到/var/www/html/
Docker 镜像的目录:
COPY index.html /var/www/html/index.html
我们还可以使用通配符来复制所有与给定模式匹配的文件。下面,我们将所有带有.html
扩展名的文件从当前目录复制到/var/www/html/
Docker 镜像的目录:
COPY *.html /var/www/html/
当使用Dockerfile 中的COPY--chown
指令将文件从本地文件系统传输到 Docker 镜像时,我们还可以指定该标志。此标志允许我们在 Docker 镜像中设置复制文件的用户和组所有权。
COPY --chown=myuser:mygroup *.html /var/www/html/
在此示例中,--chown=myuser:mygroup
指定.html
从本地目录复制到/var/www/html/
Docker 镜像中的所有文件都应归myuser
(用户)和mygroup
(组)所有。
ADD 指令
Dockerfiles 中的ADD指令的功能与COPY指令类似,但具有附加功能。
ADD <source> <destination>
指定本地文件系统或远程 URL 上文件或目录的<source>
路径或URL<destination>
。再次指定文件或目录在 Docker 镜像文件系统中应复制到的路径。
在下面的示例中,我们将使用ADD从本地文件系统复制文件:
ADD index.html /var/www/html/index.html
在这个例子中,Docker 将把index.html
文件从本地文件系统(相对于 Docker 构建上下文)复制到/var/www/html/index.html
Docker 镜像中。
在下面的示例中,我们将使用ADD从远程 URL 复制文件:
ADD http://example.com/test-data.csv /tmp/test-data.csv
与COPY不同,ADD指令允许指定 URL(在本例中http://example.com/test-data.csv
为 )作为<source>
参数。Docker 将从该 URL 下载文件并将其复制到/tmp/test-data.csv
Docker 镜像中。
ADD指令不仅可以从本地文件系统复制文件或从 URL 下载文件,还包含从某些类型的压缩档案中自动提取文件的功能。当文件<source>
是压缩档案文件(例如,.tar
、.tar.gz
、.tgz
、.bz2
、.tbz2
、)时,Docker 会自动将其内容提取到Docker 镜像文件系统中。.txz
.zip
<destination>
例如:
ADD myapp.tar.gz /opt/myapp/
在上面的例子中,myapp.tar.gz
是一个压缩的存档文件,Docker 会自动将其内容提取myapp.tar.gz
到/opt/myapp/
Docker 镜像中。
最佳实践:Dockerfile 中的 COPY 与 ADD
编写 Dockerfile 时,在COPY和ADD指令之间进行选择对于保持映像构建过程的清晰度、安全性和可靠性至关重要。
清晰度和意图
COPY 指令简单易懂,明确指出将本地文件系统中的文件或目录复制到 Docker 镜像中。这种清晰的指令有助于理解 Dockerfile 的用途,并使其更易于长期维护。
另一方面,ADD引入了一些额外的功能,例如从 URL 下载文件以及自动提取压缩包。虽然这些功能在某些情况下很方便,但它们也可能掩盖了复制文件的初衷。如果不加以谨慎管理,这种缺乏透明度的行为可能会导致意外行为或安全风险。
安全性和可预测性
使用COPY可以避免从任意 URL 下载文件的潜在风险,从而增强安全性。Docker 镜像应使用受控且经过验证的来源构建,以防止包含意外或恶意内容。将文件下载与构建过程分离,并COPY
确保 Docker 构建环境保持安全且可预测。
Docker 哲学一致性
Docker鼓励构建轻量、高效且可预测的容器化应用程序。COPY完美契合了这一理念,它提升了操作的简易性,并降低了镜像构建过程中出现意外副作用的风险。
在 Dockerfile 中使用 WORKDIR、COPY 和 ADD 指令
在此示例中,我们将把一个自定义 HTML 文件部署到 Apache Web 服务器。我们将使用 Ubuntu 作为基础镜像,并在其上安装 Apache。然后,我们将把这个自定义index.html
文件复制到 Docker 镜像中,并下载 Docker 徽标。
workdir-copy-add-example
使用以下命令创建一个名为的新目录mkdir
:
mkdir workdir-copy-add-example
导航到新创建的workdir-copy-add-example
目录:
cd .\workdir-copy-add-example\
在该workdir-copy-add-example
目录中,创建一个名为 的文件index.html
。该文件将在构建期间复制到 Docker 镜像中。我将使用 VS Code,但您也可以使用任何您更习惯的编辑器:
code index.html
将以下内容添加到 index.html 文件,保存并关闭编辑器:
<html>
<body>
<h1>
Welcome to Docker!
</h1>
<img src="logo.png" height="350" width="500"/>
</body>
</html>
这段 HTML 代码创建了一个基本的网页,用一个大标题“欢迎使用 Docker!”来欢迎访客。标题下方包含一张使用<img>
带有 source 属性 ( src="logo.png"
) 的标签显示的图片,这表示它应该获取并显示一个名为 的图片文件logo.png
。该图片的大小为高 350 像素,宽 500 像素(height="350"
和width="500"
)。
现在,在此目录中创建一个Dockerfile :
code Dockerfile
在文件中添加以下内容Dockerfile
,保存并退出:
FROM ubuntu:latest
RUN apt-get update && apt-get upgrade
RUN apt-get install apache2 -y
WORKDIR /var/www/html/
COPY index.html .
ADD https://upload.wikimedia.org/wikipedia/commons/4/4e/Docker_%28container_engine%29_logo.svg ./logo.png
CMD ["ls"]
此 Dockerfile 首先指定FROM ubuntu:latest
,表示它将基于最新的 Ubuntu 基础镜像进行构建。后续RUN apt-get update && apt-get upgrade
命令将更新和升级容器内的软件包列表。之后,apt-get install apache2 -y
使用软件包管理器安装 Apache Web 服务器。该WORKDIR /var/www/html/
指令将工作目录设置为/var/www/html/
,这是 Apache 中提供 Web 内容服务的常用位置。
在此目录中,COPY index.html .
将本地index.html
文件从主机复制到容器中。此外,ADD https://upload.wikimedia.org/wikipedia/commons/4/4e/Docker_%28container_engine%29_logo.svg ./logo.png
从 URL 检索 SVG 图像文件并将其保存在本地logo.png
同一目录中。
最后,CMD ["ls"]
指定在容器启动时ls
执行该命令,显示其中的文件和目录列表/var/www/html/
。
现在,使用以下标签构建 Docker 镜像workdir-copy-add
:
docker build -t workdir-copy-add .
您应该看到以下输出:
[+] Building 4.0s (13/13) FINISHED docker:default
=> [internal] load build definition from Dockerfile 0.0s
=> => transferring dockerfile: 290B 0.0s
=> [internal] load .dockerignore 0.0s
=> => transferring context: 2B 0.0s
=> [internal] load metadata for docker.io/library/ubuntu:latest 3.6s
=> [auth] library/ubuntu:pull token for registry-1.docker.io 0.0s
=> [1/6] FROM docker.io/library/ubuntu:latest@sha256:2e863c44b718727c860746568e1d54afd13b2fa71b160f5cd9058fc4362 0.0s
=> => resolve docker.io/library/ubuntu:latest@sha256:2e863c44b718727c860746568e1d54afd13b2fa71b160f5cd9058fc4362 0.0s
=> [internal] load build context 0.0s
=> => transferring context: 32B 0.0s
=> https://upload.wikimedia.org/wikipedia/commons/4/4e/Docker_%28container_engine%29_logo.svg 0.3s
=> CACHED [2/6] RUN apt-get update && apt-get upgrade 0.0s
=> CACHED [3/6] RUN apt-get install apache2 -y 0.0s
=> CACHED [4/6] WORKDIR /var/www/html/ 0.0s
=> CACHED [5/6] COPY index.html . 0.0s
=> CACHED [6/6] ADD https://upload.wikimedia.org/wikipedia/commons/4/4e/Docker_%28container_engine%29_logo.svg . 0.0s
=> exporting to image 0.0s
=> => exporting layers 0.0s
=> => writing image sha256:646864d79dc576f862a980ef6ddab550ef9801790d2c91967c3c9596cf85b81a 0.0s
=> => naming to docker.io/library/workdir-copy-add 0.0s
执行docker container run
命令从我们之前构建的Docker镜像启动一个新容器:
docker run workdir-copy-add
我们可以看到,和都index.html
在logo.png
目录中/var/www/html/
:
index.html
logo.png
USER 指令
在 Docker 中,默认情况下,容器以 root 用户身份运行,该用户在容器环境中拥有广泛的权限。为了降低安全风险,Docker 允许我们使用Dockerfile中的USER指令指定非 root 用户。该指令设置容器的默认用户,Dockerfile 中指定的所有后续命令(例如RUN、CMD和ENTRYPOINT)都将在该用户的上下文中执行。
实施USER指令被认为是 Docker 安全性的最佳实践,符合最小特权原则。它确保容器以其功能所需的最小权限运行,从而增强整体系统安全性并减少攻击面。
USER指令采用以下格式:
USER <user>
除了用户名之外,我们还可以指定可选的组名来运行Docker容器:
USER <user>:<group>
您需要确保<user>
和<group>
值是有效的用户名和组名。否则,Docker 守护进程在尝试运行容器时会抛出错误。
在 Dockerfile 中使用 USER 指令
在此示例中,我们将使用Dockerfile中的USER指令设置默认用户。我们将安装 Apache Web 服务器并将用户更改为www-data。最后,我们将执行命令,通过打印用户名来验证当前用户。whoami
创建一个名为的新目录user-example
mkdir user-example
导航到新创建的user-example directory
cd .\user-example\
在user-example
目录中创建一个新的Dockerfile
code Dockerfile
将以下内容添加到您的Dockerfile,保存并关闭编辑器:
FROM ubuntu:latest
RUN apt-get update && apt-get upgrade
RUN apt-get install apache2 -y
USER www-data
CMD ["whoami"]
此 Dockerfile 从最新的 Ubuntu 基础镜像启动,并在安装 Apache Web 服务器 ( apache2
) 之前更新系统软件包。它通过切换到www-data
Web 服务器常用的用户来增强安全性,从而最大限度地减少潜在漏洞。该CMD ["whoami"]
指令确保容器启动时显示当前用户 ( www-data
),从而演示了适合在 Docker 环境中托管 Web 应用程序的安全设置。
构建 Docker 镜像:
docker build -t user .
您应该看到以下输出:
[+] Building 5.0s (8/8) FINISHED docker:default
=> [internal] load build definition from Dockerfile 0.0s
=> => transferring dockerfile: 157B 0.0s
=> [internal] load .dockerignore 0.1s
=> => transferring context: 2B 0.0s
=> [internal] load metadata for docker.io/library/ubuntu:latest 4.8s
=> [auth] library/ubuntu:pull token for registry-1.docker.io 0.0s
=> [1/3] FROM docker.io/library/ubuntu:latest@sha256:2e863c44b718727c860746568e1d54afd13b2fa71b160f5cd9058fc436217b30 0.0s
=> => resolve docker.io/library/ubuntu:latest@sha256:2e863c44b718727c860746568e1d54afd13b2fa71b160f5cd9058fc436217b30 0.0s
=> CACHED [2/3] RUN apt-get update && apt-get upgrade 0.0s
=> CACHED [3/3] RUN apt-get install apache2 -y 0.0s
=> exporting to image 0.0s
=> => exporting layers 0.0s
=> => writing image sha256:ce1de597471f741f4fcae898215cfcb0d847aacf7c201690c5d4e95289476768 0.0s
=> => naming to docker.io/library/user 0.0s
现在,执行docker container run
命令从我们在上一步中构建的 Docker 镜像启动一个新容器:
docker container run user
并且输出应显示www-data
与 Docker 容器关联的当前用户:
www-data
VOLUME指令
在 Docker 中,容器旨在以可移植且轻量级的方式封装应用程序及其依赖项。然而,默认情况下,在 Docker 容器文件系统中生成或修改的任何数据都是短暂的,这意味着它们仅在容器运行时存在。当容器被删除或替换时,这些数据就会丢失,这给需要持久存储的应用程序(例如数据库或文件存储系统)带来了挑战。
为了应对这一挑战,Docker 引入了卷的概念。卷提供了一种独立于容器生命周期来持久化数据的方法。它们充当 Docker 容器和主机之间的桥梁,确保即使容器被停止、移除或替换,卷中存储的数据也能持久保存。这使得卷对于需要跨容器实例维护状态信息(例如存储数据库、配置文件或应用程序日志)的应用程序至关重要。
当你使用该指令在 Dockerfile 中定义卷时VOLUME
,Docker 会在容器的文件系统中创建一个托管目录。该目录用作卷的挂载点。至关重要的是,Docker 还会在主机上创建一个相应的目录,用于存储卷的实际数据。这种映射确保在容器内部对卷内文件所做的任何更改都会立即同步到主机上的映射目录,反之亦然。
Docker 中的卷支持多种类型,包括命名卷和主机挂载卷。命名卷由 Docker 创建和管理,从而在卷生命周期和存储管理方面提供更强大的控制力和灵活性。另一方面,主机挂载卷允许您将目录从主机文件系统直接挂载到容器中,从而提供对主机资源的直接访问。
VOLUME指令通常以 JSON 数组作为参数:
VOLUME ["path/to/volume"]
或者,我们可以指定一个包含多个路径的纯字符串:
VOLUME /path/to/volume1 /path/to/volume2
我们可以使用该docker container inspect <container>
命令查看容器中可用的卷。docker container inspect 命令的输出 JSON 将打印类似以下内容的卷信息:
[
{
"CreatedAt":"2024-06-21T22:52:52+03:00",
"Driver":"local",
"Labels":null,
"Mountpoint":"/var/lib/docker/volumes/f46f82ea6310d0db3a13897a0c3ab45e659ff3255eaeead680b48bca37cc0166/_data",
"Name":"f46f82ea6310d0db3a13897a0c3ab45e659ff3255eaeead680b48bca37cc0166",
"Options":null,
"Scope":"local"
}
]
在 Dockerfile 中使用 VOLUME 指令
在这个例子中,我们将设置一个 Docker 容器来运行 Apache Web 服务器。但是,我们不希望 Docker 容器发生故障时丢失 Apache 日志文件。为此,我们将通过将 Apache 日志路径挂载到底层 Docker 主机来持久化日志文件。
创建一个名为的新目录volume-example
mkdir volume-example
导航到新创建的volume-example
目录
cd volume-example
在volume-example
目录中创建一个新的Dockerfile
code Dockerfile
将以下内容添加到Dockerfile,保存并退出
FROM ubuntu:latest
RUN apt-get update && apt-get upgrade
RUN apt-get install apache2 -y
VOLUME ["/var/log/apache2"]
apt-get update
此 Dockerfile 首先使用最新版本的 Ubuntu 作为基础镜像,并通过运行和apt-get upgrade
更新所有已安装的软件包来确保其处于最新状态。然后,它apache2
使用 来安装 Apache HTTP Server ( ) apt-get install apache2 -y
。该VOLUME ["/var/log/apache2"]
指令在 处定义了一个 Docker 卷/var/log/apache2
,Apache 通常将其日志文件存储在其中。
现在,让我们构建 Docker 镜像:
docker build -t volume .
输出应该如下:
[+] Building 3.6s (8/8) FINISHED docker:default
=> [internal] load .dockerignore 0.0s
=> => transferring context: 2B 0.0s
=> [internal] load build definition from Dockerfile 0.0s
=> => transferring dockerfile: 155B 0.0s
=> [internal] load metadata for docker.io/library/ubuntu:latest 3.5s
=> [auth] library/ubuntu:pull token for registry-1.docker.io 0.0s
=> [1/3] FROM docker.io/library/ubuntu:latest@sha256:2e863c44b718727c860746568e1d54afd13b2fa71b160f5cd9058fc436217b30 0.0s
=> => resolve docker.io/library/ubuntu:latest@sha256:2e863c44b718727c860746568e1d54afd13b2fa71b160f5cd9058fc436217b30 0.0s
=> CACHED [2/3] RUN apt-get update && apt-get upgrade 0.0s
=> CACHED [3/3] RUN apt-get install apache2 -y 0.0s
=> exporting to image 0.0s
=> => exporting layers 0.0s
=> => writing image sha256:9c7a81e379553444e0b4f3bbf45bdd17880aea251db8f8b75669e13964b9c30f 0.0s
=> => naming to docker.io/library/volume
执行该docker container run
命令,从先前构建的镜像启动一个新容器。请注意,您还需要使用--interactive
和--tty
标志来打开交互式 Bash 会话,以便从容器的 Bash Shell 执行命令。此外,您还需要使用--name
标志将容器名称定义为volume-container
docker container run --interactive --tty --name volume-container volume /bin/bash
您的 bash shell 将按如下方式打开:
root@8aa0f5fb8a6d:/#
导航到/var/log/apache2
目录
root@8aa0f5fb8a6d:/# cd /var/log/apache2/
这将产生以下输出:
root@8aa0f5fb8a6d:/var/log/apache2#
现在,列出目录中可用的文件
root@8aa0f5fb8a6d:/var/log/apache2# ls -l
输出应如下所示
total 0
-rw-r----- 1 root adm 0 Jun 20 13:42 access.log
-rw-r----- 1 root adm 0 Jun 20 13:42 error.log
-rw-r----- 1 root adm 0 Jun 20 13:42 other_vhosts_access.log
这些是 Apache 在运行进程时创建的日志文件。检查此卷的主机挂载后,应该可以看到相同的文件。
退出容器检查主机文件系统
root@8aa0f5fb8a6d:/var/log/apache2# exit
检查volume-container
以查看挂载信息
docker container inspect volume-container
在密钥下Mounts
,您将能够看到与安装相关的信息
"Mounts":[
{
"Type":"volume",
"Name":"50d3a5abf34535fbd3a347cbd6c74acf87a7aa533494360e661c73bbdf34b3e8",
"Source":"/var/lib/docker/volumes/50d3a5abf34535fbd3a347cbd6c74acf87a7aa533494360e661c73bbdf34b3e8/_data",
"Destination":"/var/log/apache2",
"Driver":"local",
"Mode":"",
"RW":true,
"Propagation":""
}
]
使用以下命令检查卷。您可以在上一个输出的字段中docker volume inspect <volume_name>
找到<volume_name>
Name
docker volume inspect 50d3a5abf34535fbd3a347cbd6c74acf87a7aa533494360e661c73bbdf34b3e8
您应该得到类似以下内容的输出
[{
"CreatedAt":"2024-06-21T11:02:32Z",
"Driver":"local",
"Labels":{
"com.docker.volume.anonymous":""
},
"Mountpoint":"/var/lib/docker/volumes/50d3a5abf34535fbd3a347cbd6c74acf87a7aa533494360e661c73bbdf34b3e8/_data",
"Name":"50d3a5abf34535fbd3a347cbd6c74acf87a7aa533494360e661c73bbdf34b3e8",
"Options":null,
"Scope":"local"
}]
列出主机文件路径中可用的文件。主机文件路径可以通过Mountpoint
上一个输出的字段来识别
ls -l /var/lib/docker/volumes/50d3a5abf34535fbd3a347cbd6c74acf87a7aa533494360e661c73bbdf34b3e8/_data
EXPOSE 指令
Docker 中的EXPOSE指令用于指示容器在运行时将监听特定端口。此声明主要起指示作用,并不会实际将端口发布到主机系统,也不会默认允许容器外部访问这些端口。相反,它记录了哪些端口计划用于 Docker 环境中的容器间通信或网络服务。
EXPOSE指令支持 TCP 和 UDP 协议,允许灵活地根据各种网络需求公开端口。此指令是-p
或选项的前身,-P
在容器运行时使用,用于将这些公开的端口实际映射到主机上的端口,以便在需要时启用外部访问。
EXPOSE指令具有以下格式:
EXPOSE <port>
HEALTHCHECK指令
健康检查是评估容器运行状况的关键机制。它提供了一种方法来验证 Docker 容器内运行的应用程序是否正常运行。如果没有特定的健康检查,Docker 就无法自主确定容器的健康状态。这在可靠性和正常运行时间至关重要的生产环境中尤为重要。
Docker 中的HEALTHCHECK指令允许开发人员定义自定义健康检查(通常以命令或脚本的形式),用于定期检查容器的状态并报告其健康状况。该指令可确保主动监控,并帮助 Docker 编排工具根据健康状况做出明智的容器生命周期管理决策。
一个Dockerfile中只能有一个HEALTHCHECK指令。如果有多个HEALTHCHECK指令,则只有最后一个指令生效。
例如,我们可以使用以下指令来确保容器可以在http://localhost/
端点上接收流量:
HEALTHCHECK CMD curl -f http://localhost/ || exit 1
上述命令末尾的退出代码用于指定容器的健康状态,0
并且1
是此字段的有效值。0
表示健康容器,1
表示不健康容器。
在 Docker 中使用HEALTHCHECK指令时,可以配置基本命令之外的其他参数来定制执行健康检查的方式:
--interval
:指定执行健康检查的频率,默认间隔为30秒。--timeout
:定义允许健康检查命令成功完成的最长时间。如果在此期限内未收到成功响应,则健康检查将被标记为失败。默认超时时间也设置为 30 秒。--start-period
:指定 Docker 开始执行首次健康检查之前的初始延迟时间。此参数允许容器在健康检查开始前进行一些初始化,默认启动时间为 0 秒。--retries
:定义 Docker 将容器视为不健康之前允许的连续健康检查失败次数。默认情况下,Docker 最多允许重试 3 次。
在以下示例中,通过提供自定义值来覆盖HEALTHCHECK的默认值:
HEALTHCHECK \
--interval=1m \
--timeout=2s \
--start-period=2m \
--retries=3 \
CMD curl -f http://localhost/ || exit 1
在 Dockerfile 中使用 EXPOSE 和 HEALTHCHECK 指令
我们将对 Apache Web 服务器进行 Docker 化,以便从 Web 浏览器访问 Apache 主页。此外,我们还将配置健康检查,以确定 Apache Web 服务器的健康状况。
创建一个名为的新目录expose-heathcheck-example
mkdir expose-healthcheck-example
导航到新创建的expose-healthcheck-example
目录
cd .\expose-healthcheck-example\
创建Dockerfile并添加以下内容
FROM ubuntu:latest
RUN apt-get update && apt-get upgrade
RUN apt-get install apache2 curl -y
HEALTHCHECK CMD curl -f http://localhost/ || exit 1
EXPOSE 80
ENTRYPOINT ["apache2ctl", "-D", "FOREGROUND"]
此 Dockerfile 首先拉取最新的 Ubuntu 基础镜像并进行更新。然后,它使用 安装 Apache Web 服务器和 curl apt-get
。HEALTHCHECK
指令设置为运行健康检查命令 ( curl -f http://localhost/ || exit 1
),根据本地主机连接情况确保容器的健康状态。端口 80 已公开,以允许外部访问 Apache。最后,使用 将容器配置为以前台模式运行 Apache ENTRYPOINT ["apache2ctl", "-D", "FOREGROUND"]
,确保其作为主进程保持活跃和响应。此设置允许在 Docker 环境中托管可通过端口 80 访问的 Web 服务器。
构建映像
docker image build -t expose-healthcheck-example .
您应该获得类似以下内容的输出:
[+] Building 29.0s (8/8) FINISHED docker:default
=> [internal] load build definition from Dockerfile 0.0s
=> => transferring dockerfile: 244B 0.0s
=> [internal] load .dockerignore 0.0s
=> => transferring context: 2B 0.0s
=> [internal] load metadata for docker.io/library/ubuntu:latest 3.4s
=> [auth] library/ubuntu:pull token for registry-1.docker.io 0.0s
=> [1/3] FROM docker.io/library/ubuntu:latest@sha256:2e863c44b718727c860746568e1d54afd13b2fa71b160f5cd9058fc436217b30 0.0s
=> => resolve docker.io/library/ubuntu:latest@sha256:2e863c44b718727c860746568e1d54afd13b2fa71b160f5cd9058fc436217b30 0.0s
=> CACHED [2/3] RUN apt-get update && apt-get upgrade 0.0s
=> [3/3] RUN apt-get install apache2 curl -y 24.8s
=> exporting to image 0.6s
=> => exporting layers 0.6s
=> => writing image sha256:3323e865b3888a4e45852c6a8c163cb820739735716f8783a0d126b43d810f1e 0.0s
=> => naming to docker.io/library/expose-healthcheck-example 0.0s
执行docker container run
命令启动一个新容器。您将使用-p
标志将80
主机端口重定向到8080
容器端口。此外,您将使用--name
标志将容器名称指定为expose-healthcheck-container
,并使用-d
标志以分离模式运行容器。
docker container run -p 8080:80 --name expose-healthcheck-container -d expose-healthcheck-example
docker container list
使用命令列出正在运行的容器
docker container list
在输出中,你会看到是STATUS
健康expose-healthcheck-container
的
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
3ff16b11275c expose-healthcheck-example "apache2ctl -D FOREG…" About a minute ago Up About a minute (healthy) 80/tcp, 0.0.0.0:8080->8080/tcp expose-healthcheck-container
现在,您应该能够查看 Apache 主页了。http://127.0.0.1:8080
从浏览器导航到终端节点
ONBUILD 指令
Dockerfile 中的 ONBUILD 指令有助于创建可复用的基础镜像,用于后续镜像构建。它允许开发人员定义仅当其他 Docker 镜像使用当前镜像作为基础镜像时才会触发的指令。例如,您可以构建一个包含运行应用程序所需的所有必要先决条件和配置的 Docker 镜像。
通过在此“先决条件”镜像中应用 ONBUILD 指令,可以将特定指令推迟到该镜像被另一个 Dockerfile 用作父镜像时执行。这些推迟的指令不会在当前 Dockerfile 的构建过程中执行,而是在构建子镜像时继承并执行。这种方法简化了环境设置流程,并确保在从基础镜像派生的多个项目或应用程序中一致地应用通用的依赖项和配置。
ONBUILD指令采用以下格式
ONBUILD <instruction>
例如,假设我们在自定义基础镜像的Dockerfile中有以下ONBUILD指令
ONBUILD ENTRYPOINT ["echo", "Running an ONBUILD Directive"]
如果我们从自定义基础镜像创建 Docker 容器,则不会打印该Running an ONBUILD Directive
值,但如果我们将其用作另一个 Docker 镜像的基础,则会打印该值。
在 Dockerfile 中使用 ONBUILD 指令
在这个例子中,我们将使用 Apache Web 服务器构建一个父镜像,并使用ONBUILD指令复制 HTML 文件。
创建一个名为的新目录onbuild-parent-example
mkdir onbuild-parent-example
导航到新创建的onbuild-parent-example
目录:
cd .\onbuild-parent-example\
新建Dockerfile并添加以下内容
FROM ubuntu:latest
RUN apt-get update && apt-get upgrade
RUN apt-get install apache2 -y
ONBUILD COPY *.html /var/www/html
EXPOSE 80
ENTRYPOINT ["apache2ctl", "-D", "FOREGROUND"]
此 Dockerfile 首先使用最新的 Ubuntu 基础镜像。它会更新并升级系统软件包,然后安装 Apache Web 服务器。ONBUILD 指令指定使用此 Dockerfile 构建的任何子镜像都会自动将所有 HTML 文件从构建上下文复制到/var/www/html
容器内的目录。80 端口已公开,以允许传入 Apache 服务器的流量。最后,该ENTRYPOINT
命令将容器配置为以前台模式运行 Apache,确保它作为主进程保持活动状态并响应迅速。此设置使容器能够通过 80 端口上的 Apache 提供 Web 内容。
现在,构建 Docker 镜像:
docker image build -t onbuild-parent-example .
输出应如下所示:
[+] Building 3.5s (8/8) FINISHED docker:default
=> [internal] load build definition from Dockerfile 0.0s
=> => transferring dockerfile: 221B 0.0s
=> [internal] load .dockerignore 0.1s
=> => transferring context: 2B 0.0s
=> [internal] load metadata for docker.io/library/ubuntu:latest 3.3s
=> [auth] library/ubuntu:pull token for registry-1.docker.io 0.0s
=> [1/3] FROM docker.io/library/ubuntu:latest@sha256:2e863c44b718727c860746568e1d54afd13b2fa71b160f5cd9058fc4362 0.0s
=> => resolve docker.io/library/ubuntu:latest@sha256:2e863c44b718727c860746568e1d54afd13b2fa71b160f5cd9058fc4362 0.0s
=> CACHED [2/3] RUN apt-get update && apt-get upgrade 0.0s
=> CACHED [3/3] RUN apt-get install apache2 -y 0.0s
=> exporting to image 0.0s
=> => exporting layers 0.0s
=> => writing image sha256:4a6360882fb65415cdd7326392de35c2336a8599c2c4b8b7a1e4d962d81df7e4 0.0s
=> => naming to docker.io/library/onbuild-parent-example 0.0s
执行docker container run
命令从上一步构建的Docker镜像启动一个新的容器:
docker container run -p 8080:80 --name onbuild-parent-container -d onbuild-parent-example
如果您导航到http://127.0.0.1:8080/
端点,您应该会看到默认的 Apache 主页
移除容器,以免干扰端口
docker container stop onbuild-parent-container
docker container rm onbuild-parent-container
现在,让我们创建一个 Docker 镜像onbuild-parent-container
作为基础镜像,用于部署自定义 HTML 主页。为此,我们需要创建一个名为onbuild-child-example
cd ..
mkdir onbuild-child-example
创建一个包含以下内容的新 html 页面
<html>
<body>
<h1>Demonstrating Docker ONBUILD Directive</h1>
</body>
</html>
在同一目录中创建Dockerfile
FROM onbuild-parent-example
这个Dockerfile只有一个指令。它将使用FROM指令来利用onbuild-parent-example
我们之前创建的 Docker 镜像作为基础镜像。
现在,构建 docker 镜像
docker image build -t onbuild-child-example .
输出应该类似于以下内容
[+] Building 0.3s (7/7) FINISHED docker:default
=> [internal] load .dockerignore 0.1s
=> => transferring context: 2B 0.0s
=> [internal] load build definition from Dockerfile 0.1s
=> => transferring dockerfile: 64B 0.0s
=> [internal] load metadata for docker.io/library/onbuild-parent-example:latest 0.0s
=> [internal] load build context 0.1s
=> => transferring context: 134B 0.0s
=> [1/1] FROM docker.io/library/onbuild-parent-example 0.1s
=> [2/1] COPY *.html /var/www/html 0.0s
=> exporting to image 0.1s
=> => exporting layers 0.0s
=> => writing image sha256:9fb3629a292e2536300724db933eb59a6fb918f9d01e46a01aff18fe1ad6fe69 0.0s
=> => naming to docker.io/library/onbuild-child-example 0.0s
执行docker container run
命令,用我们刚刚构建的镜像启动一个新的容器
docker container run -p 8080:80 --name onbuild-child-container -d onbuild-child-example
index.html
如果您导航到端点,您现在应该能够查看我们的自定义页面http://127.0.0.1:8080/
。
概括
在本文中,我们重点介绍了 Docker 镜像的构建。我们讨论了更高级的 Dockerfile 指令,包括ENV、ARG、WORKDIR、COPY、ADD、USER、VOLUME、EXPOSE、HEALTHCHECK和ONBUILD指令。
在接下来的几篇文章中,我们将讨论什么是 Docker 注册表,了解私有和公共 Docker 注册表以及如何将映像发布到 Docker 注册表。
系好安全带,小伙子们!码头工人的旅程即将更加刺激。想了解更多,可以看看本系列的第一篇文章。它能帮你一站式了解所有细节。
文章来源:https://dev.to/kalkwst/advanced-dockerfile-directives-193f