Docker Dockerfile的介绍与基本用法

    技术2022-08-02  80

    介绍

          Dockerfile是一个记录了若干命令文本文件,用来构建一个全新的镜像。

    docker上下文

          拿菜鸟教程上的一个例子:docker build -t runoob/ubuntu:v1 . ,可以看到这条指令最后的参数是一个点,这个点就是上下文路径。通常大多数人会在Dockerfile存放目录中执行上面这条命令,假设Dockerfile中的指令不出错的情况下成功构建镜像了,那么他们很可能会以为命令中最后的参数——“点”,指的是Dockerfile所在的目录。       并不能说完全不对,在上面描述的场景中确实Dockerfile所在的目录就是上下文目录。但实际上上下文目录与Dockerfile并没有必然的联系,因为Dockerfile完全是可以不在上下文目录的。假设有下面的目录结构:

    work ├── Dockerfile └── workspace ├── a.txt └── html ├──index.html

    一个work目录下有Dockerfile文件和workspace目录,workspace目录下有a.txt文件和html目录,html目录下有index.html文件。       如果直接在work目录下执行命令docker build -t runoob/ubuntu:v1 .,可以成功构建出镜像。但如果在workspace中执行上面的命令,将会报错。或许你会说因为此时上下文路径变成了workspace,在这个路径下并没有Dockerfile,所以报错了,这个解释是对的,不过似乎有点曲解。下面我先讲讲镜像构建的过程,然后你就明白了哪里有问题:

    tip:下面的说法主要是面向docker18及后续的版本,经测试docker17版本要求Dockerfile必须在上下文路径下(即Dockerfile必须被上下文目录包含,倒不要求一定要在指定的上下文目录中,也可以是上下文目录中的某个目录下),但是docker18版本Dockerfile可以不在上下文路径中,只要在docker build的时候指定Dockerfile路径即可。       首先,docker build命令最后的参数指定的是上下文目录,在执行命令的时候如果不指定Dockerfile的路径,默认会到上下文路径中找一个名为Dockerfile的文件作为镜像构建的配置文件,并将上下文目录中的内容全部打成tar包发送给docker守护进程去解压构建镜像。(这个时候你应该能够体会到上下文路径和Dockerfile并没有必然的联系,是因为没有指定Dockerfile的路径,所以会在上下文路径中找名为Dockerfile的文件,而不是先前认为的Dockerfile所在目录就是上下文目录。)       其次,我们完全可以手动指定Dockerfile的路径,这样一来Dockerfile和上下文路径就没有关系了(这里假设不考虑Dockerfile中涉及到上下文路径的指令),假设我在work目录下执行命令:docker build -f Dockerfile -t myimages workspace,-f 参数后面指定的是Dockerfile的路径,workspace是上下文目录。结合上面的目录结构,可以看到Dockerfile并不在上下文目录中,这种情况下也可以成功构建镜像,由此可见Dockerfile并非一定要在上下文目录下。       总结:上下文目录是我们在docker build命令中指定的最后一个参数,这个上下文目录是由我们手动指定的,你写了什么路径,命令就会把那个路径当做上下文路径。作用是在docker build的时候会将上下文目录中的全部内容打包成tar包发送给docker守护进程,作为新镜像构建的组成文件,以及Dockerfile中命令涉及源文件路径的上下文路径。docker版本在17和18之间是一个分水岭,在17版本或之前的版本,Dockerfile必须放到上下文目录下,或者是其子目录下,否则在docker build构建镜像的时候会报 “Dockerfile不在上下文路径中” 的错误。但是从18版本开始,Dockerfile可以不用放在上下文路径中,即位置随意,但是在docker build的时候就必须明确使用-f 指定Dockerfile的路径。

    Dockerfile常用指令

    tip:下列指令中如CMD、ENTRYPOINT等涉及到命令的书写格式,有shell(echo $PATH)、exec([“echo”,$PATH]),这两种方式都可以,但笔者比较喜欢shell脚本的形式,故下面都以shell脚本作为示例,至于exec格式的读者可自行查找用法。

    FROM

    说明:指定基础镜像,我们构建的镜像都是以某个镜像为基础进行构建,是Dockerfile文件的首条指令 指令:FROM 基础镜像 举例:FROM ubuntu:15.10

    RUN

    说明:docker build在构建镜像的时候执行的指令,通常是一些软件包的安装及后续的删除操作 指令:RUN 命令 举例:RUN apt-get install -y nginx tip:RUN 指令可以有很多条,但是每一条指令都会构建一个镜像层,由于一个完整的镜像是由其所有的镜像层组成的,所以镜像层越多,意味着镜像体积越大,将会占用更多的存储空间,在部署方面速度也会受影响。 优化:尽量写成一个RUN指令,针对不同的命令只需要用连接符将命令连接起来即可。 如:       RUN apt-get install -y nginx       RUN apt-get install -y mysql       RUN rm -rf /var/lib/apt/lists/* 可优化为:       RUN apt-get install -y nginx \                && apt-get install -y mysql \                && rm -rf /var/lib/apt/lists/* 其中:\ 符号表示Linux连接符,表示还没写完;&&是“且”的意思,表示左右两边的命令要并列执行。

    CMD

    说明:docker run在创建并运行容器时执行的指令 指令:CMD 命令 举例:CMD echo $PATH tip:1、CMD指令也可以写很多条,但只有最后一条CMD指令会被执行。         2、如果在docker run末尾指定了执行命令,那么Dockerfile中配置的CMD指令会被覆盖,因为作为启动参数的优先级更高。         3、如果CMD指令和ENTRYPOINT指令同时存在,则CMD指令的命令作为ENTRYPOINT的参数。

    ENTRYPOINT

    说明:容器启动命令 指令:ENTRYPOINT 命令 举例:ENTRYPOINT echo $PATH tip:1、ENTRYPOINT指令也可以写很多条,但只有最后一条ENTRYPOINT指令会被执行。         2、如果在docker run时指定了- -entrypoint参数,那么 Dockerfile中设置的ENTRYPOINT命令将被覆盖,因为启动参数的优先级更高。         3、如果CMD指令和ENTRYPOINT指令同时存在,则CMD指令的命令作为ENTRYPOINT的参数。

    ENV

    说明:配置环境变量,以键值对方式设置 指令:ENV key val 或 ENV key=val 举例:ENV version 1.1.1 tip:这个环境变量就是在Linux中可以通过ENV命令查看到的环境变量,通过 $key 可以直接拿到值,key替换成设定的环境变量。

    EXPOSE

    说明:暴露容器端口 指令:EXPOSE port1/protocal1 port2/protocal2 … 举例:EXPOSE 100/tcp 200/udp tip:将容器端口暴露给宿主机,在docker run的时候可以通过-P随机映射端口到宿主机上(将容器暴露出来的端口100和200随机映射到宿主机端口),也可以通过-p手动指定映射到宿主机的端口。(如果暴露了端口,但是没有做端口映射,那么宿主机是无法访问到端口的,只能在容器内部访问容器暴露的端口)

    LABEL

    说明:给即将构建的镜像添加标签,相当于简单的信息描述,以键值对方式配置 指令:LABEL key=val key=val … 举例:LABEL maintainer=“Ken” \                        phone=“12345678” \                        addr=“xxx-yyy-zzz” tip:举例中使用“\”连接符表示还没写完,这样就可以把一个个的键值写成单行的格式,提高了Dockerfile文件的可读性。标签会自动继承父镜像的标签,如果出现同键的键值对,那么后者会覆盖前者的值。

    COPY

    说明:从上下文路径的相对路径中拷贝文件或目录到镜像文件系统中 指令:COPY 上下文路径的相对路径文件或目录 文件系统中的某个目录下 举例:COPY a.txt /work/ tip:上例中a.txt文件的路径实际上就是“上下文路径下的a.txt”,默认就是在上下文路径下直接找a.txt,当然也可以写成 ./a.txt,第二个参数“/work”是文件系统中的路径,这里是文件系统中根目录“/”下的work目录下,即将a.txt拷贝到/work下,如果work不存在则会自动创建。

    WORKDIR

    说明:指定工作目录,工作目录在每个镜像层中都会存在 指令:WORKDIR 目录 举例:WORKDIR /mydir tip:工作目录必须事先存在,即在使用WORKDIR指令之前要确保目录已经存在(要么文件系统中本来就有这个目录,要么自己RUN mkdir的方式创建)。WORKDIR指定的目录是在每个镜像层都存在的,因此涉及到路径的指令、命令等均可访问。但如果在命令中创建目录abc后,后续命令使用了abc目录但并没有将abc目录指定为工作目录,这时候docker build会报错,因为创建目录等修改均是在内存中做修改,每个镜像层都会提交给下一个镜像层,但并不会连内存也提交,因此在后续镜像层中根本就找不到abc目录。如果有将abc目录使用指令WORKDIR指定为工作目录,那么在每个镜像层中均会存在有abc目录,此时在命令中就可以正常访问了。WORKDIR指令必须写在创建目录之后(肯定是先有目录,再把它设置成工作目录嘛),至于后面具体放在什么位置,就随你设计了。

    VOLUME

    说明:卷,在这里充当一个挂载点的角色,宿主机将某个目录挂载到容器中的指定挂载点,可以实现宿主机与容器的资源共享。当然,如果其他容器也挂载了同一目录,就可以实现容器之间的资源共享。除此之外,卷的目的是为了做容器中产生的数据的持久化,不让数据存储在容器上。这样一来即使容器关闭了、销毁了也不会造成数据丢失,也不会因为数据直接存在容器中而造成容器体积增大。 指令:VOLUME 目录 举例:VOLUME /mydir tip:这里有一个挂载目录和挂载点的概念,宿主机上的我们称为“挂载目录”,容器中的我们称为“挂载点”,通过“挂载目录”挂载到“挂载点”,可以实现宿主机和容器资源共享。同时在docker run的时候还可以给即将创建的容器指定与某一容器共享挂载目录,这样就实现了容器之间的资源共享了。在Dokcerfile中的VOLUME指令指定的是镜像中的挂载点,在宿主机上将创建一个名字随机的“挂载目录”,并让“挂载目录”默认挂载到“挂载点”上。         在Dockerfile中使用VOLUME指令指定容器的挂载点,那么在用docker run创建并启动容器时(假设没有使用-v手动指定挂载目录)宿主机默认会本地创建一个挂载目录并挂载到容器的挂载点上,通过docker inspect 命令可以查看到挂载目录的信息。         如果Dockerfile中用VOLUME指定了容器的挂载点,但是在docker run时使用-v手动指定了要映射到挂载点的宿主机上的挂载目录,这时候会使用你手动指定的挂载目录,因为启动参数的优先级更高,同时宿主机也不会自动创建一个挂载目录。         宿主机上的一个挂载目录是可以同时挂载到多个容器的挂载点上的,可以在docker run 的时候使用-v手动指定挂载目录,也可以使用(- -volumes-from 容器名(或容器ID))指定要和某个容器的使用相同的挂载目录。这样就实现了容器之间的资源共享,主要还是借助宿主机上的挂载目录作为资源共享的中间桥梁。         挂载目录和挂载点是双向的,挂载点中显示的内容就是挂载目录挂载过来的内容,相当于他们的资源是共享的,不管是在挂载目录中做修改还是在挂载点中做修改,最终两边显示的内容都会发生修改。

    容器指定挂载目录后如何更改挂载目录:https://blog.csdn.net/QianLiStudent/article/details/107164192

    .dockerignore

             . dockerignore和 . gitignore的意义与用法相同,都是通过在该文件中记录一些文件或目录,表示不将这些文件或目录进行纳管。在使用docker build打包上下文目录的内容的时候会将记录在 . dockerignore中的文件或目录自动忽略掉,那么这些文件就不会被打包了,可减小生成镜像的大小,使镜像更轻便干净。(有时候上下文路径中的内容并非都是构建新镜像需要发送过去的文件,这时候需要用**.** dockerignore将某些不需要的文件忽略掉,. dockerignore文件放置上下文目录下即可)

    符号说明#注释符号,表示本行注释*匹配0到任意多个字符?匹配1个字符**匹配0到任意多个目录!将…排除在外,表示取反【因为.dockerignore记录要被忽略的文件,取反就是不能被忽略的文件】

    # .dockerignore的内容如下: *.md ab?.txt !abc.txt **/abc # .dockerignore的内容结束。 说明:扩展名为md的文件会被忽略;            abx.txt(x表示1个任意字符)的文件会被忽略;            abc.txt文件不能被忽略;            上下文目录下的任意目录下的abc目录会被忽略;

    如果写在后面的路径与写在前面的路径冲突,则后者会覆盖前者。 # .dockerignore的内容如下: abc.txt !ab?.txt # .dockerignore的内容结束。 说明:前面写了abc.txt文件要被忽略,后面又写了abx.txt(x表示1个任意字符)文件不能被忽略,后者覆盖前者,相当于前面写的abc.txt没用,因为abx.txt就包含了abc.txt并且被声明为不能忽略了。

    使用命令构建全新镜像

    命令:docker build [-f Dockerfile路径] -t 镜像名 上下文路径 举例:docker build [-f Dockerfile] -t myimage:v1 workspace tip:这里举例依然是假定上面的目录结构,在work目录下执行命令。[ ]中指定的是Dockerfile的路径,在上面这种目录结构下Dockerfile路径必须指定,因为上下文目录中没有名为Dockerfile的文件。但是如果不指定Dockerfile路径,就必须确保上下文目录中有一个名为Dockerfile的文件。

    验证镜像是否构建成功并确保镜像能够正常使用

    说明:查看本地镜像列表 命令:docker images 或 docker image ls

    说明:创建并运行容器 命令:docker run -itd --name mycontainer1 myimage:v1 /bin/bash tip:这里的镜像myimage:v1是上面构建出来的镜像。

    Processed: 0.052, SQL: 10