零基础学Docker(6)--DockerFile
Dockerfile 是构建 Docker 镜像的核心文本文件,它通过一系列预定义的指令(Instruction),以代码化、可版本控制的方式描述镜像的构建流程。简单来说,Dockerfile 就是镜像的 “源代码”,确保了镜像构建过程的一致性、可重复性和可追溯性,是 Docker 生态中实现 “一次构建,到处运行” 的关键基础。
以上是一个用于构建支持 systemd 的 CentOS 基础镜像的 Dockerfile
1. DockerFile命令
1.1 BUILD(构建阶段生效)
这些指令仅在 docker build 构建镜像时发挥作用,影响镜像的最终内容。
- FROM:指定构建镜像的基础镜像,是 Dockerfile 的首条非注释指令,后续指令基于此基础镜像操作。
- MAINTAINER:指定镜像的维护者信息(该指令已被 LABEL 部分替代,更推荐用 LABEL maintainer="xxx" 定义维护者)。
- LABEL:为镜像添加元数据的指令,可记录作者、版本、描述等信息,方便镜像的描述、分类和管理。
- COPY:将宿主机的文件或目录复制到镜像内指定路径,仅做简单复制,不处理压缩包等特殊文件。
- ADD:功能类似 COPY,但支持自动解压本地压缩包(如 .tar 等格式),还能从 URL 下载文件到镜像。
- RUN:在镜像构建过程中执行命令,比如安装软件、配置环境等,每一条 RUN 指令会生成一个新的镜像层。
- ONBUILD:定义的指令不会在当前镜像构建时执行,而是在以当前镜像为基础镜像构建子镜像时触发执行,用于传递构建逻辑。
- .dockerignore:并非 Dockerfile 内的指令,而是与 Dockerfile 同目录下的配置文件,作用是指定在构建镜像时,哪些文件或目录应被排除在构建上下文之外,减少不必要的文件传输,加快构建速度。
1.2 Both(构建和运行阶段均生效)
指令在镜像构建和容器运行时都有作用。
- WORKDIR:设置后续指令(如 RUN、COPY、CMD 等)在镜像内的工作目录,若目录不存在会自动创建。
- USER:指定后续指令(构建阶段的 RUN 以及运行阶段的 CMD、ENTRYPOINT 等)执行时使用的用户,增强容器安全性,避免以 root 用户运行不必要的进程。
1.3 RUN(运行阶段生效)
指令主要在容器运行时发挥作用。
- CMD:指定容器启动时执行的默认命令,若 docker run 命令后指定了其他命令,会覆盖 CMD 的默认值,且一个 Dockerfile 中通常只有最后一条 CMD 指令生效。
- ENV:设置环境变量,这些变量在镜像构建阶段(如 RUN 指令中)和容器运行阶段都能被使用,也可在 docker run 时通过 -e 参数覆盖。
- EXPOSE:声明容器运行时监听的端口,只是一种文档说明,不会实际将端口映射到宿主机,需在 docker run 时用 -p 或 -P 参数实现端口映射。
- VOLUME:声明容器内的匿名卷,容器运行时该目录会自动挂载到宿主机的临时目录(可通过 docker inspect 查看具体挂载路径),用于持久化数据,避免容器删除后数据丢失。
- ENTRYPOINT:指定容器启动时的 “入口程序”,其优先级高于 CMD,docker run 后的参数会作为 ENTRYPOINT 指令指定程序的参数,而不是覆盖它,若要覆盖 ENTRYPOINT,需使用 --entrypoint 参数。
这里提供一个表格方便大家后续观看:
类型(生效阶段) | 指令 | 作用 | 格式举例 |
---|---|---|---|
BUILD(构建阶段) | FROM | 指定构建镜像的基础镜像,是 Dockerfile 首条非注释指令,后续指令基于此操作 | FROM <image>[:<tag>] [AS <name>] |
LABEL | 为镜像添加元数据,记录作者、版本、描述等信息,方便镜像管理 | LABEL maintainer="xxx" version="1.0" description="xxx" | |
RUN | 镜像构建过程中执行命令,如安装软件、配置环境,每条生成新镜像层 | RUN ["executable", "param1", "param2"] 或 RUN command | |
COPY | 将宿主机文件 / 目录复制到镜像内指定路径,仅做简单复制,不处理压缩包等特殊文件 | COPY [--chown=<user>:<group>] <src>... <dest> | |
ADD | 将宿主机文件 / 目录复制到镜像,支持自动解压本地压缩包、从 URL 下载文件 | ADD [--chown=<user>:<group>] <src>... <dest> | |
ONBUILD | 定义的指令不在当前镜像构建时执行,在以当前镜像为基础构建子镜像时触发 | ONBUILD RUN command | |
.dockerignore | (非 Dockerfile 内指令,同目录配置文件)指定构建时排除的文件 / 目录,减少上下文传输 | (文本文件,写入需排除的路径,如 node_modules ) | |
Both(构建 + 运行阶段) | WORKDIR | 设置后续指令(如 RUN、COPY、CMD 等)在镜像内的工作目录,不存在则自动创建 | WORKDIR /path/to/workdir |
USER | 指定后续指令(构建阶段 RUN、运行阶段 CMD/ENTRYPOINT 等)执行的用户,增强安全性 | USER <user>[:<group>] | |
RUN(运行阶段) | CMD | 指定容器启动时执行的默认命令,docker run 后指定命令会覆盖,通常最后一条生效 | CMD ["executable","param1","param2"] 或 CMD command |
ENTRYPOINT | 指定容器启动时的 “入口程序”,优先级高于 CMD,docker run 后参数为其附加参数 | ENTRYPOINT ["executable", "param1", "param2"] | |
ENV | 设置环境变量,镜像构建和容器运行阶段均可用,docker run 时可通过 -e 覆盖 | ENV <key> <value> | |
EXPOSE | 声明容器运行时监听的端口,仅为文档说明,需用 -p /-P 实现端口映射 | EXPOSE <port> [<port>/<protocol>...] | |
VOLUME | 声明容器内匿名卷,容器运行时自动挂载到宿主机临时目录,用于持久化数据 | VOLUME ["/data"] |
2. 创建一个ubuntu镜像
官方提供的ubuntu镜像是缺少了大部分命令的,如vim命令,ifconfig命令,我们尝试编写dockerFile来创建一个自己的ubuntu镜像
编写dockerfile文件
FROM ubuntu #直接基于ubuntu镜像创建
LABEL maintainer="ty<2275980533@qq.com>" #设置维护者信息ENV MYPATH /usr/local #环境变量
WORKDIR $MYPATH #设置工作目录为环境变量MYPATH的值RUN apt update #更新apt命令
RUN apt install -y vim #下载vim命令
RUN apt install -y net-tools #下载net-toolsEXPOSE 80 #声明监听端口为80CMD echo $MYPATH #输出MYPATH
CMD echo "---end---" #输出end提示构建完成
CMD /bin/bash #启动后进入bash命令行
运行命令:
docker build -f ubuntuDockerFile -t myubuntu:v1 .
- docker build:Docker 构建镜像的核心命令,用于读取 Dockerfile 中的指令并生成镜像。
- -f ubuntuDockerFile:-f(--file 的缩写)指定 Dockerfile 的路径和文件名。默认情况下,Docker 会寻找当前目录下名为 Dockerfile 的文件,这里明确指定使用 ubuntuDockerFile 作为构建脚本(需确保该文件在当前目录下)。
- -t myubuntu:v1:-t(--tag 的缩写)为镜像添加标签,格式为 名称:版本。这里将镜像命名为 myubuntu,版本为 v1,方便后续通过标签识别和使用(如 docker run myubuntu:v1)。
- .:表示构建上下文的根目录。Docker 会将该目录下的所有文件(除 .dockerignore 排除的内容)发送给 Docker 引擎,供 Dockerfile 中的 COPY、ADD 等指令使用(即使本次 Dockerfile 没用到这些指令,也需要指定上下文目录)。
可以看到我们的命令是至上而下执行的,使用images命令可以看到我们创建的镜像
启动后可以看到默认就在我们预设的工作目录下
ifconfig也可以使用
3. docker history
docker history 是 Docker 命令行工具中的一个指令,用于查看指定镜像的构建历史,展示镜像的每一层是如何创建的。通过该命令,你可以了解到镜像中包含了哪些操作,以及每个操作对镜像大小的影响等信息。
这里查看我们刚才创建的ubuntu镜像:
可以清晰的知道镜像中安装了哪些软件包、进行了哪些配置操作,方便排查镜像构建过程中的问题。
4.CMD 和ENTRYPOINT的区别
4.1 CMD
在前面我们编写ubuntu的dockerfile时,我们在其中写了三条CMD语句,但是我们docker run时并没有看到输出我们的$MYPATH和---end---,这是因为CMD只会生效最后一条,也就是我们的CMD /bin/bash
前面我还说到过,CMD命令会被覆盖,下面我们新写一个dockerfile来进行解释:
FROM ubuntuCMD ["ls", "-a"]
创建完后启动容器:
可以看到输出了目录,说明执行了ls -a命令
我们现在想在ls -a命令后面追加一个 -l参数:
可以看到报错了,提示 -l不是一个命令,这是因为CMD是会直接被 -l替换,也就是 ls -a 被整个替换成了 -l,所以使用CMD要追加新的参数,要写完整的命令:
4.2 ENTRYPOINT
使用ENTRYPOINT时,是直接在后面追加
FROM ubuntuENTRYPOINT ["ls", "-a"]
5. 制作Tomcat镜像
接下来我们制作一个Tomcat镜像来练习
下载jdk和tomcat的压缩包:
#直接基于ubuntu镜像创建
FROM ubuntu
#设置维护者信息
LABEL maintainer="ty<2275980533@qq.com>"#将JDK压缩包解压到/usr/local/目录
ADD jdk-17.0.12_linux-x64_bin.tar.gz /usr/local/
#将Tomcat压缩包解压到/usr/local/目录
ADD apache-tomcat-10.1.46.tar.gz /usr/local/#更新apt包索引
RUN apt update
#下载并安装vim编辑器
RUN apt install -y vim#环境变量,定义基础路径
ENV MYPATH /usr/local
#设置工作目录为环境变量MYPATH的值
WORKDIR $MYPATH#定义JDK安装路径的环境变量
ENV JAVA_HOME /usr/local/jdk-17.0.12
#设置Java类路径,jdk8 及以下版本需要配置该环境变量
ENV CLASSPATH $JAVA_HOME/lib/dt.jar:$JAVA_HOME/lib/tools.jar
#定义Tomcat安装路径的环境变量
ENV CATALINA_HOME /usr/local/apache-tomcat-10.1.46
#定义Tomcat基础路径(单实例部署与CATALINA_HOME一致)
ENV CATALINA_BASE /usr/local/apache-tomcat-10.1.46
#将Java和Tomcat的可执行命令路径添加到系统PATH
ENV PATH $PATH:$JAVA_HOME/bin:$CATALINA_HOME/bin#声明容器监听的端口为8080
EXPOSE 8080#启动Tomcat并持续输出日志(保持容器前台运行)
CMD /usr/local/apache-tomcat-10.1.46/bin/startup.sh && tail -F /usr/local/apache-tomcat-10.1.46/logs/catalina.out
这里我们使用的Dockerfile为文件名,就不用使用-f指定文件名了
启动容器,这里 我们直接把webapps和logs目录挂载出来,方便添加项目和查看日志
docker run -d -p 8180:8080 --name mytomcat1 -v /home/dockerFiles/tomcat/webapps/:/usr/local/apache-tomcat-10.1.46/webapps/ -v /home/dockerFiles/tomcat/logs:/usr/local/apache-tomcat-10.1.46/logs mytomcat:v1