1. 首页
  2. Docker

Dockerfile构建docker镜像

1. Dockerfile 文件结构

Dockerfile 是一个文本文件,并且支持以 # 开头的注释行,包含了众多指令,每一条指令构建一层,因此每条指令的内容就是描述该层镜像如何构建。

一般的,Dockerfile 分为四部分:基础镜像信息、维护者信息、镜像操作指令和容器启动时执行指令。

# this dockerfile use the nginx image
# VERSION 1
# Author: enzhige

# FROM 指令必须位于 Dockerfile 文件中的第一个非注释行,用于指定构建过程中的基础镜像
FROM nginx

# 用于提供作者的本人信息
MAINTAINER enzhige "W_enzhi@163.com"

# 用于指定 docker build 过程中运行的程序,其可以是任何命令
RUN echo '<h1>Hello Docker</h1>' > /usr/share/nginx/html/index.html

# CMD 指令的首要目的在于为启动的容器指定默认要运行的程序
CMD ["/usr/sbin/nginx","-g","daemon off;"]

这个 Dockerfile 很简单,一共就三行。涉及到了 4 条指令 FROM、MAINTAINER、RUN、CMD。

2. docker build 命令构建镜像

格式为:

docker build [选项] <上下文路径/URL>

接下来构建镜像:构建镜像时必须在 Dockerfile 所在的目录下

root@k8s-stress-ops-bjqw:~/nginx # docker build -t enzhige/nginx:v1 .
Sending build context to Docker daemon   2.56kB
Step 1/4 : FROM nginx
latest: Pulling from library/nginx
27833a3ba0a5: Pull complete
ea005e36e544: Pull complete
d172c7f0578d: Pull complete
Digest: sha256:e71b1bf4281f25533cf15e6e5f9be4dac74d2328152edf7ecde23abc54e16c1c
Status: Downloaded newer image for nginx:latest
 ---> 27a188018e18
Step 2/4 : MAINTAINER enzhige "W_enzhi@163.com"
 ---> Running in db26dd67968c
Removing intermediate container db26dd67968c
 ---> f6150453c2ad
Step 3/4 : RUN echo '<h1>Hello Docker</h1>' > /usr/share/nginx/html/index.html
 ---> Running in 7334c6fdc69c
Removing intermediate container 7334c6fdc69c
 ---> 6b0d0582d49d
Step 4/4 : CMD ["/usr/sbin/nginx","-g","daemon off;"]
 ---> Running in e59b0a5d5fc7
Removing intermediate container e59b0a5d5fc7
 ---> 4d935674752b
Successfully built 4d935674752b
Successfully tagged enzhige/nginx:v1

3. Dockerfile 常用指令说明

FROM 指令

FROM 指令是最重要的一个且必须为 Dockerfile 文件开篇的第一个非注释行,用于为映像文件构建过程指定基准镜像,后续的指令运行于此基准镜像所提供的运行环境。

实践中,基准镜像可以是任何可用镜像文件,默认情况下,docker build 会在 docker 主机上查找指定的镜像文件,在其不存在时,则会从 docker hub registry 上拉取所需的镜像文件。

如果找不到指定的镜像文件,docker build 会返回一个错误信息。

语法格式:

FROM <image>[:<tag>]
或
FROM <image>@<digest>
    <image>:指定作为base image的名称;
    <tag>:base image的标签,为可选项,省略时默认为latest;

举例演示:

FROM centos:7.4.1708

MAINTAINER 指令

目前较新的版本中已经废弃了,用于让镜像制作者提供本人的详细信息。

Dockerfile 并不限制 MAINTAINER 指令可在出现的位置,但推荐将其放置于FROM 指令之后。

语法格式:

MAINTAINER <author's detail>
    <author's detail>:可以是任何文本信息,但约定俗称地使用作者名称及邮件地址;

举例演示:

MAINTAINER enzhige "W_enzhi@163.com"

LABEL指令

用于让用户给镜像指定元数据信息;

语法格式:

LABEL <key>=<value> <key>=<value> ...

举例演示:

LABEL maintainer="Enzhige W_enzhi@163.com"

COPY 指令

用于从 docker 主机复制文件到创建的新镜像文件;

语法格式:

