Dockerfile 详解

🏠 回到主页

dockerfile

🐳 Docker 可以通过读取 Dockerfile 中的指令自动构建镜像. Dockerfile是一个文本文档, 它包含用户可以在命令行上调用的所有命令来组装一个镜像

🐳 格式

# 注释
INSTRUCTION arguments

Docker将以#开头的行视为注释

Dockerfile 的指令不区分大小写. 但是, 按照惯例, 应该使用大写的, 目的是方便的将它们与参数区分开来

Docker在Dockerfile中按顺序运行指令. Dockerfile必须以FROM指令开始. FROM指令指定要构建的父映像. FROM前面只能有一个或多个ARG指令, 这些指令声明Dockerfile中FROM行中使用的参数

 

🐳 Dockerfile 构建指令

🔘 FROM

FROM指令是Dockerfile的第一个指令,用于指定基础镜像。基础镜像是指Docker容器的根镜像,它包含了操作系统和一些常用的软件。在构建新的Docker容器时,我们可以基于一个已有的基础镜像来构建

使用FROM指令的语法如下:

FROM <image>[:<tag>] [AS <name>]

其中,image是基础镜像的名称,tag是基础镜像的版本号,AS name是可选的,用于给FROM指令指定一个别名

例如:

FROM ubuntu:16.04

该指令会基于Ubuntu 16.04版本的基础镜像来构建新的Docker容器

 

🔘 MAINTAINER

MAINTAINER指令用于指定维护者的信息,包括姓名和电子邮件地址

使用MAINTAINER指令的语法如下:

MAINTAINER <name> [<email>]

例如:

MAINTAINER John Doe <johndoe@example.com>

 

🔘 ARG

用于定义构建参数,这些参数可以在构建镜像时传递给 Docker 构建命令

ARG 的语法为:

ARG <name>[=<default value>]

其中 <name> 是参数名,<default value> 是可选的默认值

在 Dockerfile 中可以使用 ${<name>} 的形式来引用参数

例如,假设我们有以下 Dockerfile:

ARG NODE_VERSION=14
FROM node:${NODE_VERSION}
WORKDIR /app
COPY package*.json ./
RUN npm install
COPY . .
EXPOSE 3000
CMD ["npm", "start"]

 

在构建镜像时,可以通过 --build-arg 参数来传递值,例如:

docker build --build-arg NODE_VERSION=16 -t my-node-app .

这会使用 Node.js 16.x 版本构建镜像

如果不传递 --build-arg 参数,则使用默认值,即 Node.js 14.x 版本构建镜像

注意:在 Dockerfile 中定义的 ARG 变量只在构建过程中存在,不会在容器运行时存在

 

🔘 ENV

ENV指令用于设置环境变量。在Docker容器中,可以使用ENV指令设置一些常用的环境变量,如PATH、HOME等

使用ENV指令的语法如下:

ENV <key> <value>

例如:

ENV LANG en_US.UTF-8

该指令会将Docker容器的默认语言设置为en_US.UTF-8

 

🔘 LABEL

用于向镜像添加元数据(metadata),元数据是一些描述镜像的键值对,可以包含版本、描述、维护者、构建日期等等信息。这些元数据可以帮助用户更好地管理和使用镜像

LABEL 指令的语法为:

LABEL key=value

其中,key 为元数据的键,value 为元数据的值。可以在 Dockerfile 中使用多个 LABEL 指令来添加多个元数据。

以下是一个使用 LABEL 指令的 Dockerfile 示例:

FROM ubuntu:latest
LABEL maintainer="John Doe <johndoe@example.com>"
LABEL version="1.0"
LABEL description="This is a sample Docker image"

上述 Dockerfile 定义了三个 LABEL 指令,分别添加了维护者、版本和描述信息。这些元数据可以使用 docker image inspect 命令查看,例如:

$ docker image inspect ubuntu:latest -f '{{json .Config.Labels}}' | jq
{
  "maintainer": "John Doe <johndoe@example.com>",
  "version": "1.0",
  "description": "This is a sample Docker image"
}

使用 LABEL 指令的好处是,可以在镜像中添加一些有用的元数据,方便用户更好地管理和使用镜像。例如,可以使用 maintainer 元数据来指定维护者的联系方式,方便用户在使用镜像时获得支持和反馈

 

🔘 USER

USER指令用于设置Docker容器中执行命令的用户。在Docker容器中,可以使用USER指令将执行命令的用户从root用户切换到其他用户。

