我是基于ChatGPT-turbo-3.5实现的AI助手,在此网站上负责整理和概括文章
本文介绍了Dockerfile作为定义Docker镜像构建过程的文本文件,包含一系列指令(如FROM、RUN、COPY等),通过标准化方式实现镜像的可重复构建。文章详细阐述了Dockerfile的指令格式、构建镜像流程、配置指令与操作指令的分类及用法,并提供了优化建议,如多阶段构建、使用轻量级基础镜像和非root用户运行,以减小镜像体积和提升安全性,具有较高的实用价值。
Dockerfile 是用于定义 Docker 镜像构建过程的文本文件,包含一系列指令(Instructions),指导 Docker 如何自动构建镜像。它是 Docker 生态中的核心组件之一,通过代码化的方式实现镜像的标准化和可重复构建。
# 指令格式
-
指令不区分大小写,但推荐大写(约定俗成,提高可读性):
FROM ubuntu:22.04 # 推荐 from ubuntu:22.04 # 有效但不推荐 -
每条指令占一行,格式为:
INSTRUCTION arguments # 例如: RUN apt-get update COPY ./app /app -
注释: 使用
#开头:# 这是一个注释 FROM alpine:3.14 -
换行与续行: 使用
\分割长命令:RUN apt-get update && \ apt-get install -y \ curl \ git
# 构建镜像流程
- 从基础镜像启动临时容器
- Docker 根据
FROM指令指定的基础镜像(如ubuntu:22.04)创建一个临时容器。 - 此时容器文件系统是基础镜像的只读层。
- Docker 根据
- 执行指令并修改容器
- 按顺序执行 Dockerfile 中的指令(如
RUN、COPY),在临时容器中进行操作(安装软件、复制文件等)。 - 所有修改发生在容器的可写层(容器层,Container Layer)。
- 按顺序执行 Dockerfile 中的指令(如
- 提交为新的镜像层
- 当前指令执行完成后,Docker 会将容器的可写层提交为一个新的只读镜像层(Layer)。
- 该层仅包含当前指令引起的变更(如新增的文件或配置)。
- 类似
docker commit,但完全自动化,无需手动操作。
- 基于新镜像层启动下一个临时容器
- Docker 销毁上一个临时容器,基于刚提交的新镜像层启动一个新的临时容器。
- 新容器继承之前所有层的修改。
- 重复执行直到所有指令完成
- 循环执行步骤 2~4,直到 Dockerfile 中所有指令处理完毕。
- 最终生成一个完整的镜像,包含所有叠加的只读层和一个可选的
CMD/ENTRYPOINT。

# DockerFile指令
dockerfile指令包括配置指令(配置镜像信息)和操作指令(具体执行操作)两部分
官方文档:https://docs.docker.com/engine/reference/builder/
| 分类 | 指令 | 类型 | 作用 | 示例 |
|---|---|---|---|---|
| 配置指令 | FROM |
基础配置 | 指定基础镜像(必须为第一条指令) | FROM ubuntu:22.04 |
LABEL |
元数据 | 添加镜像的元信息(如作者、版本) | LABEL maintainer="[email protected]" |
|
ENV |
环境变量 | 设置容器内的环境变量(运行时可用) | ENV NODE_ENV=production |
|
ARG |
构建变量 | 定义仅在构建时有效的变量 | ARG APP_VERSION=1.0 |
|
WORKDIR |
工作目录 | 设置后续指令的工作目录(类似 cd) |
WORKDIR /app |
|
USER |
运行用户 | 指定运行命令的用户(避免使用 root) |
USER node |
|
EXPOSE |
端口声明 | 声明容器运行时监听的端口(需配合 -p 映射) |
EXPOSE 8080 |
|
VOLUME |
数据卷 | 定义挂载点(用于持久化数据) | VOLUME /data |
|
| 操作指令 | RUN |
命令执行 | 在构建时执行命令(安装软件、配置环境等) | RUN apt-get update && apt-get install -y curl |
COPY |
文件复制 | 复制本地文件到镜像(推荐) | COPY ./src /app/src |
|
ADD |
高级复制 | 类似 COPY,但支持解压和远程 URL(谨慎使用) |
ADD https://example.com/file.tar.gz /tmp/ |
|
CMD |
启动命令 | 指定容器启动时的默认命令(可被 docker run 覆盖) |
CMD ["npm", "start"] |
|
ENTRYPOINT |
入口命令 | 指定容器启动时的入口程序(通常与 CMD 配合) |
ENTRYPOINT ["/app/entry.sh"] |
|
HEALTHCHECK |
健康检查 | 定义容器健康状态检查命令 | `HEALTHCHECK --interval=30s CMD curl -f http://localhost/ | |
# 配置命令
- 作用:定义镜像的元信息、运行时环境、默认配置等。
- 不直接修改文件系统,而是影响镜像的元数据或后续指令的行为。
| 指令 | 说明 | 格式 |
|---|---|---|
| ARG | 定义创建镜像过程中使用的变量 | ARG <name>[=<default value>] |
| FROM | 指定所创建的基础镜像 | FROM <image>:<tag> FROM <image>:<digest> |
| LABEL | 为生成的镜像添加元数据标签信息 | LABEL <key>=<value> <key>=<value> <key>=<value> ... |
| EXPOSE | 声明镜像内服务监听的端口 | EXPOSE <port> [<port>/<portocol>…] |
| ENV | 指定环境变量 | ENV <key> <value> ENV <key>=<value> ... |
| ENTRYPOINT | 指定镜像的默认入口命令 | ENTRYOINT ["executable","param1","param2"]:exec调用执行 ENTRYOINT command param1 param2:shell中执行 |
| VOLUME | 创建一个数据卷挂载点 | VOLUME ["/data"] |
| USER | 指定运行容器时的用户名或UID | USER daemon |
| WORKDIR | 配置工作目录 | WORKDIR /path |
| ONBUILD | 创建子镜像时指定自动执行的操作命令 | ONBUILD [INSTRUCTION] |
| STOPSIGNAL | 指定退出的信号值 | STOPSIGNAL signal |
| HEALTHCHECK | 配置所启动容器如何进行健康检查 | HEALTHCHECK [OPTIONS] CMD command: 根据所执行命令返回值是否为0来判断; HEALTHCHECK NONE: 禁止基础镜像中的健康检查。 |
| SHELL | 指定默认shell类型 | SHELL ["executable", "parameters"] |
# FROM
-
指定基础镜像
FROM <image>:<tag>FROM <image> [AS <name>] FROM <image>[:<tag>] [AS <name>] FROM <image>[@<digest>] [AS <name>] -
FROM必须是第一条指令。如果在一个dockerfile中指定多个镜像时,使用多个FROM指令
-
如果不以任何镜像为基础,那么写法为:
FROM scratch -
结合ARG定义
ARG VERSION=9.3 FROM debian:${VERSION}
# MAINTAINER
-
指定维护者信息。该信息会写入生成镜像的Author属性域中。
-
格式:
MAINTAINER <name> -
根据官方文档,该指令已过时。应该使用
LABEL指令代替,因为它更灵活。- LABEL指令添加元数据到镜像中。如果要使用包含有空格的元数据,可以给key-value加上引号。
LABEL <key>=<value> <key>=<value> <key>=<value> ... LABEL maintainer="[email protected]"
# LABEL
-
为镜像添加一些元数据(metadata),以键值对的形式
LABEL <key>=<value> <key>=<value> <key>=<value> ... -
LABEL会继承基础镜像种的LABEL,如遇到key相同,则值覆盖
-
在
LABEL值中包含空格,请像在命令行中一样使用引号和反斜杠# 比如我们可以添加镜像的作者: LABEL author=fulsun -
一个Dockerfile种可以有多个LABEL,可以在一行上指定多个标签,也可以多个label, 如下:
# 您可以在一行上指定多个标签 LABEL multi.label1="value1" multi.label2="value2" other="value3" LABEL multi.label1="value1" \ multi.label2="value2" \ other="value3" # 指定多个LABEL标签 LABEL "com.example.vendor"="ACME Incorporated" LABEL com.example.label-with-value="foo" LABEL version="1.0" LABEL description="This text illustrates \ that label-values can span multiple lines." -
要查看镜像的标签,请使用
docker image inspect命令。您可以使用 --format 选项仅显示标签;docker image inspect --format='' imageID
# EXPOSE
-
声明镜像内服务监听的端口,默认协议是 TCP
EXPOSE <port> [<port>/<portocol>…] -
使用EXPOSE指令并不会真的发布该端口(即监听宿主主机的相应端口)。为了真的发布该端口,需要在docke run时使用-p或-P参数指定发布端口。
-
如果 docker run,指定了自动映射 -P,那么会将所有暴露的端口随机映射到宿主机的高阶端口
-
如果 docker run,指定了 --net=host 宿主机网络模式,容器中 EXPOSE 指令暴露的端口会直接使用宿主机对应的端口,不存在映射关系
-
-
如果 EXPOSE 暴露的端口确定要和某个宿主机端口建立映射关系,还是要用到 docker run -p 参数
EXPOSE <port> [<port>/<protocol>...] # 例: EXPOSE 80 EXPOSE 80/tcp EXPOSE 80/udp
# ENV
-
ENV指令将环境变量<key>设置为值<value>。这个值将在构建阶段的所有后续指令的环境中, -
该值将被解释为其他环境变量,因此如果引号字符没有转义,它们将被删除。
-
像命令行解析一样,引号和反斜杠可以用于在值中包含空格。
# 一次设置一个(后续会删除) ENV <key> <value> # 一次设置多个 ENV <key>=<value> ... ENV MY_NAME="John Doe" ENV MY_DOG=Rex\ The\ Dog ENV MY_CAT=fluffy ENV MY_NAME="John Doe" MY_DOG=Rex\ The\ Dog \ MY_CAT=fluffy -
当使用生成的镜像运行容器时,使用
ENV设置的环境变量将持久存在于容器内。 -
你可以使用
docker inspect查看这些值,并使用docker run --env <key>=<value>修改它们。docker run --env <key>=<value> image
# VOLUME
-
定义匿名数据卷。在启动容器时忘记挂载数据卷,会自动挂载到匿名卷。
- 避免重要的数据,因容器重启而丢失,这是非常致命的。
- 避免容器不断变大。
-
格式:
VOLUME ["<路径1>", "<路径2>"...] VOLUME <路径> VOLUME ["/data"] -
在启动容器 docker run 的时候,我们可以通过
-v参数修改挂载点。
# USER
-
当服务不需要管理员权限时,可以通过该命令指定运行用户,并且可以在Dockerfile中创建所需要的用户。指定运行容器时的用户名或UID
USER <user>[:<group>] 或 USER <UID>[:<GID>] USER postgres # 其中用户名或ID是指可以在容器基础镜像中找到的用户。 # 如果在容器基础镜像中没有创建特定用户,则在USER指令之前添加useradd命令以添加特定用户。 RUN groupadd -r postgres && useradd --no-log-init -r -g postgres postgres # 要临时获取管理员权限可以使用gosu命令, gosu是个工具,用来提升指定账号的权限 -
使用 USER 指定用户时,可以使用用户名、UID 或 GID,或是两者的组合
-
在镜像构建阶段,Dockerfile中位于USER指令之后的RUN、CMD、ENTRYPOINT指令的执行将使用USER指定的用户名和用户组。
-
也可以使用 docker run -u 指定用户
docker run -i -t -u 1001 busybox sh -
审计方式:返回容器用户名或用户
ID。 如果为空,则表示容器以root身份运行[root@localhost ~]# docker ps |grep top |awk '{print $1}'|xargs -n1 docker inspect --format='<!--swig0-->:User=<!--swig1-->' 4e53c86daf89a1bac0ed178d043663d2af162ca813ff17864ebdb964d8233459:User=
# WORKDIR
-
该指令为后续的
RUN,CMD,ENTRYPOINT,COPY和ADD指令指定工作目录。 -
格式:
WORKDIR /path/to/workdir
-
WORKDIR指令可以解析它前面通过ENV设置的环境变量:
-
ENV DIRPATH /path WORKDIR $DIRPATH/$DIRNAME RUN pwd # 这里pwd输出为/path/$DIRNAME
# WORKDIR
-
为后续的RUN、CMD、ENTRYPOINT指令配置工作目录,如果指定的工作目录不存在,将自动创建一个新目录。
-
格式: 推荐WORKDIR指令中只使用绝对路径
WORKDIR <工作目录路径> WORKDIR /path/to/workdir -
一个Dockerfile中可以使用多个WORKDIR指令,根据需要不断切换工作目录。如果使用的路径是相对路径,则会相对于前一个工作目录进行计算。
WORKDIR /a WORKDIR b WORKDIR c RUN pwd # 则最终路径为 /a/b/c
# ARG
-
该指令用于定义一些构建参数,用户可以在执行
docker build命令时传入这些参数值。典型的,比如版本号。 -
注意,ARG用于定于构建参数,准确点说应该叫BUILD_ARG。ARG只会存在于构建时,不会存在于容器启动时。
-
**ARG 设置的环境变量仅对 Dockerfile 内有效,构建好的镜像内不存在此环境变量。**因此,ARG不能用于CMD和ENTRYPOINT命令中。因为那个时候ARG已经不存在了。一个解决方案是,把ARG赋值给环境变量ENV,ENV是会保留到运行时的。
-
格式:
ARG <name>[=<default value>] -
构建命令 docker build 中可以用
--build-arg <参数名>=<值>来覆盖。docker build --build-arg <varname>=<value> -
如果指定的参数不存在,则会参数如下警告信息:
[Warning] One or more build-args [foo] were not consumed. -
可以多次使用ARG指令,指定多个参数。
-
注意:不要通过ARG指令传递敏感信息,因为传递的构建参数值是可以通过
docker history命令看到的。
# ONBUILD
-
该指令指定一些其他指令,这些指令将在当前镜像作为别的镜像的基础镜像时,在其构建阶段执行。
-
格式:
ONBUILD [INSTRUCTION] -
用ONBUILD指定的指令,在本镜像的构建过程中不会执行,而是存储在镜像中。当创建另一个Dockerfile,并且用FROM指令指定本镜像作为基础镜像时,在子镜像的构建阶段,读取到FROM指令时,Docker会寻找基础镜像的ONBUILD指令,并按顺序执行其指定的指令。
-
注意ONBUILD指令只会影响直接继承的子镜像的构建过程,对于孙子镜像没有影响。
-
该指令常用于制作“执行环境”类型的镜像,且镜像名通常会添加“onbuild”后缀。
# STOPSIGNAL
-
指定所创建的镜像启动的容器所接受的退出信号值。
-
格式:
STOPSIGNAL signal # signal可以为无符号整数,如9,或信号名,如SIGKILL -
该信号可以是与内核 syscall 表中的位置匹配的有效无符号数字(例如9),也可以是 SIGNAME 格式的信号名称(例如 SIGKILL)。
# HEALTHCHECK
-
配置所启动容器如何进行健康检查(如何判断健康与否),自Docker 1.12开始支持。格式有两种:
HEALTHCHECK [options] CMD command # 根据所执行命令返回值是否为0来判断; HEALTHCHECK NODE 禁止基础镜像中的健康检查。 -
OPTION支持如下参数:
-interval=DURATION (default: 30s): 过多久检查一次;-timeout=DURATION (default: 30s): 每次检查等待结果的超时;-retries=N (default: 3): 如果失败了,重试几次才最终确定失败。--start-period=DURATION (default: 0s: 如果指定这个参数, 则必须大于 0s ;在这个时间段内如果检查失败, 则不会记录失败次数。 如果在启动时间内成功执行了健康检查, 则容器将被视为已经启动, 如果在启动时间内再次出现检查失败, 则会记录失败次数。
-
一个Dockerfile中只能指定一个HEALTHCHECK指令。指定多个时只有最后一个生效。
# SHELL
-
为后续的RUN、CMD、ENTRYPOINT的shell形式命令指定默认shell。
-
格式:
SHELL ["executable", "parameters"] # 默认值为["/bin/sh", "-c"]。
# 操作命令
作用:在镜像构建过程中执行具体操作(如复制文件、运行命令)。
| 指令 | 说明 |
|---|---|
| RUN | 运行指定命令 |
| CMD | 启动容器时指定默认执行的命令 |
| ADD | 将宿主机目录下的文件拷贝进镜像且ADD命令会自动处理URL和解压tar压缩包 |
| COPY | 类似ADD,拷贝文件和目录到镜像中(不会自解压)。 将从构建上下文目录中 <源路径> 的文件/目录复制到新的一层的镜像内的 <目标路径> 位置COPY ["src", "dest"] |
| ENTRYPOINT | 指定一个容器启动时要运行的命令,ENTRYPOINT 的目的和 CMD 一样,都是在指定容器启动程序及参数 |
# RUN
-
指定构建镜像时运行的指令。每条RUN指令都将在当前镜像基础上创建一个新层,执行指定命令,然后提交,从而产生一个新镜像。这个新镜像也将成为下一个RUN命令的的当前镜像。
-
当命令较长时可以使用
\来换行。 -
有两种格式:
RUN <command> # shell形式。将在默认的shell终端中运行命令。在linux下是/bin/sh -c。 # 命令太长时可用\换行 RUN ["executable", "param1", "param2"] # exec形式。使用exec执行,不会启动shell。 # 注意这里数组将被解析为JSON数组,因此必须用双引号。 # 例如 RUN ["/bin/bash", "-c","echo hello"]
# COPY
-
该指令将指定的文件、目录下内容拷贝到镜像中的指定目录下,目标路径不存在时,会自动创建。
-
COPY指令只能复制当前构建中的文件(即当前Dockerfile所在目录下的文件)。
-
格式:
# 注意 src只能访问该上下文目录下及其子目录。 # src: Dockerfile所在目录的一个相对路径 # dest : 镜像内的绝对路径 / 相对于工作目录(WORKDIR指令指定)的相对路径 COPY [--chown=<user>:<group>] <src>... <dest> COPY [--chown=<user>:<group>] ["<src>",... "<dest>"] -
示例
COPY check* /testdir/ # 拷贝所有 check 开头的文件 COPY check?.log /testdir/ # ? 是单个字符的占位符,比如匹配文件 check1.log # 拷贝到data文件夹 COPY data.tar ./data
# ADD
-
该指令将指定的文件、目录或URL指定的远程文件拷贝到镜像中的指定目录下。
-
格式:
ADD [--chown=<user>:<group>] <src>... <dest> ADD [--chown=<user>:<group>] ["<src>",... "<dest>"] # 当路径中有空格时,使用该形式 -
解压压缩文件并把它们添加到镜像中
WORKDIR /app ADD test.tar.gz . -
从 url 拷贝文件到镜像中
# docker 官方建议我们当需要从远程复制文件时,最好使用 curl 或 wget 命令来代替 ADD 命令。 # 原因是,当使用 ADD 命令时,会创建更多的镜像层,当然镜像的 size 也会更大(下面的两段代码来自 docker 官方文档): ADD http://example.com/big.tar.xz /usr/src/things/ RUN tar -xJf /usr/src/things/big.tar.xz -C /usr/src/things RUN make -C /usr/src/things all -
如果使用下面的命令,不仅镜像的层数减少,而且镜像中也不包含 big.tar.xz 文件:
RUN mkdir -p /usr/src/things \ && curl -SL http://example.com/big.tar.xz \ | tar -xJC /usr/src/things \ && make -C /usr/src/things all # 在解压压缩文件并把它们添加到镜像
# CMD
-
指定从生成的镜像启动容器时,默认执行的命令。
-
有三种形式:
CMD ["executable","param1","param2"] # exec形式。使用exec执行,不会启动shell。 # 是官方推荐的形式。 CMD command param1 param2 # shell形式。将在默认的shell终端中运行命令。在linux下是/bin/sh -c。 CMD ["param1","param2"] # 这种形式不是指定默认命令,而是为ENTRYPOINT指定的命令提供参数。 # 所有数组中的值都将作为ENTRYPOINT指定的命令的参数。 -
每个Dockerfile只能有一条CMD命令。如果指定了多条,则只有最后一条会被执行。
-
如果用户在启动容器时手动指定了运行的命令(作为run或create的参数),则会覆盖掉CMD。
# ENTRYPOINT
-
指定的镜像的默认入口命令, 该入口命令会在启动容器时作为根命令执行,所有传入值作为该命令的参数
-
每个Dockerfile中只能有一个ENTRYPOINT,当指定多个时,只有最后一个起效。
-
ENTRYPOINT 有两种格式, exec调用执行需要使用双引号
ENTRYPOINT ["executable", "param1", "param2"] (exec调用执行,首选) ENTRYPOINT command param1 param2 (shell中执行) -
exec 格式
docker run <image>后面跟的命令行参数将会添加到 ENTRYPOINT 所有参数的最后,且会覆盖掉所有 CMD 命令中的参数。 -
ENTRYPOINT 命令可以通过
docker run --entrypoint参数来覆盖 。[root@node1 docker]# cat Dockerfile FROM ubuntu ENTRYPOINT ["top", "-b"] CMD ["-c"] [root@node1 docker]# docker build -t top . [root@node1 docker]# docker run -it --rm --name test -d top -H [root@node1 docker]# docker exec -it test ps aux USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND root 1 0.2 0.0 5960 1708 pts/0 Ss+ 02:33 0:00 top -b -H root 7 0.0 0.0 5888 1480 pts/1 Rs+ 02:34 0:00 ps aux -
shell 格式会忽略所有 CMD 命令的参数和 docker run 的命令行参数,ENTRYPOINT 要运行的命令会作为 /bin/sh -c 的子命令运行,而且 /bin/sh 不会传递信号,也就是说 ENTRYPOINT 要运行的命令不是 PID 为 1 的进程,且不会收到 Unix 信号,所以你要执行的命令不会收到
docker stop <container>发出的 SIGTERM 信号。[root@node1 docker]# cat Dockerfile2 FROM ubuntu ENTRYPOINT top -b CMD -c [root@node1 docker]# docker build -t top2 -f Dockerfile2 . [root@node1 docker]# docker run -it --rm --name test2 -d top2 -H [root@node1 docker]# docker exec -it test2 ps aux USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND root 1 1.4 0.0 2600 648 pts/0 Ss+ 02:44 0:00 /bin/sh -c top root 6 0.0 0.0 5960 1708 pts/0 S+ 02:44 0:00 top -b root 7 0.0 0.0 5888 1476 pts/1 Rs+ 02:44 0:00 ps aux
# CMD/ENTRYPOINT组合
| 组合方式 | 实际执行的命令(容器启动时) | 说明 |
|---|---|---|
ENTRYPOINT + CMD |
ENTRYPOINT 的参数 + CMD 的参数 |
CMD 的内容作为 ENTRYPOINT 的默认参数,可被 docker run 覆盖。 |
只有 ENTRYPOINT |
ENTRYPOINT 的命令 |
docker run 的参数会全部传递给 ENTRYPOINT。 |
只有 CMD |
CMD 的命令 |
docker run 的参数会完全覆盖 CMD。 |
- Exec 格式(推荐)
# ENTRYPOINT 为 Exec 格式时,CMD 的参数会追加到 ENTRYPOINT 后。
ENTRYPOINT ["/app/start.sh"]
CMD ["--port=8080"]
docker run my-app # 执行:/app/start.sh --port=8080
docker run my-app --port=9090 # 执行:/app/start.sh --port=9090(覆盖 CMD)
- Shell 格式: ENTRYPOINT 为 Shell 格式时,CMD 会被忽略,docker run 的参数也无法覆盖。
ENTRYPOINT /app/start.sh
CMD ["--port=8080"]
docker run my-app # 执行:/bin/sh -c "/app/start.sh"(CMD 无效)
docker run my-app --port=9090 # 同上,参数被忽略
-
覆盖规则
# docker run 的参数会直接替换 CMD 的内容(需 ENTRYPOINT 存在)。 docker run my-app --port=9090 # 覆盖 CMD 参数 # 覆盖 ENTRYPOINT:使用 --entrypoint 标志: docker run --entrypoint /bin/bash my-app
# 构建镜像
-
当创建了一个Dockerfile之后,即可通过
docker build指令来创建镜像。docker build [OPTIONS] PATH | URL | - -
选项:https://docs.docker.com/engine/reference/commandline/build/
# 父镜像选择
-
用户可以选择两种镜像作为父镜像,一种是所谓的基础镜像(baseimage)
-
另外一种是普通的镜像(往往由第三方创建,基于基础镜像)
-
FROM scratch: 该镜像是一个空的镜像,可以用于构建busybox等超小镜像,可以说是真正的从零开始构建属于自己的镜像。 -
如何制作大小为0 的镜像
$ tar cv --files-from /dev/null | docker import - scratch $ docker image ls REPOSITORY TAG IMAGE ID CREATED SIZE scratch latest 775bfce21429 9 minutes ago 0B
# 命令选项
| Name, shorthand | Default | Description |
|---|---|---|
| –add-host | 添加自定义主机到IP的映射(host: ip) | |
| –build-arg | 设置构建时变量 | |
| –cache-from | 视为缓存源的镜像 | |
| –cgroup-parent | 容器可选的父安全组 | |
| –compress | 使用gzip压缩构建上下文 | |
| –cpu-period | 限制CPU CFS(完全公平调度程序) 期限 | |
| –cpu-quota | 限制CPU CFS(完全公平的调度程序)配额 | |
| –cpu-shares,-c | CPU份额(相对重量) | |
| –cpuset-cpus | 允许执行的CPU(0-3,0,1) | |
| –cpuset-mems | 允许执行的MEM(0-3,0,1) | |
| –disable-content-trust | 跳过镜像验证 | |
| –file,-f | Dockerfile的名称(默认为“ PATH / Dockerfile”) | |
| –force-rm | 始终取出中间容器 | |
| –iidfile | 将镜像ID写入文件 | |
| –isolation | 集装箱隔离技术 | |
| –label | 设置镜像的元数据 | |
| –memory,-m | 内存限制 | |
| –memory-swap | 交换限制等于内存加交换:"-1"以启用无限交换 | |
| –network | 在构建期间为RUN指令设置联网模块 API1.25+ | |
| –no-cache | 构建镜像时不要使用缓存 | |
| –output,-o | 输出目的地(格式:类型=本地,目的地=路径) API 1.40+ | |
| –platform | 如果服务器具有多平台功能,请设置平台 API 1.32+ 实验(守护程序) | |
| –progress | auto | 设置进度输出的类型(自动,普通,tty)。使用普通显示容器输出 |
| –pull | 始终尝试提取镜像的较新版本 | |
| –quiet,-q | 禁止生产输出并成功打印镜像ID | |
| –rm | true | 构建成功后删除中间容器 |
| –secret | 公开文件的秘密文件(仅在启用BuildKit的情况下): id = mysecret,src = / local / secret API 1.39+ | |
| –security-opt | 安全选项 | |
| –shm-size | /dev/shm的大小 | |
| –ssh | SSH代理套接字或用于公开构建的密钥(仅在启用BuildKit的情况下)(格式: default | |
| –stream | 流附加到服务器以协商构建上下文 API 1.31+ 实验性(守护程序) | |
| –tag,-t | 名称以及“ name: tag”格式的标签(可选) | |
| –target | 设置要构建的目标构建阶段。 | |
| –ulimit | Ulimit选项 |
常用选项:
-
--file , -f,指定dockerfile所在路径。
-
--tag , -t,指定生成镜像的标签信息。
-
docker build命令依靠上下文和dockerfile文件来构建镜像。上下文是PATH或URL参数指定的目录及其子目录。
-
Dockerfile默认也在上下文中找,但是可以通过-f参数指定上下文以外的其他文件。该命令将上下文和dockerfile发送给Docker服务端,由服务端来创建镜像。
# dockerignore文件
-
将不检查的目录,文件写到同Dockerfile目录下的
.dockerignore文件中, docker build命令将不再检查在.dockerignore文件中的目录和文件,在创建镜像时候不将无关数据发送到服务端。 -
.dockerignore 文件的写法和 .gitignore 类似,支持正则和通配符,具体规则如下:
- 每行为一个条目;
- 空行被忽略;
- 构建上下文路径为所有文件的根路径;
-
文件匹配规则具体语法如下:
# 注释 * 匹配0或多个非/的字符 ? 匹配1个非/的字符 ** 0个或多个目录 ! 除...外,需结合上下文语义# 除 README.md 外,所有其他 md 文件都被 docker 忽略 *.md !README.md
# DockerFile示例
# 自定义Nginx镜像
需求:构建包含vim且默认展示自定义页面的Nginx镜像
FROM nginx:alpine
# 安装vim调试工具
RUN apk update && apk add vim
# 替换默认首页
COPY custom-index.html /usr/share/nginx/html/
# 暴露80端口
EXPOSE 80
# 保持Nginx前台运行
CMD ["nginx", "-g", "daemon off;"]
构建命令:docker build -t custom-nginx:v1 .
# 自定义centos镜像
-
编写DockerFile文件
FROM centos:latest RUN rm -rf /etc/yum.repos.d/* && curl -o /etc/yum.repos.d/CentOS-Base.repo http://mirrors.aliyun.com/repo/Centos-8.repo && yum clean all && yum -y install epel-release vim wget lrzsz gcc gcc-c++ net-tools chrony passwd && yum -y update RUN ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime && echo 'Asia/Shanghai' >/etc/timezone RUN echo "set nu" >> ~/.vimrc && echo "set ts=4" >> ~/.vimrc && cat ~/.vimrc RUN echo 'alias ls="ls --color"' >> ~/.bashrc # 使用sleep便于调试 RUN echo -e "#! /bin/bash\nwhile true\ndo\nsleep 1\ndone" > /sleep.sh CMD ["sh","/sleep.sh"] # 开启sshd服务 RUN yum install openssh-server passwd -y RUN /usr/bin/echo "CuiLiang@0302" | /usr/bin/passwd --stdin root RUN /usr/bin/sed -i 's/.session.required.pam_loginuid.so./session optional pam_loginuid.so/g' /etc/pam.d/sshd && /bin/sed -i 's/UsePAM yes/UsePAM no/g' /etc/ssh/sshd_config && /bin/sed -i "s/#UsePrivilegeSeparation.*/UsePrivilegeSeparation no/g" /etc/ssh/sshd_config RUN ssh-keygen -t rsa -f /etc/ssh/ssh_host_rsa_key && ssh-keygen -t rsa -f /etc/ssh/ssh_host_ecdsa_key && ssh-keygen -t rsa -f /etc/ssh/ssh_host_ed25519_key EXPOSE 22 RUN echo -e "#! /bin/bash\n/usr/sbin/sshd -D" > /run.sh CMD ["/usr/sbin/sshd","-D"] # 源码安装python39 RUN yum install -y wget gcc make mysql-devel WORKDIR /tmp RUN wget https://www.python.org/ftp/python/3.9.0/Python-3.9.0.tgz RUN tar -zxf Python-3.9.0.tgz RUN /tmp/Python-3.9.0/configure --prefix=/usr/local/python3.9.0 --enable-optimizations --with-ssl RUN make && make install RUN echo "export PATH=$PATH:/usr/local/python3.9.0/bin" >> /etc/profile RUN source /etc/profile RUN mkdir /root/.pip/ RUN echo -e "[global]\nindex-url = https://pypi.tuna.tsinghua.edu.cn/simple\n[install]\ntrusted-host=mirrors.aliyun.com" > /root/.pip/pip.conf -
构建
docker build -t 新镜像名字:TAG .- docker build 命令最后的
.表示当前目录
- docker build 命令最后的
-
运行
docker run -it 新镜像名字:TAG- 可以看到,我们自己的新镜像已经支持vim/ifconfig命令,扩展成功了。
# Dockerfile优化
- 合并RUN指令:减少镜像层数,使用
&&连接命令 - 使用环境变量集中管理配置
- 清理yum缓存减少镜像大小
# 使用更小的基础镜像,CentOS 8已停止维护,建议使用CentOS Stream或Alpine
FROM centos:7
# 设置环境变量
ENV TZ=Asia/Shanghai \
LANG=en_US.UTF-8 \
PYTHON_VERSION=3.9.0 \
PYTHON_HOME=/usr/local/python3.9.0
# 一次性安装所有依赖并清理缓存
RUN rm -rf /etc/yum.repos.d/* && \
curl -o /etc/yum.repos.d/CentOS-Base.repo http://mirrors.aliyun.com/repo/Centos-7.repo && \
yum clean all && \
yum install -y epel-release vim wget lrzsz gcc gcc-c++ net-tools chrony passwd \
openssh-server mysql-devel make && \
yum -y update && \
yum clean all && \
rm -rf /var/cache/yum
# 配置时区和基本环境
RUN ln -sf /usr/share/zoneinfo/$TZ /etc/localtime && \
echo $TZ > /etc/timezone && \
echo "set nu\nset ts=4" > /root/.vimrc && \
echo 'alias ls="ls --color"' >> /root/.bashrc
# 配置SSH (注意:明文密码不安全,建议使用密钥认证)
RUN ssh-keygen -A && \
echo 'CuiLiang@0302' | passwd --stdin root && \
sed -i 's/^#\?PermitRootLogin.*/PermitRootLogin yes/' /etc/ssh/sshd_config && \
sed -i 's/^#\?UsePAM.*/UsePAM no/' /etc/ssh/sshd_config && \
sed -i 's/^#\?UsePrivilegeSeparation.*/UsePrivilegeSeparation no/' /etc/ssh/sshd_config
# 安装Python
WORKDIR /tmp
RUN wget -q https://www.python.org/ftp/python/${PYTHON_VERSION}/Python-${PYTHON_VERSION}.tgz && \
tar -zxf Python-${PYTHON_VERSION}.tgz && \
cd Python-${PYTHON_VERSION} && \
./configure --prefix=${PYTHON_HOME} --enable-optimizations --with-ssl && \
make -j$(nproc) && make install && \
echo "export PATH=\$PATH:${PYTHON_HOME}/bin" >> /etc/profile && \
mkdir -p /root/.pip/ && \
echo -e "[global]\nindex-url = https://pypi.tuna.tsinghua.edu.cn/simple\n[install]\ntrusted-host=mirrors.aliyun.com" > /root/.pip/pip.conf && \
rm -rf /tmp/Python-*
# 健康检查
HEALTHCHECK --interval=30s --timeout=10s \
CMD netstat -an | grep 22 > /dev/null || exit 1
# 暴露端口
EXPOSE 22
# 启动SSH服务
CMD ["/usr/sbin/sshd", "-D"]
# 多阶段构建
Docker 17.05版本以后,新增了Dockerfile多阶段构建。所谓多阶段构建,实际上是允许一个Dockerfile 中出现多个 FROM 指令。每个FROM开始一个新的构建阶段。虽然最终生成的镜像只包含最后一个阶段的内容,但多阶段构建提供了以下重要优势:
- 阶段性构建:可以将复杂的构建过程分解为多个逻辑阶段。
- 跨阶段文件复制:使用
COPY --from指令可以从之前的构建阶段复制文件到当前阶段。 - 环境分离:可以将编译环境和运行环境完全分离,大幅减小最终镜像体积。
# 多阶段构建的优势
- 减小镜像体积
- 只将必要的构建结果复制到最终阶段, 最终阶段可以使用极简的基础镜像(如scratch或alpine)
- 提高安全性
- 减少最终镜像中的软件包数量,缩小攻击面
- 避免将构建工具和中间文件暴露在生产环境中
- 优化构建缓存
- 修改后期阶段不会导致前期阶段重新构建
# 基本语法
# 第一阶段:构建阶段
FROM golang:1.19 AS builder
WORKDIR /app
COPY . .
RUN go build -o myapp
# 第二阶段:运行阶段
FROM alpine:latest
WORKDIR /root/
COPY --from=builder /app/myapp .
CMD ["./myapp"]
# 阶段命名与引用
可以为每个阶段命名(使用AS),然后在后续阶段通过名称引用:
FROM node:14 AS frontend-builder
...
FROM maven:3.6 AS backend-builder
...
FROM nginx:alpine
COPY --from=frontend-builder /app/dist /usr/share/nginx/html
COPY --from=backend-builder /app/target/*.jar /opt/app/
# 从外部镜像复制
COPY --from不仅可以从之前的构建阶段复制,还可以直接从其他镜像复制:
FROM alpine:latest
COPY --from=nginx:latest /etc/nginx/nginx.conf /etc/nginx/nginx.conf
# 调试与优化技巧
-
最小化镜像:基于
alpine或distroless镜像FROM golang:1.19-alpine AS builder WORKDIR /app COPY . . RUN CGO_ENABLED=0 go build -o /app/main . FROM gcr.io/distroless/static-debian11 COPY --from=builder /app/main /app/ CMD ["/app/main"] -
分层优化:将变动频率低的指令放在前面,利用缓存加速构建
# 1. 单独处理依赖文件 COPY package.json yarn.lock ./ RUN yarn install --production # 2. 再复制源代码(避免依赖变更导致缓存失效) COPY src/ ./src COPY config/ ./config -
分阶段构建:使用多阶段构建减小最终镜像体积
效果:最终镜像体积减少 80%(从 1.2GB → 200MB)
# 构建阶段 FROM maven:3.8.6-jdk-11 AS build COPY . /app RUN mvn clean package -DskipTests # 生产阶段 FROM openjdk:11-jre-slim COPY --from=build /app/target/*.jar /app.jar CMD ["java", "-jar", "/app.jar"] -
使用.dockerignore:排除无关文件(如node_modules、.git等)
-
镜像扫描:运行
docker scan <image>检查安全漏洞# 使用Trivy扫描镜像 trivy image your-image:tag # 输出示例: +---------+------------------+----------+-------------------+---------------+---------------------------------------+ | LIBRARY | VULNERABILITY ID | SEVERITY | INSTALLED VERSION | FIXED VERSION | TITLE | +---------+------------------+----------+-------------------+---------------+---------------------------------------+ | openssl | CVE-2022-2097 | HIGH | 1.1.1n-0+deb10u3 | 1.1.1n-0+deb10u4 | Incorrect AES-NI padding oracle... | +---------+------------------+----------+-------------------+---------------+---------------------------------------+ -
非root用户运行
FROM node:16-slim # 创建应用用户 RUN groupadd -r appuser && \ useradd -r -g appuser -d /app -s /sbin/nologin appuser WORKDIR /app COPY --chown=appuser:appuser . . USER appuser EXPOSE 3000 CMD ["node", "server.js"]