COPY <src>...<dest>
或
COPY ["<src>",..."<dest>"]
    <src>:要复制的源文件或目录,支持使用通配符;
    <dest>:目标路径,既正在创建的image的文件系统路径;建议为<dest>使用绝对路径,否则,COPY 指令则以 WORKDIR 为起始路径;

注意:在路径中有空白字符时,通常使用第二种格式;

文件复制准则:

  1. <src>必须是 build 上下文中的路径,不能是其父目录中的文件;
  2. 如果 <src> 是目录,则其内部文件或子目录会被递归复制,但 <src> 目录自身不会被复制;
  3. 如果指定了多个 <src> ,或在 <src> 中使用了通配符,则 <dest> 必须是一个目录,且必须以「/」结尾;
  4. 如果 <dest> 事先不存在,它将会被自动创建,这包括其父目录路径;

举例演示:复制 htdocs 目录中的 index.html 目录至容器中的 /data/webapps 目录中

COPY htdocs /data/webapps/

ADD 指令

ADD 指令类似于 COPY 指令,ADD 支持使用 TAR 文件和 URL 路径;

语法格式:

ADD <src>...<dest>
或
ADD ["<src>",..."<dest>"]

操作准则:同 COPY 指令

  1. 如果<src>为 URL 且<dest>不以/结尾,则<src>指定的文件将被下载并直接被创建为<dest>
  2. 如果<dest>以/结尾,则文件名 URL 指定的文件将被直接下载并保存为 <dest>/<filename>
  3. 如果<src>是一个本地系统上的压缩格式的tar文件,它将被展开为一个目录,其行为类似于tar -xf指令;然而,通过 URL 获取到的tar文件将不会自动展开;
  4. 如果 <src> 有多个,或其间接或直接使用了通配符,则 <dest> 必须是一个以/结尾的目录路径;如果 <dest> 不以/结尾,则其被视作一个普通文件,<src> 的内容将被直接写入到 <dest>

举例演示1:复制两个 yum 仓库配置文件到创建镜像的 /etc/yum.repos.d/ 目录下

# This dockerfile use the centos image
# VERSION 1
# Author: enzhige

FROM centos:7.4.1708

ADD http://mirrors.aliyun.com/repo/Centos-7.repo http://mirrors.aliyun.com/repo/epel-7.repo /etc/yum.repos.d/

举例演示2:复制 tomcat 的源码包到新创建镜像的 /usr/local/ 目录下

# This dockerfile use the centos image
# VERSION 1
# Author: enzhige

FROM centos:7.4.1708

ADD apache-tomcat-8.5.15.tar.gz /usr/local/

docker build 镜像

root@k8s-stress-ops-bjqw:~/file # docker build -t enzhige/tomcat:v1 .
Sending build context to Docker daemon  9.397MB
Step 1/2 : FROM centos:7.4.1708
 ---> 9f266d35e02c
Step 2/2 : ADD apache-tomcat-8.5.15.tar.gz /usr/local/
 ---> e28d87132e8c
Successfully built e28d87132e8c
Successfully tagged enzhige/tomcat:v1

验证 tomcat 源码包是否复制到容器内部并解压

验证ADD指令复制tar包
验证ADD指令复制tar包

WORKDIR 指令

用于为 Dockerfile 中所有的 RUN、CMD、ENTRYPOINT、COPY 和 ADD 指令设定工作目录;容器启动时执行的命令会在该目录下执行。

语法格式:

WORKDIR <dirpath>

举例演示:下面我们来看一个 Dockerfile

# This dockerfile use the centos image
# VERSION 1
# Author: enzhige

FROM centos:7.4.1708

RUN mkdir /testdir && \
    echo "Hello World" > /testdir/test.txt

WORKDIR /testdir

CMD ["more","test.txt"]

假设根据该 dockerfile 构建的镜像名为 myimage

1、运行 docker run myimage 输出 Hello World

WORKDIR指令示例1
WORKDIR指令示例1

2、运行 docker run myimage more test.txt 和上面输出一致

WORKDIR指令示例2
WORKDIR指令示例2

可以看出,more 的参数是 test.txt,但没有指定路径,却能成功,说明当前路径就是上面 WORKDIR 指令设置的。