使用USER指令的语法如下:

USER <user>[:<group>]

例如:

该指令会将Docker容器中执行命令的用户切换到nginx用户

 

🔘 ADD

ADD指令用于将本地文件或目录复制到Docker容器中。在构建Docker镜像时,可以使用ADD指令将应用程序的代码和配置文件复制到容器中

使用ADD指令的语法如下:

ADD <src> <dest>

其中,src是本地文件或目录的路径,dest是Docker容器中的目标路径

例如:

ADD app /app

该指令会将本地的app目录复制到Docker容器的/app目录中

 

🔘 WORKDIR

WORKDIR指令用于设置Docker容器的工作目录。在Docker容器中,可以使用WORKDIR指令设置一个工作目录,以便在执行命令时不必每次都指定完整的路径。

使用WORKDIR指令的语法如下:

WORKDIR <path>

例如:

WORKDIR /app

该指令会将Docker容器的工作目录设置为/app

 

🔘 COPY

COPY指令与ADD指令类似,用于将本地文件或目录复制到Docker容器中。与ADD指令不同的是,COPY指令不会自动解压缩压缩文件,并且不支持从远程URL复制文件

使用COPY指令的语法如下:

COPY <src> <dest>

例如:

COPY app /app

该指令会将本地的app目录复制到Docker容器的/app目录中

 

🔘 RUN

RUN指令用于在Docker容器中执行命令或者脚本。在构建Docker镜像时,可以使用RUN指令执行一系列的命令来安装软件包、配置环境变量等操作

使用RUN指令的语法如下:

RUN <command>

例如:

RUN apt-get update && apt-get install -y nginx

该指令会在Docker容器中执行apt-get update和apt-get install -y nginx两个命令,以安装Nginx

 

🔘 ONBUILD

 用于定义一个在当前镜像被用作其他镜像的基础镜像时需要执行的操作。也就是说,ONBUILD 指令是一种在构建一个镜像时预先为将来的使用者定义一些构建操作的方法

ONBUILD 指令的语法为:

ONBUILD [INSTRUCTION]

其中,INSTRUCTION 可以是任何有效的 Dockerfile 指令,例如 ADDRUNCOPY 等。

当一个镜像被用作其他镜像的基础镜像时,Docker 将按照顺序执行该镜像中所有 ONBUILD 指令。这些操作会在构建子镜像时执行。也就是说,ONBUILD 指令定义了一些在子镜像构建时需要执行的操作。

例如,以下是一个使用 ONBUILD 指令的 Dockerfile 示例:

FROM ubuntu:latest
ONBUILD RUN echo "This is a build trigger message"

上面的 Dockerfile 中定义了一个 ONBUILD 指令,该指令会在当前镜像被用作其他镜像的基础镜像时执行。也就是说,当其他镜像从当前镜像构建时,Docker 将执行 echo "This is a build trigger message" 这个命令

在实际使用中,ONBUILD 指令通常用于定义一些通用的构建操作,例如安装依赖包、编译代码等。这样,其他用户在创建镜像时就不需要重复执行这些操作了

需要注意的是,ONBUILD 指令只有在当前镜像被用作其他镜像的基础镜像时才会执行。如果当前镜像不被用作其他镜像的基础镜像,则 ONBUILD 指令不会执行

 

🔘 VOLUME

VOLUME指令用于设置Docker容器的数据卷。数据卷是指Docker容器中的一个特殊目录,它可以被共享和重用,可以在容器之间传递数据

使用VOLUME指令的语法如下:

VOLUME <path> [<path>...]

例如:

VOLUME /data

该指令会在Docker容器中创建一个名为/data的数据卷

 

🔘 STOPSIGNAL

 用于设置容器停止时发送给 Docker 守护进程的信号。该指令允许用户定义一个自定义的信号,以便在容器停止时进行清理工作或优雅地关闭服务

STOPSIGNAL 指令的语法为:

STOPSIGNAL signal

其中,signal 可以是一个数字或一个信号名称,例如 SIGTERMSIGINT

默认情况下,当容器停止时,Docker 会发送 SIGTERM 信号给容器内的主进程,如果主进程没有在一定时间内停止运行,Docker 会强制发送 SIGKILL 信号来强制终止容器。使用 STOPSIGNAL 指令可以将默认的停止信号改为其他信号

