Dockerfile官网
https://docs.docker.com/reference/dockerfile/
什么是Dockerfile?
Dockerfile 是一个文本文件,其内包含了一条条的指令(Instruction),每一条指令构建一层,因此每一条指令的内容,就是描述该层应当如何构建。
当我们从docker镜像仓库中下载的镜像不能满足我们的需求时,我们可以通过以下两种方式对镜像进行更改:
- 从已经创建的容器中更新镜像,并且提交这个镜像
- 使用 Dockerfile 指令来创建一个新的镜像
镜像的定制实际上就是定制每一层所添加的配置、文件。如果我们可以把每一层修改、安装、构建、操作的命令都写入一个脚本,用这个脚本来构建、定制镜像,那么之前提及的无法重复的问题、镜像构建透明性的问题、体积的问题就都会解决。这个脚本就是 Dockerfile。
那Dockerfile有什么作用呢?
- 对于开发人员,可以为开发团队提供一个完全一致的开发环境
- 对于测试人员,可以直接拿开发时所构建的镜像测试。
- 对于运维人员,在部署时,可以实现快速部署、移值。
Dockerfile指令
FROM
FROM指令初始化一个新的构建阶段,并为后续指令设置 基础镜像。因此,有效的 Dockerfile 必须以FROM指令开头。镜像可以是任何有效的镜像。FROM指定一个基本镜像,类似docker pull
下载镜像。
FROM可以在单个 Dockerfile 中多次出现,以创建多个镜像,或将一个构建阶段用作另一个构建阶段的依赖项。
示例:
FROM [--platform=<platform>] <image>[:<tag>] [AS <name>]
FROM java:8
LABEL
指定镜像的元数据,例如作者、联系方式、邮箱等信息
语法:
LABEL <key>=<value> [<key>=<value>...]
LABEL author=huangSir
RUN
RUN指令制作镜像过程中需要执行的命令,通常系统配置,服务配置,下载软件,部署等等,不能出现阻塞当前终端的命令.
RUN它有两种形式:
#shell脚本的形式:
RUN [OPTIONS] <command> ...
#Exec的形式:
RUN [OPTIONS] [ "<command>", ... ]
通常RUN指令种,shell脚本是最常用的:
#第一种heredocs形式
RUN <<EOF
apt-get update
apt-get install -y curl
EOF
#第二种换行符的形式
RUN apt-get update \
&& apt-get install -y curl \
&& curl www.baidu.com
ADD
可以把指定文件、目录、以及远程 HTTPS 和 Git URL 获取文件的功能添加至镜像的指定目录中,如果是压缩包会自动进行解压
示例:
#将本地文件添加至镜像中
ADD nginx.tar /app/tools/
ADD file1.txt file2.txt /usr/src/things/
#要从远程位置添加文件,您可以指定 URL 或 Git 仓库的地址作为源。例如:
ADD https://example.com/archive.zip /usr/src/things/
ADD [email protected]:user/repo.git /usr/src/things/
COPY
COPY功能类似ADD,但是不会解压压缩包,同时可以进行多阶段构建镜像,以助于减小镜像的大小
#将本地文件添加至镜像中
COPY nginx.tar /app/tools/
COPY file1.txt file2.txt /usr/src/things/
#要从远程位置添加文件,您可以指定 URL 或 Git 仓库的地址作为源。例如:
COPY https://example.com/archive.zip /usr/src/things/
COPY [email protected]:user/repo.git /usr/src/things/
WORKDIR
指定容器的默认工作目录,也就是设置进入容器时默认所在的路径,例如:
WORKDIR /path/to/workdir
RUN pwd
上面会输出/path/to/workdir
,也相当于你进入容器时所在的目录为/path/to/workdir
,并且当目录不存在时会自动创建它。
WORKDIR指令可以在 Dockerfile 中多次使用。如果提供了相对路径,则该路径将相对于前一条 WORKDIR指令的路径,例如:
WORKDIR /a
WORKDIR b
WORKDIR c
RUN pwd
此时会输出/a/b/c
VOLUME
设置需要挂载的数据卷,创建镜像之后运行容器可以使用-v
选项指定。
VOLUME的值可以是 JSON 数组,VOLUME ["/var/log/"]
,也可以是包含多个参数的纯字符串,例如VOLUME /var/log
或VOLUME /var/log /var/db
示例:
VOLUME ["/var/log/","/data/mysql"]
VOLUME /var/log
VOLUME /var/log /var/db
EXPOSE
EXPOSE指令告知 Docker 容器在运行时监听指定的网络端口。您可以指定端口监听 TCP 还是 UDP,如果不指定协议,则默认为 TCP。
示例:
EXPOSE 80/tcp
EXPOSE 80/udp
CMD
CMD指令设置从镜像运行容器时要执行的默认的命令,可以在docker run的时候进行替换:
示例:
#例如Dockerfile如下:
FROM alpine
CMD ["echo", "Hello, Docker!"]
#运行容器时,可以通过以下方式覆盖默认的 CMD:
docker run my-image echo "Custom Message"
CMD可以使用 shell 或 exec 形式指定指令:
#exec形式
CMD ["executable","param1","param2"]
CMD ["param1","param2"]
#shell形式
CMD command param1 param2
Dockerfile 中只能有一条CMD 指令。如果列出多条CMD指令,则只有最后一条指令生效。
ENTRYPOINT
ENTRYPOINT也用于指定容器的入口命令,无法被docker run替换,docker run指定的时候仅仅作为后续追加的命令,可以和CMD指令进行搭配使用。
ENTRYPOINT可以使用shell或exec形式指定指令
#exec形式
ENTRYPOINT ["executable","param1","param2"]
ENTRYPOINT ["param1","param2"]
#shell形式
ENTRYPOINT command param1 param2
当 Dockerfile 中有多条ENTRYPOINT指令时,只有最后一条ENTRYPOINT指令才会有效。
ENV
创建容器/镜像全局变量,后续Dockerfile中使用该变量时可以${变量名}使用
示例:
ENV FILE_PATH=/data/mysql
RUN mkdir -p ${FILE_PATH}
#会输出/data/mysql
RUN echo ${FILE_PATH}
ARG
ARG指令定义了一个变量,用户可以在构建时docker build
使用--build-arg <varname>=<value>
标志的命令将该变量传递给构建器。
Dockerfile 可以包含一条或多ARG条指令。例如,以下是一个有效的 Dockerfile:
FROM busybox
ARG user1
ARG buildno
也可以给ARG设置默认值:
FROM busybox
ARG user1=someuser
ARG buildno=1
USER
USER 指令用于指定后续指令执行时所使用的用户。这在需要以非 root 用户运行容器时非常有用,可以提高容器的安全性,避免以 root 用户运行可能导致的安全风险。
示例:
FROM busybox
RUN useradd www-www
USER www-www
RUN apt install curl
HEALTHCHECK
HEALTHCHECK指令告诉 Docker 如何测试容器是否仍在运行。这可以检测出诸如 Web 服务器陷入无限循环、无法处理新连接等情况,即使服务器进程仍在运行。
当容器指定了健康检查后,除了正常状态外,它还会拥有一个健康状态。此状态最初为starting。每当健康检查通过时,它都会变为healthy(无论它之前的状态如何)。在连续失败一定次数后,它将变为unhealthy。
示例:
HEALTHCHECK --interval=30s --timeout=3s --retries=3 \
CMD curl http://127.0.0.1:8080
#--interval 间隔多少秒执行CMD的内容
#--timeout 指定超时时间是多少
#--retries 允许重试几次
#CMD 执行的命令
总结
上述指令在生产过程中均已包含,当然还有几个指令没有说明,例如SHELL(可以使用RUN指令替换)、MAINTAINER(该指令已弃用,可以使用LABEL标签替换)ONBUILD、STOPSIGNAL这四个指令,感兴趣的同学可以自行查阅相关资料
Dockerfile相关命令说明
语法:
docker build [选项] <上下文路径>
常用选项说明:
- -t:指定镜像的名称和标签(tag)。格式为
<name>:<tag>
,其中<tag>
是可选的 - -f:指定 Dockerfile 的路径。默认情况下,Docker 会在当前目录下查找名为 Dockerfile 的文件。
- --build-arg:设置构建时的变量,用于传递给 ARG 指令。
- --no-cache:禁用缓存,强制重新构建。
- --rm:构建完成后删除中间容器,默认值为 true
- --debug:debug测试Dockerfile
示例:
假设你的 Dockerfile 位于当前目录下,你可以使用以下命令构建镜像:
docker build -t my-image .
如果 Dockerfile 不在当前目录下,可以使用 -f 指定路径:
docker build -f /path/to/Dockerfile -t my-image /path/to/context
-f /path/to/Dockerfile:指定 Dockerfile 的路径。
/path/to/context:指定构建上下文的路径。
如果 Dockerfile 中使用了 ARG 指令,可以通过 --build-arg 参数在构建时传递值。
示例 Dockerfile
FROM alpine
ARG MY_VAR=default_value
RUN echo "The value is $MY_VAR"
构建命令
docker build --build-arg MY_VAR=custom_value -t my-image .
总结
一般来说,Dockerfile文件和文件中指定的其它文件,都会指定在同一个目录下,所以我们只需要记住下面这个命令即可
#最后有个点,代表当前目录下的所有文件内容
docker build -t 镜像名:版本号 .
使用tomcat构建Java项目
这里使用zrlog的war包来代表生产环境中的实际项目,在这里感谢zrlog的提供方:https://gitee.com/94fzb/zrlog
下载war包:
[root@master /data/docker/zrlog]# wget wget https://dl.zrlog.com/release/zrlog.war
--2025-04-13 14:59:30-- http://wget/
Resolving wget (wget)... failed: Temporary failure in name resolution.
wget: unable to resolve host address 'wget'
--2025-04-13 14:59:30-- https://dl.zrlog.com/release/zrlog.war
Resolving dl.zrlog.com (dl.zrlog.com)... 154.17.16.140
Connecting to dl.zrlog.com (dl.zrlog.com)|154.17.16.140|:443... connected.
HTTP request sent, awaiting response... 200 OK
Length: 10794045 (10M) [application/java-archive]
Saving to: 'zrlog.war'
zrlog.war 100%[==================================================================================================================>] 10.29M 4.63MB/s in 2.2s
2025-04-13 14:59:33 (4.63 MB/s) - 'zrlog.war' saved [10794045/10794045]
FINISHED --2025-04-13 14:59:33--
Total wall clock time: 3.3s
Downloaded: 1 files, 10M in 2.2s (4.63 MB/s)
[root@master /data/docker/zrlog]# ll
total 10552
drwxr-xr-x 2 root root 4096 Apr 13 14:59 ./
drwxr-xr-x 5 root root 4096 Apr 13 14:59 ../
-rw-r--r-- 1 root root 10794045 Jul 10 2024 zrlog.war
编写Dockerfile:
[root@master /data/docker/zrlog]# cat Dockerfile
FROM tomcat:9.0.87-jdk8-corretto
LABEL author=huangSir
LABEL version=1.0
ENV CODE_FILE="zrlog.war"
ENV SRC_PATH="/usr/local/tomcat/webapps/ROOT.war"
ENV WORK_DIR="/usr/local/tomcat"
COPY ${CODE_FILE} ${SRC_PATH}
WORKDIR ${WORK_DIR}
EXPOSE 8080
CMD ["catalina.sh","run"]
HEALTHCHECK --interval=30s --timeout=3s --retries=3 \
CMD curl http://127.0.0.1:8080
构建镜像并查看
#构建镜像
[root@master /data/docker/zrlog]# docker build -t zrlog_tomcat:8.0 .
[+] Building 0.2s (8/8) FINISHED docker:default
...
=> => naming to docker.io/library/zrlog_tomcat:8.0
#查看构建的镜像
[root@master /data/docker/zrlog]# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
zrlog_tomcat 8.0 be4206f2e7bf About a minute ago 467MB
运行容器
[root@master /data/docker/zrlog]# docker run -d -p 8080:8080 --name zrlog --restart always zrlog_tomcat:8.0
8cb866a61b46bb04bcf1a04a064388d6a8c985d84ea9a3a11478086ad89cf28c
[root@master /data/docker/zrlog]# docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
8cb866a61b46 zrlog_tomcat:8.0 "catalina.sh run" 4 seconds ago Up 4 seconds 0.0.0.0:8080->8080/tcp, [::]:8080->8080/tcp, 8443/tcp zrlog
测试访问:
http://10.0.0.10:8080/
Dockerfile书写要求
合理编写Dockerfile可以减少构建出来的镜像的大小,避免构建Docker镜像时产生过多的层级。
Dockerfile生产环境书写要求:
1、尽量保证每个镜像功能单一,尽量避免多个服务运行在同一个镜像中
2、选择合适的基础镜像,不一定都要从头做
3、注释与说明,添加一定的注释和镜像属性信息
4、指定版本号
5、减少镜像的层数/步骤,尽可能合并RUN,ADD,COPY
6、记得减少镜像的大小,清理垃圾,记得清理缓存,临时文件,压缩包等等...
7、合理使用.dockeringnore,Dockerfile同一个目录,隐藏文件,构建的忽略的文件
.dockerignore使用
.dockerignore
是一个隐藏文件,在Dockerfile文件同级目录中
示例:
[root@master /data/docker/zrlog]# ll
total 10556
drwxr-xr-x 2 root root 4096 Apr 13 15:33 ./
drwxr-xr-x 5 root root 4096 Apr 13 14:59 ../
-rw-r--r-- 1 root root 0 Apr 13 15:33 .dockerignore
-rw-r--r-- 1 root root 370 Apr 13 15:25 Dockerfile
-rw-r--r-- 1 root root 10794045 Jul 10 2024 zrlog.war
.dockerignore
文件是 Docker 构建过程中使用的一个配置文件,类似于 Git 中的 .gitignore
文件。它的作用是告诉 Docker 在构建镜像时,应该忽略哪些文件和目录,从而避免将不必要的文件复制到镜像中,减少镜像大小并提高构建效率。
示例:
# 忽略所有以 . 开头的文件
.*
# 但不忽略 .dockerignore 文件本身
!.dockerignore
# 忽略临时文件
*.tmp
*.swp
# 忽略日志文件
*.log
# 忽略测试目录
tests/
# 忽略数据目录下的敏感文件
data/sensitive_data.txt
# 忽略虚拟环境
venv/
# 忽略构建文件
build/
dist/
# 忽略特定文件
requirements.txt
# 准许Dockerfile文件
!Dockerfile
# 准许entrypoint.sh脚本
!entrypoint.sh
说明:
*:表示排除所有
!:准许指定的文件传输到docker镜像中
Dockerfile多阶段构建
Docker 的多阶段构建(Multi-Stage Builds)是一种强大的功能,允许你在同一个 Dockerfile 中定义多个构建阶段,每个阶段可以基于不同的基础镜像。最终,你可以从这些阶段中选择需要的文件和目录,构建出一个更小、更安全的最终镜像。多阶段构建特别适用于需要编译源代码或安装大量依赖的应用程序。
多阶段构建的优势
- 减小镜像大小:只将必要的文件和依赖复制到最终镜像中,避免包含编译工具和中间文件。
- 提高安全性:避免将编译工具和源代码暴露在最终镜像中。
- 简化构建过程:在一个 Dockerfile 中完成所有构建步骤,无需多个 Dockerfile 或复杂的构建脚本。
多阶段构建的基本结构
多阶段构建的 Dockerfile 通常包含多个 FROM 指令,每个 FROM 指令定义一个新的阶段。你可以为每个阶段指定一个名称(通过 AS 关键字),并在后续阶段中引用这些名称。
示例:
# 第一阶段:构建阶段
FROM maven:3.8.1-jdk-11 AS builder
WORKDIR /app
COPY pom.xml .
RUN mvn dependency:go-offline
COPY src ./src
RUN mvn package
# 第二阶段:运行阶段
FROM openjdk:11-jre-slim
COPY --from=builder /app/target/my-java-app.jar /app.jar
CMD ["java", "-jar", "/app.jar"]
说明:
上述第一阶段构建时,使用maven打包成一个jar包,然后在第二阶段构建时,会将第一阶段打包的jar包copy到第二阶段中进行运行