如果我们在上面的 dockerfile 中把 WORKDIR 指令去掉,创建的容器运行会报文件不存在错误。

VOLUME 指令

用于在镜像中创建一个挂载点目录,以挂载 Docker host 上的卷或其它容器上的卷;

语法格式:

VOLUME <mountpoint>
或
VOLUME ["<mountpoint>"]
    <mountpoint>:为在容器中的目录,而在宿主机上的目录是在/var/lib/docker/volumes/目录下的随机字符串

如果挂载点目录路径下此前文件存在,docker run 命令会在卷挂载完成后将此前的所有文件复制到新挂载的卷中;

举例演示:下面我们来看一个 Dockerfile

# This dockerfile use the centos image
# VERSION 1
# Author: enzhige

FROM centos:7.4.1708

VOLUME ["/data1","/data2"]

上面的 dockerfile 文件通过 VOLUME 指令指定了两个挂载点 /data1 和 /data2

运行容器后,通过 docker inspect 查看通过该 dockerfile 创建的镜像生成的容器,可以看到如下信息

dovker volume指令
dovker volume指令

EXPOST 指令

用于为容器打开指定要监听的端口以实现与外部通信;

语法格式:

EXPOSE <port>[/<protocol>] [port][/<protocol>]...]
    <protocol>:用于指定传输层协议,可为tcp或udp二者之一,默认为tcp协议;

EXPOSE指令可一次指定多个端口,例如:

EXPOSE 11211/udp 11211/tcp

注意:这里暴露的端口为容器的端口,而宿主机上的端口是动态的

ENV指令

用于为镜像定义所需的环境变量,并可被Dockerfile文件中位于其后的其它指令(如ENV、ADD、COPY等)所调用;

调用格式为: $variable_name 或 ${variable_name}

语法格式:

ENV <key> <value>
或
ENV <key>=<value> ...
  • 第一种格式中,之后的所有内容均会被视作其的组成部分,因此,一次只能设置一个变量;
  • 第二种格式可用一次设置多个变量,每个变量为一个 “=” 的键值对,如果中包含空格,可以以反斜线 (\) 进行转义,也可通过对加引号进行标识;另外,反斜线也可用于续行;

定义多个变量时,建议使用第二种格式,以便在同一层中完成所有功能;

举例演示:下面我们来看一个 Dockerfile

# This dockerfile from centos image build jdk image
# VERSION 1
# Author: enzhige

FROM centos:7.4.1708
MAINTAINER enzhige "W_enzhi@163.com"

ENV JAVA_HOME="/usr/local/jdk"
ENV CLASSPATH=".:${JAVA_HOME}/jre/lib/rt.jar:${JAVA_HOME}/lib/tools.jar"
ENV PATH="${JAVA_HOME}/bin:${PATH}"

ADD jdk-8u121-linux-x64.tar.gz /usr/local/
RUN ln -s /usr/local/jdk1.8.0_121/ /usr/local/jdk

上面 dockerfile 我们要构建一个 jdk 镜像,通过 ENV 声明了 JDK 的环境变量信息

RUN 指令

用于指定 docker build 过程中运行的命令,其可以是任何命令;

语法格式:

RUN <command>
或
RUN ["<executable>","<paraml>","<paraml2>"]
  1. 第一种格式中,通常是一个 shell 命令,且以 “/bin/sh -c” 来运行它,这意味着此进程在容器中的 PID 不为 1 不能接收 Unix 信号,因此,当使用 docker stop 命令停止容器时,此进行接收不到 SIGTERM 信号;
  2. 第二种格式中的参数是一个 JSON 格式的数组,其中为要运行的命令,后面的为传递给命令的选项或参数,然而,此种格式指定的命令不会以 “/bin/sh -c” 来发起,因此常见的 shell 操作如变量替换以及通配符 (? , * 等) 替换将不会进行;不过,如果要运行的命令依赖于此 shell 特性的话,可以将其替换为类似下面的格式。
RUN ["/bin/bash","-c","<executable>","paraml"]

RUN 指令中可以使用 && 执行多个命令从而避免使用多个 RUN,也可以使用 \ 来进行换行操作,从而使每一行显示一个命令让 Dockerfile 变得更加易于阅读。

举例演示:在执行 build 过程中,在容器中安装 nginx 服务