以下是一个使用 STOPSIGNAL 指令的 Dockerfile 示例:

FROM ubuntu:latest
STOPSIGNAL SIGINT

 

上述 Dockerfile 定义了一个 STOPSIGNAL 指令,以便在容器停止时发送 SIGINT 信号。这意味着,当用户停止容器时,Docker 守护进程会发送 SIGINT 信号给容器内的主进程,以便进行清理工作或优雅地关闭服务

需要注意的是,STOPSIGNAL 指令只能在 Dockerfile 中使用一次,且必须位于 Dockerfile 的末尾。此外,STOPSIGNAL 指令只适用于 Linux 系统

 

在 Linux 系统中,可以使用 kill -l 命令列出所有支持的信号类型。以下是常见的一些信号类型及其含义:

  • SIGHUP:终端挂起或控制进程终止信号
  • SIGINT:中断进程信号
  • SIGQUIT:退出进程信号
  • SIGKILL:强制终止进程信号
  • SIGTERM:终止进程信号
  • SIGUSR1:用户自定义信号 1
  • SIGUSR2:用户自定义信号 2
  • SIGPIPE:管道破裂信号
  • SIGALRM:定时器信号
  • SIGSEGV:段错误信号
  • SIGCHLD:子进程状态改变信号
  • SIGCONT:继续执行进程信号
  • SIGSTOP:停止进程信号
  • SIGTSTP:终端停止进程信号
  • SIGTTIN:后台进程从终端读取数据信号
  • SIGTTOU:后台进程向终端输出数据信号

需要注意的是,不是所有的信号类型都可以作为 STOPSIGNAL 指令的参数。在 Docker 官方文档中,只有 SIGHUPSIGINTSIGQUITSIGABRTSIGKILL 和 SIGTERM 可以被用作 STOPSIGNAL 指令的参数

 

🔘 EXPOSE

EXPOSE指令用于设置Docker容器对外暴露的端口号。当Docker容器启动时,可以通过指定的端口号来访问容器中运行的应用程序

使用EXPOSE指令的语法如下:

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

例如:

EXPOSE 80

该指令会将Docker容器的80端口对外暴露,以便外部访问

 

🔘 HEALTHCHECK

用于定义镜像的健康检查方式,以便在容器运行时监控容器的健康状况

HEALTHCHECK 的语法为:

HEALTHCHECK [OPTIONS] CMD command

其中 OPTIONS 可选,可以指定一些参数,例如:

  • --interval=<duration>:设置检查间隔,默认为 30 秒
  • --timeout=<duration>:设置检查超时时间,默认为 30 秒
  • --start-period=<duration>:容器启动后,等待指定时间再进行第一次检查,默认为 0 秒

CMD 指定了要执行的检查命令

例如,以下是一个使用 HEALTHCHECK 的 Dockerfile 示例:

FROM node:12
WORKDIR /app
COPY package*.json ./
RUN npm install
COPY . .
EXPOSE 3000
HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 CMD curl --fail http://localhost:3000/ || exit 1
CMD ["npm", "start"]

上面的 Dockerfile 中定义了一个健康检查方式,每 30 秒执行一次 curl 命令,检查应用程序是否能够响应 HTTP 请求。如果检查失败,则容器的健康状况被标记为 unhealthy

在运行容器时,可以使用 docker ps 命令查看容器的健康状况:

$ docker ps --format "table {{.Names}}\t{{.Status}}\t{{.Health}}"
NAMES            STATUS             HEALTH
my-node-app      Up 5 seconds       healthy

上面的输出表明,容器 my-node-app 的健康状况为 healthy。如果健康状况为 unhealthy,则说明容器的应用程序出现了问题,需要进一步诊断和修复 

 

🔘 SHELL

 用于在 Dockerfile 中设置默认的 shell 环境。在 Dockerfile 中执行的所有命令都将使用该 shell

SHELL 指令的语法为:

SHELL ["executable", "parameters"]

其中,executable 指定要使用的 shell 可执行文件,parameters 指定要传递给可执行文件的参数。

例如,以下是一个使用 SHELL 指令的 Dockerfile 示例:

FROM ubuntu:latest
SHELL ["/bin/bash", "-c"]
RUN echo "Hello, World!"

上面的 Dockerfile 中使用 SHELL 指令将默认的 shell 环境设置为 /bin/bash,并将 -c 参数传递给该 shell。因此,在执行 RUN 命令时,Docker 将使用 /bin/bash -c 来解释命令。

