5分钟读懂 DockerFile 文件

基础信息

是什么

Dockerfile 是镜像的描述文件,Docker 可以读取这个文件生成新的镜像。
image.png

Dockerfile、镜像、容器三者关系

从应用软件的角度来看,Dockerfile、Docker镜像与Docker容器分别代表软件的三个不同阶段:

  • Dockerfile是软件的原材料
  • Docker镜像是软件的交付品
  • Docker容器则可以认为是软件镜像的运行态,也即依照镜像运行的容器实例

Dockerfile 面向开发,Docker 镜像成为交付标准,Docker 容器则涉及部署与运维,三者缺一不可,合力充当Docker体系的基石。
image.png
构建三步骤:

  1. 编写 Dockerfile 文件
  2. Docker build 命令构建镜像
  3. Docker run 依镜像运行容器实例

Docker 执行 Dockerfile 的大致流程

  1. Docker从基础镜像运行一个容器
  2. 执行一条指令并对容器作出修改
  3. 执行类似 Docker commit 的操作提交一个新的镜像层
  4. Docker 再基于刚提交的镜像运行一个新容器
  5. 执行 Dockerfile 中的下一条指令直到所有指令都执行完成

编写 Dockerfile

Docker hub 上的镜像也给出了 Dockerfile 文件链接,可以点击查看参照。

Dockerfile 基础信息:

  1. 每条保留字指令都必须为大写字母且后面要跟随至少一个参数
  2. 指令按照从上到下,顺序执行
  3. #表示注释
  4. 每条指令都会创建一个新的镜像层并对镜像进行提交

Dockerfile 文件保留字

FROM

基础镜像,当前新镜像是基于哪个镜像的,指定一个已经存在的镜像作为模板,第一条必须是 FORM

MAINTAINER

镜像维护者的姓名和邮箱地址

RUN

构建镜像时(Docker build)运行的命令

