《Docker极简教程》--Dockerfile--Dockerfile的基本语法
Dockerfile是一种文本文件,用于定义Docker镜像的内容和构建步骤。它包含一系列指令,每个指令代表一个构建步骤,从基础镜像开始,逐步构建出最终的镜像。通过Dockerfile,用户可以精确地描述应用程序运行环境的配置、依赖项安装、文件复制等操作。这使得应用程序的部署和分发变得更加可控和可重复。Dockerfile的内容可以根据需求自定义,允许开发者根据应用程序的特性和需求来灵活配置镜像的构建过程,从而实现高效、可靠的容器化部署。
一、Dockerfile语法
1.1 指令
-
FROM
在Dockerfile中,FROM语句用于指定基础镜像,即构建新镜像所需的起始点。基础镜像是构建过程中的第一步,它提供了操作系统和运行环境的基本配置。FROM语句的基本语法如下:FROM <镜像名称>[:<标签>]
其中:
<镜像名称>
:指定所使用的基础镜像的名称。<标签>
:(可选)指定所使用的基础镜像的版本或标识符。如果不指定标签,则默认使用latest标签。
示例:
FROM ubuntu:20.04
这个示例指定了基于Ubuntu 20.04版本的官方镜像作为基础镜像。在构建新镜像时,Docker引擎会从Docker Hub或本地镜像仓库中获取指定的基础镜像,并在其基础上执行后续的构建步骤。
-
RUN
在Dockerfile中,RUN指令用于在镜像中执行命令。这些命令通常用于安装软件包、更新系统、配置环境变量等。RUN指令可以多次出现,每次出现都会在镜像中创建一个新的中间层,这些中间层将用于构建最终的镜像。RUN指令的基本语法如下:RUN <command>
其中
<command>
是要执行的命令,可以是任何有效的Linux命令或Shell命令。可以使用反斜杠(\)将一条命令拆分为多行,或者使用&&
连接多个命令,以确保在同一层中执行,从而减少镜像大小。示例:FROM ubuntu:20.04 RUN apt-get update && apt-get install -y \python3 \python3-pip \&& rm -rf /var/lib/apt/lists/*
这个示例中,
RUN
指令用于更新APT包列表并安装Python3及其相关的软件包。最后,使用rm -rf /var/lib/apt/lists/*
命令清理APT缓存,以减少镜像大小。 -
COPY
COPY
指令用于将文件或目录从构建上下文中的源路径复制到容器文件系统中的目标路径。这个指令对于将本地文件或目录复制到镜像中是非常有用的。COPY
指令的基本语法如下:COPY <源路径> <目标路径>
其中:
<源路径>
:指定要复制的文件或目录在构建上下文中的路径。这个路径是相对于Dockerfile
所在目录的路径。<目标路径>
:指定将文件或目录复制到容器中的位置。这个路径是相对于容器的根目录的路径。
示例:
FROM ubuntu:20.04 COPY ./app /app
在这个示例中,假设在与
Dockerfile
相同的目录下有一个名为app
的目录,COPY
指令将会把这个目录下的所有内容复制到容器中的/app
目录下。Tip:
COPY
指令只能复制本地文件系统中的文件或目录,不能从URL或远程文件系统中复制文件。 -
ADD
ADD
指令与COPY
指令类似,都用于将文件从构建上下文中复制到容器中。但ADD
指令不仅可以复制本地文件,还可以解压缩压缩文件、使用URL等。ADD
指令的基本语法如下:
ADD <源路径> <目标路径>
其中:
<源路径>
:指定要复制的文件或目录在构建上下文中的路径。这个路径是相对于Dockerfile
所在目录的路径。<目标路径>
:指定将文件或目录复制到容器中的位置。这个路径是相对于容器的根目录的路径。
示例:
FROM ubuntu:20.04
ADD ./app.tar.gz /app
在这个示例中,假设在与Dockerfile
相同的目录下有一个名为app.tar.gz
的压缩文件,ADD
指令将会把这个压缩文件解压缩并将其中的内容复制到容器中的/app
目录下。
Tip:相比于
COPY
指令,ADD
指令具有更多的功能,但也可能引入一些不必要的复杂性,因此在一般情况下,建议尽量使用COPY
指令来复制文件。
-
CMD
CMD
指令用于在容器启动时执行特定的命令或指定容器的默认执行命令。每个Dockerfile只能包含一个CMD
指令,如果有多个,则只有最后一个生效。如果在运行容器时提供了命令,则会覆盖CMD
指令中定义的默认命令。
CMD
指令有两种形式:Shell形式和Exec形式。- Shell形式:
其中CMD <command>
<command>
可以是任何Shell命令,例如:CMD echo "Hello, world!"
- Exec形式:
其中CMD ["executable", "param1", "param2"]
["executable", "param1", "param2"]
是一个JSON数组,用于指定可执行文件及其参数,例如:CMD ["python", "-u", "app.py"]
在这个示例中,指定了执行Python脚本
app.py
的命令。
如果Dockerfile中没有CMD
指令,则会使用基础镜像中的默认CMD指令,如果基础镜像中也没有默认CMD指令,则容器启动时将会立即退出。 - Shell形式:
-
ENTRYPOINT
ENTRYPOINT
指令用于设置容器启动时要执行的命令。与CMD
指令不同,ENTRYPOINT
指定的命令不会被覆盖,而是作为容器的主要执行命令。如果在运行容器时提供了命令,则会被传递给ENTRYPOINT
指定的命令作为参数。
ENTRYPOINT
指令的语法有两种形式:Shell形式和Exec形式。- Shell形式:
其中ENTRYPOINT <command>
<command>
可以是任何Shell命令,例如:ENTRYPOINT echo "Hello, world!"
- Exec形式:
其中ENTRYPOINT ["executable", "param1", "param2"]
["executable", "param1", "param2"]
是一个JSON数组,用于指定可执行文件及其参数,例如:ENTRYPOINT ["python", "app.py"]
在这个示例中,指定了以Python解释器执行
app.py
脚本的命令。
使用ENTRYPOINT
指令的主要优点是可以在容器启动时提供固定的执行环境,从而确保容器始终以相同的方式运行。通常,ENTRYPOINT
指令与CMD
指令一起使用,CMD
指定默认参数,但用户可以在运行容器时覆盖这些参数。 - Shell形式:
-
WORKDIR
WORKDIR
指令用于在容器内设置工作目录,即定义容器启动时的默认工作路径。当容器启动后,任何后续命令都会在该目录下执行。如果工作目录不存在,WORKDIR
指令会自动创建。
WORKDIR
指令的基本语法如下:WORKDIR <路径>
其中
<路径>
是容器中的工作目录路径。该路径可以是相对路径(相对于上一个WORKDIR
指令的路径)或绝对路径。示例:FROM ubuntu:20.04 WORKDIR /app
在这个示例中,
WORKDIR
指令将容器的工作目录设置为/app
。当容器启动后,所有后续的命令都会在/app
目录下执行。 如果该目录不存在,Docker将自动创建该目录。
使用WORKDIR
指令可以使Dockerfile更加简洁和可读,同时也可以确保容器内部的命令都在预期的工作目录中执行,提高了容器的可维护性。 -
ENV
ENV
指令用于设置环境变量,这些环境变量可以在构建和运行过程中被Docker容器使用。通过设置环境变量,可以在容器中指定一些常量或配置,以便于应用程序的正确运行。
ENV
指令的基本语法如下:ENV <key> <value>
其中
<key>
是环境变量的名称,<value>
是环境变量的值。
示例:FROM ubuntu:20.04 ENV LANG C.UTF-8
在这个示例中,
ENV
指令设置了LANG
环境变量为C.UTF-8
。这个环境变量的设置将影响容器中所有的进程,确保它们以正确的字符集编码运行。
除了上述的基本语法外,还可以使用ENV
指令定义多个环境变量,或者使用${variable}
来引用其他环境变量,例如:FROM ubuntu:20.04 ENV JAVA_HOME /usr/lib/jvm/java-11-openjdk-amd64 ENV PATH $PATH:$JAVA_HOME/bin
这个示例中,
PATH
环境变量被修改,以包含Java的可执行文件目录,这样就可以直接在命令行中运行Java命令了。
使用ENV
指令可以使Dockerfile更加灵活和可配置,同时也方便了容器内部应用程序的管理和调试。 -
EXPOSE
EXPOSE
指令用于指定容器在运行时将监听的端口,但它并不会实际打开或映射这些端口。它只是将指定的端口号添加到容器的元数据中,以便于与外部环境进行交互时提供一些提示信息。
EXPOSE
指令的基本语法如下:EXPOSE <port> [<port>/<protocol>...]
其中:
<port>
是要暴露的端口号。<protocol>
是要使用的协议(通常是 TCP 或 UDP)。如果未指定协议,默认为 TCP。
示例:
FROM ubuntu:20.04 EXPOSE 80
这个示例中,
EXPOSE
指令指定容器将监听80端口,但是并没有指定协议,默认为TCP。当运行容器时,可以通过-p
参数来映射宿主机上的端口到容器中的80端口。docker run -p 8080:80 <image_name>
这个命令将容器内部的80端口映射到宿主机的8080端口上。
Tip:
EXPOSE
指令并不是强制性的,它只是一种标记机制,用于告诉用户容器内部的服务所监听的端口。在容器运行时,仍然需要使用-p
参数来映射端口,否则容器内部的端口对外部是不可访问的。 -
VOLUME
VOLUME
指令用于在容器中创建一个挂载点,并将其链接到主机上的一个目录,从而允许容器中的数据持久化保存到主机上。这个挂载点可以用来存储容器生成的数据,以便于在容器重新启动或迁移时保持数据的持久性。
VOLUME
指令的基本语法如下:VOLUME ["<路径>"]
其中
<路径>
是容器中的目录路径,这个目录将被指定为挂载点。如果省略路径,则表示使用匿名挂载点,Docker将为挂载点自动分配一个路径。
示例:FROM ubuntu:20.04 VOLUME ["/data"]
在这个示例中,
VOLUME
指令创建了一个名为/data
的挂载点,它将与主机上的一个目录进行关联。当容器运行时,可以使用-v
参数将宿主机上的目录挂载到容器中,例如:docker run -v /host/path:/data <image_name>
这个命令将宿主机上的
/host/path
目录挂载到容器中的/data
目录,容器内部的数据操作将直接反映到主机上挂载的目录中。
使用VOLUME
指令可以实现容器内部数据的持久化存储,从而实现容器的数据共享和迁移。 -
USER
USER
指令用于设置容器中运行命令的用户或用户组。通过USER
指令,可以切换到指定的用户或用户组来增强容器的安全性和隔离性。
USER
指令的基本语法如下:USER <用户名>[:<组名>] or <UID>[:<GID>]
其中:
<用户名>
:指定要切换到的用户名。<组名>
:(可选)指定要切换到的组名。如果未指定组名,则使用与用户名相同的组。<UID>
:指定要切换到的用户ID。<GID>
:(可选)指定要切换到的组ID。如果未指定组ID,则使用与用户ID相同的组ID。
示例:
FROM ubuntu:20.04 RUN groupadd -r mygroup && useradd -r -g mygroup myuser USER myuser
在这个示例中,首先通过
RUN
指令创建了一个名为myuser
的用户和一个名为mygroup
的用户组。然后使用USER
指令切换到myuser
用户,接下来的命令将以myuser
用户的身份执行。
使用USER
指令可以降低容器内部命令执行的权限,从而增加了容器的安全性。通常建议在Docker镜像中使用非特权用户来运行应用程序,以最小化潜在的安全风险。 -
LABEL
LABEL
指令用于为Docker镜像添加元数据,可以用来提供关于镜像的信息、版本、作者等。这些标签信息可以在构建和运行镜像时被查看和使用,有助于组织和管理镜像。
LABEL
指令的基本语法如下:LABEL <key>=<value> <key>=<value> ...
其中
<key>
是标签的名称,<value>
是标签的值。
示例:FROM ubuntu:20.04 LABEL maintainer="John Doe <john@example.com>" LABEL version="1.0" LABEL description="This is a sample Dockerfile demonstrating the use of LABEL instruction."
在这个示例中,使用了三个
LABEL
指令,分别指定了镜像的维护者、版本和描述信息。
标签信息可以通过docker inspect
命令来查看,例如:docker inspect --format='{{json .Config.Labels}}' <image_name>
这个命令将会输出镜像的所有标签信息。
使用LABEL
指令可以提高镜像的可读性和可管理性,使其更易于识别和使用。 -
ARG
ARG
指令用于定义构建时的参数,这些参数可以在Dockerfile
中使用,并且可以在构建镜像时通过命令行参数进行覆盖。ARG
指令可以用于在构建过程中传递变量,从而实现动态配置镜像的构建过程。
ARG
指令的基本语法如下:
ARG <name>[=<default value>]
其中 <name>
是参数的名称, <default value>
是参数的默认值。如果未提供默认值,则参数可以在构建过程中通过--build-arg
选项进行传递。
示例:
FROM ubuntu:20.04
ARG APP_VERSION=1.0
ENV APP_VERSION=${APP_VERSION}
在这个示例中,定义了一个名为APP_VERSION
的构建参数,并设置了默认值为1.0
。然后将这个参数赋值给APP_VERSION
环境变量,使其在镜像中可用。
在构建镜像时,可以通过--build-arg
选项来覆盖默认值,例如:
docker build --build-arg APP_VERSION=2.0 -t myimage .
这个命令将会使用2.0
作为APP_VERSION
的值进行构建。
使用ARG
指令可以使Dockerfile
更加灵活和可配置,允许在构建时根据需要动态设置参数。
1.2 注释和空白行
在Dockerfile中,注释和空白行可以帮助提高文件的可读性,并且可以用于添加注释和分隔构建步骤。注释和空白行在Dockerfile中不会被解释为指令,它们只是用于提供额外的说明和组织结构。
- 注释:
在Dockerfile中,可以使用#
符号添加注释,#
符号后的内容将被视为注释,直到行尾。注释可以用于解释每个指令的作用、提供版本信息、添加作者信息等。例如:# This is a Dockerfile for building a custom nginx image # Version 1.0 # Author: John DoeFROM nginx:latest
- 空白行:
空白行用于在Dockerfile中创建可读性更好的结构,可以用于分隔不同的构建步骤,或者用于增加可读性。在Dockerfile中,空白行是没有任何指令的行,或者只包含空格或制表符的行。例如:
在这个示例中,空白行被用于分隔不同的构建步骤,使得Dockerfile更加清晰易读。FROM nginx:latest# Install additional packages RUN apt-get update \&& apt-get install -y \curl \wget# Set up configuration files COPY nginx.conf /etc/nginx/nginx.conf# Expose ports EXPOSE 80# Start nginx service CMD ["nginx", "-g", "daemon off;"]
注释和空白行在Dockerfile中起到了组织结构和解释说明的作用,建议在编写Dockerfile时充分利用它们来提高文件的可读性和可维护性。
二、Dockerfile的最佳实践
2.1 最小化镜像大小
最小化镜像大小是Dockerfile编写的一个重要方面,可以通过以下最佳实践来实现:
- 选择轻量级基础镜像: 选择基于Alpine Linux等轻量级操作系统的基础镜像,而不是使用通用的Linux发行版。轻量级基础镜像通常只包含最基本的软件包和库,可以显著减小镜像大小。
- 单层构建: 尽量将多个命令合并到单个RUN指令中,这样可以减少镜像的层数,进而减小镜像的体积。例如,在安装软件包时,将多个apt-get命令合并成一个RUN指令。
- 清理无用文件: 在每个构建步骤中清理掉不必要的临时文件、APT缓存等。可以使用
apt-get clean
、apt-get autoclean
等命令来清理APT缓存,以减小镜像的大小。 - 避免安装不必要的依赖: 仅安装应用程序运行所必需的依赖项,避免安装不必要的软件包和库。
- 使用多阶段构建: 对于编译型语言(如Go、Java)的应用程序,可以使用多阶段构建来减小镜像大小。在一个阶段中编译应用程序,然后在另一个阶段中将编译好的应用程序复制到最终的镜像中,这样可以减少镜像中不必要的构建工具和依赖项。
- 优化镜像打包: 使用
.dockerignore
文件来排除不必要的文件和目录,减少镜像构建上下文的大小。避免将大量数据和日志文件打包进镜像。 - 精简运行时环境: 确保在运行时只包含应用程序所需的最小文件和配置,避免包含不必要的文件和目录。使用
docker history
命令查看镜像的构建历史,识别不必要的文件和层,进一步优化镜像。
通过遵循上述最佳实践,可以有效地减小镜像的大小,提高镜像的性能和安全性,同时减少存储和网络传输的成本。
2.2 合理使用缓存
合理使用缓存是优化Dockerfile构建过程的重要策略,可以显著减少构建时间和资源消耗。以下是一些合理使用缓存的最佳实践:
- 将频繁变动的步骤放置在最后: Docker会从之前的镜像层缓存中执行步骤,如果某一步骤之后的步骤发生变化,那么之后的所有步骤都会重新构建。因此,将变动较大的步骤放置在Dockerfile的末尾,可以最大程度地利用缓存。
- 合并多个命令: 尽量将多个命令合并为单个RUN指令,这样可以减少镜像的层数,从而减少重复构建的情况。每个RUN指令都会创建一个新的镜像层,因此将多个命令合并到一个RUN指令中可以减少构建层数。
- 使用有效的镜像标签: 当构建基于其他镜像的派生镜像时,如果基础镜像的标签发生了变化,那么所有依赖于该基础镜像的派生镜像都会重新构建。因此,确保使用稳定的镜像标签(如具体版本号)而不是latest等动态标签。
- 利用构建缓存: Docker在构建过程中会使用缓存,可以通过–cache-from选项指定一个已构建镜像来作为构建缓存。这可以在多个构建之间共享缓存,加快构建速度。
- 使用.dockerignore文件: 在项目根目录下创建.dockerignore文件,排除不必要的文件和目录,这样可以减少构建上下文的大小,从而加快构建速度。
- 利用Docker构建缓存指令: 在Dockerfile中使用一些不更改镜像层的指令,如COPY和ADD,这些指令不会使构建缓存失效,可以最大化地利用构建缓存。
通过遵循上述最佳实践,可以最大程度地利用Docker的构建缓存,减少构建时间和资源消耗,提高构建效率。
2.3 保持镜像清洁
保持镜像清洁是维护 Docker 环境的重要实践,可以通过以下方法来实现:
- 定期清理无用镜像和容器: 运行
docker image prune
和docker container prune
命令可以清理掉未使用的镜像和容器。无用的镜像和容器会占用存储空间,并且可能导致资源浪费。 - 避免构建过多的中间镜像层: 在编写 Dockerfile 时,尽量合并多个命令到一个 RUN 指令中,以减少中间镜像层的数量。这样可以降低镜像的大小,并减少构建和存储资源的消耗。
- 删除不必要的文件和目录: 确保在构建镜像时清理掉不必要的临时文件、APT 缓存和其他不需要的文件。在使用
RUN
指令安装软件包后,可以执行清理命令,如apt-get clean
、rm -rf /var/cache/apt/*
等,以减小镜像大小。 - 避免在运行时产生大量临时文件: 在应用程序设计中,尽量避免在运行时产生大量临时文件,以防止镜像过度膨胀。如果必须产生临时文件,建议将其放置在临时文件系统中,以便容器停止时自动清理。
- 使用多阶段构建(Multi-stage builds): 对于需要编译或打包的应用程序,可以使用多阶段构建来减少最终镜像的大小。在第一个阶段中,构建应用程序,并将构建好的文件复制到第二个阶段中。第二个阶段只包含运行时所需的最小文件和依赖项。
- 定期审查和更新镜像: 定期审查镜像并更新其中的软件包和依赖项,以确保镜像中的软件包都是最新的版本。可以使用漏洞扫描工具(如 Clair、Trivy 等)来检查镜像中的漏洞,并及时修复。
- 使用最小化的基础镜像: 选择轻量级的基础镜像,如 Alpine Linux,而不是通用的 Linux 发行版,以减小镜像的大小。避免使用包含大量预安装软件包和依赖项的基础镜像。
通过采取上述措施,可以保持 Docker 环境中的镜像清洁,减小镜像大小,提高镜像的安全性和可维护性,同时节省存储和网络带宽资源。
2.4 使用多阶段构建
多阶段构建(Multi-stage builds)是一种优化 Docker 镜像构建过程的方法,通过在一个 Dockerfile 中定义多个构建阶段来实现。每个阶段都可以基于不同的基础镜像,并且可以包含不同的构建步骤,最终只将最终产物复制到最终的镜像中。这样可以减小最终镜像的大小,同时减少构建过程中的资源消耗。
以下是使用多阶段构建的基本方法:
- 定义多个构建阶段: 在 Dockerfile 中使用多个
FROM
指令来定义多个构建阶段。每个FROM
指令表示一个新的构建阶段的开始。通常第一个阶段用于编译或打包应用程序,而后续的阶段用于创建最终的运行时镜像。 - 在每个阶段中执行必要的构建步骤: 在每个构建阶段中执行必要的构建步骤,包括安装依赖项、编译代码、打包应用程序等。每个阶段可以基于不同的基础镜像,并且可以独立地执行自己的构建步骤。
- 将必要的文件复制到最终阶段: 在最后一个构建阶段中,使用
COPY
指令将之前阶段中生成的必要文件复制到最终的镜像中。通常只需要复制运行时所需的最小文件和依赖项。 - 删除不必要的中间文件: 在每个构建阶段结束时,可以使用
RUN
指令删除不必要的临时文件和依赖项,以减小镜像的大小。
使用多阶段构建可以帮助减小镜像的大小,并且可以降低构建过程中的资源消耗,同时提高了镜像的安全性和可维护性。
2.5 安全性考虑
在 Docker 容器中,保证安全性是至关重要的。以下是一些在 Docker 环境中考虑安全性的重要方面:
- 使用官方镜像或受信任的基础镜像: 建议使用官方提供的镜像或来自受信任来源的基础镜像作为应用程序的基础。官方镜像通常受到更严格的审查和维护,因此更加安全可靠。
- 定期更新镜像: 定期更新基础镜像和应用程序镜像,以确保其中包含的软件包和依赖项都是最新的版本。及时更新可以修复已知的安全漏洞和问题,提高镜像的安全性。
- 最小化容器权限: 在运行容器时,尽量以非特权用户身份运行应用程序,避免使用 root 用户。限制容器的权限可以减小攻击面,提高容器的安全性。
- 使用容器内防火墙: 在容器内部配置防火墙规则,限制网络流量的进出,只允许必要的端口和协议。这可以防止恶意攻击者通过容器内部的漏洞进行攻击。
- 审查 Dockerfile 和镜像内容: 审查 Dockerfile 中的每一步和每个基础镜像中的内容,确保其中没有包含不必要的软件包和依赖项,以及恶意代码。同时,审查镜像中的文件和目录,查找潜在的安全漏洞。
- 限制容器资源: 使用 Docker 容器的资源限制功能,限制容器的 CPU、内存、网络和磁盘等资源使用,防止恶意容器占用过多的系统资源,造成拒绝服务攻击。
- 加强容器间隔离: 使用 Docker 的安全特性,如命名空间和控制组,加强容器之间的隔离性,防止容器间的信息泄漏和攻击。
- 监控和日志记录: 配置适当的监控和日志记录系统,实时监控容器的运行状态和安全事件,及时发现并响应潜在的安全威胁。
保证 Docker 容器的安全性需要综合考虑多个方面,并采取一系列措施来加强容器的安全性。及时更新镜像、最小化容器权限、使用容器内防火墙、审查 Dockerfile 和镜像内容等都是保障 Docker 容器安全的重要措施。
三、示例
3.1 基本示例
以下是一个简单的 Dockerfile 示例,用于构建一个基于 Node.js 的 web 应用程序镜像:
# 使用官方 Node.js 镜像作为基础镜像
FROM node:14# 设置工作目录
WORKDIR /app# 复制 package.json 和 package-lock.json 到工作目录
COPY package*.json ./# 安装应用程序依赖
RUN npm install# 将应用程序文件复制到工作目录
COPY . .# 暴露端口
EXPOSE 3000# 定义容器启动时运行的命令
CMD ["node", "app.js"]
在这个示例中:
- 使用
FROM
指令选择官方 Node.js 14 镜像作为基础镜像。 - 使用
WORKDIR
指令设置工作目录为/app
。 - 使用
COPY
指令将package.json
和package-lock.json
文件复制到工作目录。 - 使用
RUN
指令运行npm install
命令安装应用程序依赖。 - 使用
COPY
指令将应用程序文件复制到工作目录。 - 使用
EXPOSE
指令暴露端口 3000。 - 使用
CMD
指令定义容器启动时运行的命令,这里是运行node app.js
。
这个 Dockerfile 示例用于构建一个简单的 Node.js web 应用程序镜像。你可以根据自己的实际需求和应用程序进行相应的修改和定制。
3.2 多阶段构建示例
以下是一个使用多阶段构建的 Dockerfile 示例,用于构建一个基于 Go 语言的静态网站生成器的镜像:
# 第一阶段:编译 Go 应用程序
FROM golang:1.17 AS builderWORKDIR /app# 将源代码复制到工作目录
COPY . .# 编译应用程序
RUN go build -o static-generator .# 第二阶段:创建最终镜像
FROM alpine:latest# 设置工作目录
WORKDIR /app# 从第一阶段中复制编译好的应用程序
COPY --from=builder /app/static-generator .# 设置容器启动时的命令
CMD ["./static-generator"]
在这个示例中:
- 第一阶段使用
golang:1.17
作为基础镜像,并在其中编译 Go 应用程序。 - 第二阶段使用
alpine:latest
作为基础镜像,并从第一阶段中复制编译好的应用程序。 - 最终的镜像只包含了编译好的应用程序文件,而不包含编译工具和其他不必要的文件。
这个示例演示了如何使用多阶段构建来减小最终镜像的大小,并且使镜像更加精简。
3.3 镜像优化示例
以下是一个镜像优化示例,假设我们要构建一个基于 Python 的 Web 应用程序镜像:
# 使用官方 Python 镜像作为基础镜像
FROM python:3.9-slim# 设置工作目录
WORKDIR /app# 复制依赖文件到工作目录
COPY requirements.txt .# 安装依赖
RUN pip install --no-cache-dir -r requirements.txt# 复制应用程序文件到工作目录
COPY . .# 设置环境变量
ENV FLASK_APP=app.py# 暴露端口
EXPOSE 5000# 启动应用程序
CMD ["flask", "run", "--host=0.0.0.0"]
这个 Dockerfile 示例进行了一些镜像优化:
- 使用
python:3.9-slim
作为基础镜像。-slim
版本相比标准版本来说更小,因为它不包含额外的依赖项和工具。 - 使用
--no-cache-dir
选项在pip install
中安装 Python 依赖项,这可以避免在镜像中生成缓存文件,减小镜像的体积。 - 设置了
FLASK_APP
环境变量,以指定 Flask 应用程序的入口文件。 - 使用
EXPOSE
指令暴露应用程序的端口。 - 使用
CMD
指令定义容器启动时运行的命令,这里是启动 Flask 应用程序。
通过以上优化,可以使得镜像更加精简、高效,减小镜像的大小,同时保证了容器的安全性和可靠性。
四、总结
本文介绍了编写Docker镜像构建脚本的基础知识。首先,通过FROM指令选择基础镜像,然后使用RUN指令运行命令,COPY和ADD指令复制文件,CMD和ENTRYPOINT指令定义容器启动时执行的命令。另外,还介绍了WORKDIR、ENV、EXPOSE、VOLUME、ARG、LABEL等指令的用法。了解并熟练使用这些指令,能够有效地构建出高效、可靠的Docker镜像。
相关文章:
《Docker极简教程》--Dockerfile--Dockerfile的基本语法
Dockerfile是一种文本文件,用于定义Docker镜像的内容和构建步骤。它包含一系列指令,每个指令代表一个构建步骤,从基础镜像开始,逐步构建出最终的镜像。通过Dockerfile,用户可以精确地描述应用程序运行环境的配置、依赖…...
css中, grid-auto-rows: 怎样简写在grid:中
grid-auto-rows:100px; grid-template-columns:1fr 1fr; 👆可以写成👇 grid:auto-flow 100px / 1fr 1fr;在CSS Grid布局中,grid-auto-rows 属性用于指定自动生成的网格容器的行的大小。如果你想要将 grid-auto-rows 的值简写在 grid 属性中&a…...
@ 代码随想录算法训练营第8周(C语言)|Day53(动态规划)
代码随想录算法训练营第8周(C语言)|Day53(动态规划) Day50、动态规划(包含题目 ● 123.买卖股票的最佳时机III ● 188.买卖股票的最佳时机IV ) 123.买卖股票的最佳时机III 题目描述 给定一个数组 price…...

算法-矩阵置零
1、题目来源 73. 矩阵置零 - 力扣(LeetCode) 2、题目描述 给定一个 m x n 的矩阵,如果一个元素为 0 ,则将其所在行和列的所有元素都设为 0 。请使用 原地 算法。 示例 1: 输入:matrix [[1,1,1],[1,0,1…...

xilinx除法器的使用
平台:Vivado2018.3. 芯片:xcku115-flva1517-2-i (active) 最近学习使用了xilinx除法器,在使用过程中出现了很多次除法器的结果和我预计的结果不一致,特此记录学习一下。 参考文件:pg151.下载地址 pg151-div-gen.pdf …...

算法沉淀——递归(leetcode真题剖析)
算法沉淀——递归 01.汉诺塔问题02.合并两个有序链表03.反转链表04.两两交换链表中的节点05.Pow(x, n) 递归是一种通过调用自身的方式来解决问题的算法。在递归算法中,问题被分解为更小的相似子问题,然后通过对这些子问题的解进行组合来解决原始问题。递…...
BERT模型中的input_ids和attention_mask参数
一、概述 1.1 input_ids 在BERT模型及其衍生体中,输入文本首先经过一个分词处理流程,其中文本被细分为单词或子单词(subwords),每个分词随后映射到一个唯一的整数标识符。这些标识符组成了所谓的input_ids数组&#x…...

java+vue_springboot企业设备安全信息系统14jbc
企业防爆安全信息系统采用B/S架构,数据库是MySQL。网站的搭建与开发采用了先进的java进行编写,使用了vue框架。该系统从三个对象:由管理员、人员和企业来对系统进行设计构建。主要功能包括:个人信息修改,对人员管理&am…...

vulhub中Apache Log4j Server 反序列化命令执行漏洞复现(CVE-2017-5645)
Apache Log4j是一个用于Java的日志记录库,其支持启动远程日志服务器。Apache Log4j 2.8.2之前的2.x版本中存在安全漏洞。攻击者可利用该漏洞执行任意代码。 1.我们使用ysoserial生成payload,然后直接发送给your-ip:4712端口即可。 java -jar ysoserial-…...
基于python+django+vue.js开发的医院门诊管理系统/医疗管理系统
功能介绍 平台采用B/S结构,后端采用主流的Python语言进行开发,前端采用主流的Vue.js进行开发。 功能包括:医生管理、科室管理、护士管理、住院管理、药品管理、用户管理、日志管理、系统信息模块。 源码地址 https://github.com/geeeeeee…...
Linux文件系统笔记
文章目录 FILE SYSTEM软硬链接 动静态库 使用别人提供的库 FILE SYSTEM 文件的管理工作: 1.基础知识: 文件 属性 内容不是所有文件都会打开所有的打开的,未打开的文件会进行管理未打开文件,要能做到快速定位文件磁盘–物理存…...

vue封装el-table表格组件
先上效果图: 本文包含了具名插槽、作用域插槽、jsx语法三种: Render.vue(很重要,必须有): <script> export default {name: "FreeRender",functional: true,props: {scope:Object,render: Functio…...
「Python系列」Python数据结构
文章目录 一、数据结构二、相关链接 一、数据结构 Python提供了多种内置的数据结构,这些数据结构在编程中非常有用。以下是Python中常见的一些数据结构: 列表(List): 列表是Python中最常用的数据结构之一,它是一个有…...

MySQL多实例部署:从概念到实操的全面指南
目录 MySQL多实例管理 单实例 什么是多实例 多实例的好处 多实例的弊端 MySQL多实例用在哪些场景 资金紧张的公司 用户并发访问量不大的业务 大型网站也有用多实例 部署MySQL多实例 rpm和源码的优缺点 二进制方式安装mysql 准备二进制mysql运行所需的环境 准备多…...

C++学习Day07之虚函数和纯虚函数
目录 前言一、程序及输出1.1 虚函数1.2 纯虚函数1.2.1 定义、示例1.2.2 引入原因1.2.3 抽象类 二、分析与总结 前言 在 C 中,虚函数和纯虚函数是实现多态性的重要概念。虚函数是在基类中声明为虚函数的函数,在派生类中可以被重写,实现动态联…...

GZ036 区块链技术应用赛项赛题第9套
2023年全国职业院校技能大赛 高职组 “区块链技术应用” 赛项赛卷(9卷) 任 务 书 参赛队编号: 背景描述 随着异地务工人员的增多,房屋租赁成为一个广阔是市场;目前,现有技术中的房屋租赁是由…...

微服务—RabbitMQ高级(延迟消息)
本博客为个人学习笔记,学习网站:2023黑马程序员RabbitMQ入门到实战教程 高级篇章节 目录 延迟消息 死信交换机 延迟消息插件 下载安装 延迟交换机声明 编辑 发送延迟消息 订单状态同步问题 延迟消息 在电商的支付业务中,对于一些库…...
香港服务器如何取消windows的自动更新
大家用过电脑的人对windows系统的自动更新应该都不会陌生,其实香港服务器的使用也是一样的方法。为什么要对香港服 务器windows的自动更新进行关闭呢?其主要原因在于,有些更新是不能更新,一更新话,系统反而会变得不稳定…...

kali虚拟机桥接模式快速设置
第一步:选择 虚拟机 > 设置 > 虚拟机设置,设置桥接模式 不选择复制物理网络连接状态选项: 如果采用DHCP的方式来分配IP地址,当电脑网络从有线或无线网络之间进行移动时,DHCP会重新分配ip地址,即虚拟机…...
「连载」边缘计算(十五)02-18:边缘部分源码(源码分析篇)
(接上篇) ChannelContext struct定义如下所示。 KubeEdge/beehive/pkg/core/context/context.go // ChannelContext is object for Context channel type ChannelContext struct { //ConfigFactory goarchaius.ConfigurationFactory channels map[…...
挑战杯推荐项目
“人工智能”创意赛 - 智能艺术创作助手:借助大模型技术,开发能根据用户输入的主题、风格等要求,生成绘画、音乐、文学作品等多种形式艺术创作灵感或初稿的应用,帮助艺术家和创意爱好者激发创意、提高创作效率。 - 个性化梦境…...
Java如何权衡是使用无序的数组还是有序的数组
在 Java 中,选择有序数组还是无序数组取决于具体场景的性能需求与操作特点。以下是关键权衡因素及决策指南: ⚖️ 核心权衡维度 维度有序数组无序数组查询性能二分查找 O(log n) ✅线性扫描 O(n) ❌插入/删除需移位维护顺序 O(n) ❌直接操作尾部 O(1) ✅内存开销与无序数组相…...
线程与协程
1. 线程与协程 1.1. “函数调用级别”的切换、上下文切换 1. 函数调用级别的切换 “函数调用级别的切换”是指:像函数调用/返回一样轻量地完成任务切换。 举例说明: 当你在程序中写一个函数调用: funcA() 然后 funcA 执行完后返回&…...

【第二十一章 SDIO接口(SDIO)】
第二十一章 SDIO接口 目录 第二十一章 SDIO接口(SDIO) 1 SDIO 主要功能 2 SDIO 总线拓扑 3 SDIO 功能描述 3.1 SDIO 适配器 3.2 SDIOAHB 接口 4 卡功能描述 4.1 卡识别模式 4.2 卡复位 4.3 操作电压范围确认 4.4 卡识别过程 4.5 写数据块 4.6 读数据块 4.7 数据流…...

ios苹果系统,js 滑动屏幕、锚定无效
现象:window.addEventListener监听touch无效,划不动屏幕,但是代码逻辑都有执行到。 scrollIntoView也无效。 原因:这是因为 iOS 的触摸事件处理机制和 touch-action: none 的设置有关。ios有太多得交互动作,从而会影响…...

Mac下Android Studio扫描根目录卡死问题记录
环境信息 操作系统: macOS 15.5 (Apple M2芯片)Android Studio版本: Meerkat Feature Drop | 2024.3.2 Patch 1 (Build #AI-243.26053.27.2432.13536105, 2025年5月22日构建) 问题现象 在项目开发过程中,提示一个依赖外部头文件的cpp源文件需要同步,点…...

SiFli 52把Imagie图片,Font字体资源放在指定位置,编译成指定img.bin和font.bin的问题
分区配置 (ptab.json) img 属性介绍: img 属性指定分区存放的 image 名称,指定的 image 名称必须是当前工程生成的 binary 。 如果 binary 有多个文件,则以 proj_name:binary_name 格式指定文件名, proj_name 为工程 名&…...
A2A JS SDK 完整教程:快速入门指南
目录 什么是 A2A JS SDK?A2A JS 安装与设置A2A JS 核心概念创建你的第一个 A2A JS 代理A2A JS 服务端开发A2A JS 客户端使用A2A JS 高级特性A2A JS 最佳实践A2A JS 故障排除 什么是 A2A JS SDK? A2A JS SDK 是一个专为 JavaScript/TypeScript 开发者设计的强大库ÿ…...
如何配置一个sql server使得其它用户可以通过excel odbc获取数据
要让其他用户通过 Excel 使用 ODBC 连接到 SQL Server 获取数据,你需要完成以下配置步骤: ✅ 一、在 SQL Server 端配置(服务器设置) 1. 启用 TCP/IP 协议 打开 “SQL Server 配置管理器”。导航到:SQL Server 网络配…...

AD学习(3)
1 PCB封装元素组成及简单的PCB封装创建 封装的组成部分: (1)PCB焊盘:表层的铜 ,top层的铜 (2)管脚序号:用来关联原理图中的管脚的序号,原理图的序号需要和PCB封装一一…...