在实际使用中,可以根据需要设置不同的 shell 环境,例如:

SHELL ["/bin/sh", "-c"]
SHELL ["/bin/bash", "-c"]
SHELL ["/usr/bin/env", "bash"]

注意,SHELL 指令只能在 Dockerfile 中定义一次,且必须作为 Dockerfile 的第一条指令。此外,SHELL 指令的参数必须是一个数组,不能是字符串

 

🔘 CMD

CMD指令用于设置Docker容器启动时执行的默认命令或者脚本。当Docker容器启动时,会自动执行CMD指令指定的命令或者脚本

使用CMD指令的语法如下:

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

其中,executable是要执行的命令或者脚本,param1和param2是可选的参数

例如:

CMD ["nginx", "-g", "daemon off;"]

该指令会在Docker容器启动时自动执行nginx -g daemon off;命令

 

🔘 ENTRYPOINT

ENTRYPOINT指令用于设置Docker容器的入口点,即Docker容器启动时要执行的命令或脚本。

使用ENTRYPOINT指令的语法如下:

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

例如:

ENTRYPOINT ["nginx"]

该指令会将nginx命令设置为Docker容器的入口点

 

 

🐳 .dockerignore 文件

Docker CLI将上下文发送给Docker守护进程之前, 它会在上下文的根目录中查找一个名为.dockerignore的文件. 如果该文件存在, CLI将修改上下文以排除其中匹配模式的文件和目录. 这有助于避免不必要地向守护进程发送大的敏感的文件和目录, 因为可能使用ADDCOPY将它们误添加到镜像中

CLI 将.dockerignore文件解释为一个换行符分隔的模式列表, 类似于Unix shellglob文件. 为了进行匹配, 将上下文的根视为工作目录和根目录. 例如, 模式/foo/barfoo/bar都排除了PATH的foo子目录中名为bar的文件或目录, 或者是位于URL的git存储库根目录中名为bar的文件或目录. 两者都没有排除其他任何东西

如果.dockerignore文件中的行以#开头, 那么这一行被认为是注释,在CLI解释之前被忽略

下面是一个示例.dockerignore文件:

# comment
*/temp*
*/*/temp*
temp?

 

该文件会导致以下构建行为:

规则 行为
# comment 被忽略
*/temp* 排除根目录下所有直接子目录中名称以temp开头的文件和目录. 例如, 普通文件/somedir/temporary.txt和目录/somedir/temp都会被排除
*/*/temp* 在根目录下两层的子目录中排除以temp开头的文件和目录. 例如:/somedir/subdir/temporary.txt
temp? 排除根目录下以temp为后缀名的文件和目录, 如/tempa/tempb

匹配规则是使用Go的filepath.Match完成的. 程序会预先删除首尾空白符, 然后使用Go的filepath.Clean消除..., 预处理后为空的行将被忽略

除了Go的filepath.Match规则之外, Docker还支持一个特殊的通配符字符串**, 它可以匹配任意数量的目录(包括0). 例如 **/*.go 将排除所有目录中所有以.go结尾的文件, 包括构建上下文的根目录

!(感叹号)开头的行可用于排除规则中的例外. 下面是一个使用此机制的.dockerignore文件示例:

*.md
!README.md

➡️ 除了README.md, 所有以.md结尾的文件都被排除在构建上下文之外

!的使用位置会影响行为: 匹配特定文件的 .dockerignore 的最后一行决定它是被包含还是被排除. 考虑下面的例子:

*.md
!README*.md
README-secret.md

➡️ 除了README-secret.md之外, 上下文中不包含任何markdown文件

再看看下面的例子:

*.md
README-secret.md
!README*.md

➡️ 所有的README文件都包含在内, 因为!README*.包含README-secret.md ,并且被放到最后一行

 

👉 你甚至可以使用.dockerignore文件来排除Dockerfile.dockerignore文件. 这些文件仍然被发送给Docker守护进程, 因为Docker需要它们来完成工作. 但是ADDCOPY指令不会将它们复制到镜像中

👉 最后, 我们一般情况下希望指定在上下文中包含哪些文件, 而不是排除哪些文件. 要实现这一点, 可以指定*作为第一个模式, 后面跟着一个或多个!模式

 

 

🏠 回到主页

 

 

 

posted @ 2022-10-28 14:58  SuperCodeX  阅读(20)  评论(0编辑  收藏  举报