两种格式

  • shell 格式:RUN 命令
    • RUN 后面的命令相当于在 FORM 的基础镜像上,实例出的容器里的终端中运行。
      • 比如想要基于 centos7 构建一个带有 node 环境的 centos7Plus 镜像,
      • RUN yum install -y node
      • 那就相当于在原版 centos7 的终端中执行yum install -y node
  • exec 格式(json 数组形式):RUN ["可执行文件", "参数1", "参数2"]
    • eg:RUN ["./test.php", "dev", "offline"等同于RUN ./test.php dev offline

EXPOSE

当前容器对外暴露出的端口

WORKDIR

指定在创建容器后,终端默认登录的进来工作目录,一个落脚点。比如 Tomcat 镜像,一进入容器终端,当前目录为/usr/local/tomcat

USER

指定该镜像以什么样的用户去执行,如果都不指定,默认是root。
USER 用户名:用户组 或
USER 用户id:组id

ENV

用来在构建镜像过程中设置环境变量。

举个例子:
ENV MY_PATH /usr/mytest
这个环境变量可以在后续的任何RUN指令中使用,这就如同在命令前面定义了别名一样;也可以在其它指令中直接使用这些环境变量。
比如,设置默认工作目录:WORKDIR $MY_PATH

ADD 和 COPY

ADD <src> <dest>

  • 详解:该命令将复制指定的 路径下内容到镜像中的 路径下
  • :可以是 Dockerfile 所在目录的一个相对路径(文件或目录);也可以是一个 URL;还可以是一个 tar 文件(自动解压为目录)
  • :可以是镜像内绝对路径,或者相对于工作目录(WORKDIR)的相对路径
  • 路径:支持正则表达式,

COPY 类似 ADD,拷贝文件和目录到镜像中。
将从构建上下文目录中 <源路径> 的文件/目录复制到新的一层的镜像内的 <目标路径> 位置。

和 RUN 一样两种格式:

  • COPY src dest
  • COPY ["src", "dest"]

<src源路径>:源文件或者源目录
<dest目标路径>:容器内的指定路径,该路径不用事先建好,路径不存在的话,会自动创建。

注意

  1. 来自远程 URL 资源不会被解压缩。如果一个目录被复制或解压时,它的行为与 tar -x 相同。
  2. 是绝对路径,或相对于 WORKDIR 的路径。并且表示目录要以斜杠结尾,要不然会被视为文件,内容将会追加入该文件中。
# 使用相对路径的栗子
ADD test.txt relativeDir/
# 等价于
ADD test.txt <WORKDIR>/relativeDir/

# 使用绝对路径的栗子
# 将 test.txt 添加到 /absoluteDir/ 目录下
ADD test.txt /absoluteDir/
  1. 如果是目录,只会复制目录中的内容过去,目录本身不复制过去。并且**是 Dockerfile 文件所在目录的相对路径。**
    1. 不能添加 …/something 、 /something 这样的目录,因为 docker 构建的第一步是将上下文目录,也就是 Dockerfile 文件所在的目录(和子目录)发送到 docker 守护进程
# test.txt 是相对路径,相对于构建上下文
COPY test.txt /mkdir/

# 错误写法,文件均不在上下文目录中,并不会被找到
# 这个找的就是构建上下文的上级目录的 test.txt
COPY ../test.txt /mkdir/

# 这个找的是本机根目录下的 test.txt
COPY /test.txt /mkdir/

根据官方 Dockerfile 最佳实践,除非真的需要从远程 url 添加文件或自动提取压缩文件才用 ADD,其他情况一律使用 COPY。

  • ADD 从远程 url 获取文件和复制的效果并不理想,因为该文件会增加 Docker Image 最终的大小
  • 相反,应该使用 curl huo wget 来获取远程文件,然后在不需要它时进行删除

总结:

  • ADD 支持添加远程 url 和自动提取压缩格式的文件,COPY 只允许从本机中复制文件
  • COPY 支持从其他构建阶段中复制源文件(–from)
  • 根据官方 Dockerfile 最佳实践,除非真的需要从远程 url 添加文件或自动提取压缩文件才用 ADD,其他情况一律使用 COPY

VOLUME

容器数据卷,用于数据保存和持久化工作

CMD 和 ENTRYPOINT

两者都是指定新建的镜像运行时(docker run),容器终端中默认执行的命令。
比如运行 ubuntu 镜像:docker run -it ubuntu,一运行就直接启动了 Ubuntu 系统的 bash。应该就是构建 ubuntu 镜像时,Dockerfile 通过 CMD 或者 ENTRYPOINT 指定了默认启动 bash。

两者指定的命令,最好是阻塞式命令,也就是有前台任务的命令,比如 bash,tail 这些。要不然如果是非阻塞式的命令,命令一运行完毕,容器就退出停止执行了。

Dockerfile 中这两个指令必须要有一个,如果没有则从父镜像中继承,如果父镜像也没有,则镜像构建失败。

两者和 RUN 一样有两种格式定义命令,shell 和 exec。

  • Shell格式:。例如:apt-get install python3
  • Exec格式:[“executable”, “param1”, “param2”, …]。例如: [“apt-get”, “install”, “python3”]

CMD 和 ENTRYPOINT 推荐使用 Exec 格式,因为指令可读性更强,更容易理解。RUN 则两种格式都可以。

区别

  1. Dockerfile 中可以有多个 CMD 指令,但只有最后一个生效,并且 CMD 指定的默认命令会被 docker run 之后手动传入的命令替换。

比如 Redis,直接运行docker run -it redis它直接进入 Redis 控制命令行。如果运行docker run -it redis /bin/bash,就会进入 Redis 容器的虚拟 Linux 环境终端,需要再运行redis-cli才能进入 Redis 命令控制行。

[root@192 app]# docker run -it redis
1:C 08 Feb 2023 19:57:36.965 # oO0OoO0OoO0Oo Redis is starting oO0OoO0OoO0Oo
......
1:M 08 Feb 2023 19:57:36.968 * Ready to accept connections

(ctrl + c)
1:M 08 Feb 2023 19:58:37.474 # Redis is now ready to exit, bye bye...

[root@192 app]# docker run -it redis /bin/bash
root@b5df641baaa0:/data# redis-cli
  1. ENTRYPOINT 为 shell 格式,则以 ENTRYPOT 为准,忽略 CMD 和 docker run 后手动添加的命令。
  2. ENTRYPOINT 为 exec 格式且 CMD 也为 exec 格式时,无论是 CMD 还是手动输入的命令都是拼接到 ENTRYPOINT 的命令后面作为参数。
    1. 注意:docker run 后面存在命令时,始终会覆盖 CMD 的命令
FROM alpine
WORKDIR /docker
RUN echo 111 >> 1.txt
RUN echo 222 >> 2.txt
RUN echo 333 >> 3.txt
CMD ["cat","/docker/2.txt"]
ENTRYPOINT ["cat","/docker/1.txt"]
docker build -t test1 .

docker run test1 #执行的命令相当于 cat 1.txt cat 2.txt
> cat: can't open 'cat': No such file or directory
> 111
> 222

docker run test1 3.txt #执行的命令相当于 cat 1.txt 3.txt
> 111
> 333 #docker run 后面参数代替了 CMD
  1. ENTRYPOINT 为 exec 格式且 CMD 为 shell 时,拼接的结果是 [ENTRYPOINT命令] /bin/sh -c [CMD命令],因为整个命令中间多了一个/bin/sh -c命令通常会导致整个命令无效。所以这种混合格式使用要少用。
    • sh -c命令,它可以让 bash 将一个字串作为完整的命令来执行。
FROM alpine
WORKDIR /docker
RUN echo 111 >> 1.txt
RUN echo 222 >> 2.txt
RUN echo 333 >> 3.txt
CMD cat /docker/2.txt
ENTRYPOINT ["cat","/docker/1.txt"]
docker build -t test1 .

docker run test2 #执行的命令相当于 cat 1.txt /bin/sh -c cat 2.txt
#因为 cat 命令无法识别 -c 选项,所以导致整个命令无效报错
cat: unrecognized option: c
BusyBox v1.34.1 (2021-11-23 00:57:35 UTC) multi-call binary.

Usage: cat [-nbvteA] [FILE]...

Print FILEs to stdout

        -n      Number output lines
        -b      Number nonempty lines
        -v      Show nonprinting characters as ^x or M-x
        -t      ...and tabs as ^I
        -e      ...and end lines with $
        -A      Same as -vte

docker run test2 3.txt #覆盖了CMD,所以命令正常
> 111
> 333

官网介绍的区别:

Understand how CMD and ENTRYPOINT interact

Both and instructions define what command gets executed when running a container. There are few rules that describe their co-operation.CMD ENTRYPOINT

  1. Dockerfile should specify at least one of or commands.CMD ENTRYPOINT
  2. ENTRYPOINT should be defined when using the container as an executable.
  3. CMD should be used as a way of defining default arguments for an command or for executing an ad-hoc command in a container.ENTRYPOINT
  4. CMD will be overridden when running the container with alternative arguments.

The table below shows what command is executed for different / combinations:ENTRYPOINTCMD
image.png

Note:
If is defined from the base image, setting will reset to an empty value. In this scenario, must be defined in the current image to have a value.CMD ENTRYPOINT CMD CMD

总结

不同阶段,发挥作用的关键字。

BUILD BOTH RUN
FORM WORKDIR CMD
MAINTAINER USER ENV
COPY EXPOSE
ADD VOLUME
RUN ENTRYPOINT
ONBUILD
.dockerignore

练习

需求:构建带有 vim+ifconfig+jdk17 的 centos 镜像。
下载 jdk17 到 Dockerfile 文件所在目录(这里为:/docker)

wget https://download.oracle.com/java/17/latest/jdk-17_linux-x64_bin.tar.gz -P /docker/

编写 Dockerfile:

FROM centos7
MAINTAINER ikun
 
ENV MYPATH /usr/local
WORKDIR $MYPATH
 
#安装vim编辑器
RUN yum -y install vim
#安装ifconfig命令查看网络IP
RUN yum -y install net-tools
#安装lib库
RUN yum -y install glibc.i686
RUN mkdir /usr/local/java

#ADD 是相对路径jar,把jdk添加到容器中,安装包必须要和Dockerfile文件在同一位置
ADD jdk-17_linux-x64_bin.tar.gz /usr/local/java/

#配置java环境变量
ENV JAVA_HOME /usr/local/java/jdk-17.0.6
ENV CLASSPATH $JAVA_HOME/lib/dt.jar:$JAVA_HOME/lib/tools.jar:$CLASSPATH
ENV PATH $JAVA_HOME/bin:$PATH
 
EXPOSE 80

#CMD的覆盖不影响CMD输出
CMD echo $MYPATH
CMD echo "success--------------ok"
CMD /bin/bash

构建镜像

build 命令构建镜像

语法:docker build -t <镜像名>:<tag> <Dockerfile文件目录>
举例:docker build -t myCentos .

  • tag 省略,默认为 latest
  • . 表示当前目录

虚悬镜像(dangling image)

虚悬镜像就是镜像名和 tag 都是 none 的镜像。
我们在构建的时候,不小心忘记填写镜像名和 tag,如docker build .就会构建出虚悬镜像。
查看本机的虚悬镜像列表:docker image ls -f dangling=true
虚悬镜像没有价值,建议删除:docker image prune

猜你喜欢

转载自blog.csdn.net/qq_43220213/article/details/129608582