Dockerfile全解析:指令、构建、多阶段构建等

笔记哥 / 04-13 / 41点赞 / 0评论 / 418阅读
## Dockerfile官网 https://docs.docker.com/reference/dockerfile/ ## 什么是Dockerfile? Dockerfile 是一个文本文件,其内包含了一条条的指令(Instruction),每一条指令构建一层,因此每一条指令的内容,就是描述该层应当如何构建。 当我们从docker镜像仓库中下载的镜像不能满足我们的需求时,我们可以通过以下两种方式对镜像进行更改: 1. 从已经创建的容器中更新镜像,并且提交这个镜像 2. 使用 Dockerfile 指令来创建一个新的镜像 镜像的定制实际上就是定制每一层所添加的配置、文件。如果我们可以把每一层修改、安装、构建、操作的命令都写入一个脚本,用这个脚本来构建、定制镜像,那么之前提及的无法重复的问题、镜像构建透明性的问题、体积的问题就都会解决。这个脚本就是 Dockerfile。 **那Dockerfile有什么作用呢?** - 对于开发人员,可以为开发团队提供一个完全一致的开发环境 - 对于测试人员,可以直接拿开发时所构建的镜像测试。 - 对于运维人员,在部署时,可以实现快速部署、移值。 ## Dockerfile指令 ### FROM FROM指令初始化一个新的构建阶段,并为后续指令设置 基础镜像。因此,有效的 Dockerfile 必须以FROM指令开头。镜像可以是任何有效的镜像。FROM指定一个基本镜像,类似`docker pull`下载镜像。 FROM可以在单个 Dockerfile 中多次出现,以创建多个镜像,或将一个构建阶段用作另一个构建阶段的依赖项。 示例: ```csharp FROM [--platform=] [:] [AS ] FROM java:8 ``` ### LABEL 指定镜像的元数据,例如作者、联系方式、邮箱等信息 语法: ```csharp LABEL = [=...] LABEL author=huangSir ``` ### RUN RUN指令制作镜像过程中需要执行的命令,通常系统配置,服务配置,下载软件,部署等等,不能出现阻塞当前终端的命令. RUN它有两种形式: ```csharp #shell脚本的形式: RUN [OPTIONS] ... #Exec的形式: RUN [OPTIONS] [ "", ... ] ``` 通常RUN指令种,shell脚本是最常用的: ```csharp #第一种heredocs形式 RUN <=` 标志的命令将该变量传递给构建器。 Dockerfile 可以包含一条或多ARG条指令。例如,以下是一个有效的 Dockerfile: ```csharp FROM busybox ARG user1 ARG buildno ``` 也可以给ARG设置默认值: ```csharp FROM busybox ARG user1=someuser ARG buildno=1 ``` ### USER USER 指令用于指定后续指令执行时所使用的用户。这在需要以非 root 用户运行容器时非常有用,可以提高容器的安全性,避免以 root 用户运行可能导致的安全风险。 示例: ```csharp FROM busybox RUN useradd www-www USER www-www RUN apt install curl ``` ### HEALTHCHECK HEALTHCHECK指令告诉 Docker 如何测试容器是否仍在运行。这可以检测出诸如 Web 服务器陷入无限循环、无法处理新连接等情况,即使服务器进程仍在运行。 当容器指定了健康检查后,除了正常状态外,它还会拥有一个健康状态。此状态最初为starting。每当健康检查通过时,它都会变为healthy(无论它之前的状态如何)。在连续失败一定次数后,它将变为unhealthy。 示例: ```csharp 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相关命令说明 ### 语法: ```csharp docker build [选项] <上下文路径> ``` 常用选项说明: - -t:指定镜像的名称和标签(tag)。格式为 `:`,其中 `` 是可选的 - -f:指定 Dockerfile 的路径。默认情况下,Docker 会在当前目录下查找名为 Dockerfile 的文件。 - --build-arg:设置构建时的变量,用于传递给 ARG 指令。 - --no-cache:禁用缓存,强制重新构建。 - --rm:构建完成后删除中间容器,默认值为 true - --debug:debug测试Dockerfile ### 示例: 假设你的 Dockerfile 位于当前目录下,你可以使用以下命令构建镜像: ```csharp docker build -t my-image . ``` 如果 Dockerfile 不在当前目录下,可以使用 -f 指定路径: ```csharp 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 ```csharp FROM alpine ARG MY_VAR=default_value RUN echo "The value is $MY_VAR" ``` 构建命令 ```csharp docker build --build-arg MY_VAR=custom_value -t my-image . ``` ### 总结 一般来说,Dockerfile文件和文件中指定的其它文件,都会指定在同一个目录下,所以我们只需要记住下面这个命令即可 ```csharp #最后有个点,代表当前目录下的所有文件内容 docker build -t 镜像名:版本号 . ``` ## 使用tomcat构建Java项目 这里使用zrlog的war包来代表生产环境中的实际项目,在这里感谢zrlog的提供方:https://gitee.com/94fzb/zrlog 下载war包: ```csharp [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: ```csharp [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 ``` 构建镜像并查看 ```csharp #构建镜像 [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 ``` 运行容器 ```csharp [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/ ![image](https://cdn.res.knowhub.vip/c/2504/13/c9eb6bed.png?G1cAAOTcVkxwvxy2iOF0Q2CDZsAijaBSwnq956x9A3x%2fMLLmZ7Q%2bY3%2f4TeszQPRy9wKMbKhIgYSMqwkLJqpFxVyq5jUC) ## Dockerfile书写要求 合理编写Dockerfile可以减少构建出来的镜像的大小,避免构建Docker镜像时产生过多的层级。 Dockerfile生产环境书写要求: 1、尽量保证每个镜像功能单一,尽量避免多个服务运行在同一个镜像中 2、选择合适的基础镜像,不一定都要从头做 3、注释与说明,添加一定的注释和镜像属性信息 4、指定版本号 5、减少镜像的层数/步骤,尽可能合并RUN,ADD,COPY 6、记得减少镜像的大小,清理垃圾,记得清理缓存,临时文件,压缩包等等... 7、合理使用.dockeringnore,Dockerfile同一个目录,隐藏文件,构建的忽略的文件 ## .dockerignore使用 `.dockerignore`是一个隐藏文件,在Dockerfile文件同级目录中 示例: ```csharp [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 在构建镜像时,应该忽略哪些文件和目录,从而避免将不必要的文件复制到镜像中,减少镜像大小并提高构建效率。 示例: ```csharp # 忽略所有以 . 开头的文件 .* # 但不忽略 .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 关键字),并在后续阶段中引用这些名称。 示例: ```csharp # 第一阶段:构建阶段 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到第二阶段中进行运行