# This dockerfile from centos image install nginx
# VERSION 1
# Author: enzhige

FROM centos:7.4.1708
MAINTAINER enzhige "W_enzhi@163.com"

ADD http://mirrors.aliyun.com/repo/epel-7.repo /etc/yum.repos.d/

RUN yum -y install nginx && \
    yum clean all

EXPOSE 80/tcp

CMD ["/usr/sbin/nginx","-g","daemon off;"]

CMD 指令

类似于 RUN 指令,CMD 指令也可以用于运行任何命令或应用程序,不过二者的运行时间点不同;

RUN 指令运行与镜像文件构建过程中,而 CMD 指令运行与基于 Dockerfile 构建出的新镜像文件启动一个容器时;

CMD 指令的首要目的在于为启动的容器指定默认要运行的程序,且其运行结束后,容器也将终止;不过,CMD 指定的命令其可以被 docker run 的命令行选项所覆盖;

在 Dockerfile 中可以存在多个CMD指令,但仅最后一个会生效;

语法格式:

CMD <command>或
CMD ["<executable>","<paraml>","<param2>"]或
CMD ["<paraml>","<param2>"]
# 前两种语法格式的意义同 RUN
# 第三种则用于为 ENTRYPOINT 指令提供默认参数;

举例演示:指定容器启动运行 nginx -g “daemon off;” 前台启动

CMD ["/usr/sbin/nginx","-g","daemon off;"]

ENTRYPOINT指令

类似 CMD 指令的功能,用于为容器指定默认运行程序,从而使得容器像是一个单独的可执行程序。

与 CMD 不同的是,有 ENTRYPOINT 启动的程序不会被 docker run 命令行指定的参数所覆盖,而且,这些命令行参数会被当做参数传递给 ENTRYPOINT 指令指定的程序;

不过 docker run 命令的 –entrypoint 选项的参数可覆盖 ENTRYPOINT 指令指定的程序;

语法格式:

ENTRYPOINT <command>
ENTRYPOINT ["<executable>","<paraml>","<param2>"]

docker run 命令传入的命令参数会覆盖 CMD 指令的内容并且附加到 ENTRYPOINT

Dockerfile 文件中也可以存在多个 ENTRYPOINT 指令,但仅最后一个会生效。

举例演示:下面我们来看一个 Dockerfile

# This dockerfile use the centos image
# VERSION 1
# Author: enzhige

FROM centos:7.4.1708
MAINTAINER enzhige "W_enzhi@163.com"

ENTRYPOINT ["echo"]
CMD ["hellodocker"]

假设通过该 Dockerfile 构建的镜像名为 enzhige/centos:v3。

1、当运行 docker run –name c1 enzhige/centos:v3 输出的内容是 hellodocker,可以看出 CMD 指令的参数是被添加到 ENTRYPOINT 指令的后面,当做参数然后被执行。

ENTRYPOINT指令示例1
ENTRYPOINT指令示例1

2、当运行 docker run –name c2 enzhige/centos:v3 hello word 输出的内容是 hello world ,可以看出 docker run 命令的参数得确是被添加到 ENTRYPOINT 指令的后面,当做参数然后被执行,这时 CMD 指令被覆盖了。

ENTRYPOINT指令示例2
ENTRYPOINT指令示例2

USER 指令

用于指定运行 image 时的或运行 Dockerfile 中任何 RUN、CMD 或 ENTRYPOINT 指令指定的程序时的用户名或 UID;
默认情况下,容器的运行身份为 root 用户;

语法格式:

USER <UID>|<UserName>
# 需要注意的是,<UID>可以为任意数字,但实践中其必须为/etc/passwd中某用户的有效UID,否则,docker run命令将运行失败;
# 多数情况下无需指定,因为容器中的root并非真root;

如果觉得文章不错,不妨给个『打赏』写作不易,各位的支持,能激发和鼓励我更大的写作热情。谢谢

原创文章,作者:恩志,如若转载,请注明出处:https://www.xbzdr.com/546.html

发表评论

电子邮件地址不会被公开。 必填项已用*标注

联系我们

在线咨询:点击这里给我发消息

邮件:510749025@qq.com

工作时间:周一至周五,9:30-18:30,节假日休息

QR code