docker入门之dockerfile文件详解

史上最通俗易懂的dockerFile基础知识介绍,轻轻松松从入门到精通

Posted by yishuifengxiao on 2019-03-03

一 DockerFile 内容基础知识

Dockerfile 是用来构建 Docker 镜像的构建文件,是由一系列命令和参数构成的脚本。

编写 dockerFile 的步骤

  1. 编写 DockerFile 文件
  2. 执行 docker build 命令,获得一个自定义的镜像
  3. 执行 docker run ,运行生成的镜像

centos 官方 dockerFile 文件内容如下:

1
2
3
4
5
6
7
8
9
10
FROM scratch
ADD centos-7-docker.tar.xz /

LABEL org.label-schema.schema-version="1.0" \
org.label-schema.name="CentOS Base Image" \
org.label-schema.vendor="CentOS" \
org.label-schema.license="GPLv2" \
org.label-schema.build-date="20181205"

CMD ["/bin/bash"]
  1. 每条保留字指令都必须为大写字母且后面要跟随至少一个参数
  2. 指令按照从上到下,顺序执行
  3. 表示注释

  4. 每条指令都会创建一个新的镜像层,并对镜像进行提交

1.1 Docker 执行 Dockerfile 的大致流程

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

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

  • Dockerfile 是软件的原材料

  • Docker 镜像是软件的交付品

  • Docker 容器则可以认为是软件的运行态。

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

  • Dockerfile,需要定义一个 Dockerfile,Dockerfile 定义了进程需要的一切东西。Dockerfile 涉及的内容包括执行代码或者是文件、环境变量、依赖包、运行时环境、动态链接库、操作系统的发行版、服务进程和内核进程(当应用进程需要和系统服务和内核进程打交道,这时需要考虑如何设计 namespace 的权限控制)等等;
  • Docker 镜像,在用 Dockerfile 定义一个文件之后,docker build 时会产生一个 Docker 镜像,当运行 Docker 镜像时,会真正开始提供服务;
  • Docker 容器,容器是直接提供服务的。

1.2 docker 的体系结构


  • ADD: 将宿主机目录下的文件拷贝进镜像且ADD命令会自动处理URL和解压tar压缩包
  • CMD: 指定一个容器启动时要运行的命令,Dockerfile 中可以有多个 CMD 指令,但只有最后一个生效,CMD 会被 docker run 之后的参数替换
  • COPY: 类似ADD,拷贝文件和目录到镜像中。将从构建上下文目录中 <源路径> 的文件/目录复制到新的一层的镜像内的 <目标路径> 位置(COPY src dest 或者 COPY [“src”,”dest”])
  • ENTRYPOINT: 指定一个容器启动时要运行的命令,ENTRYPOINT的目的和 CMD 一样,都是在指定容器启动程序及参数
  • ENV: 用来在构建镜像过程中设置环境变量
  • EXPOSE: 当前容器对外暴露出的端口
  • FROM : 基础镜像,当前新镜像是基于哪个镜像的
  • HEALTHCHECH- 健康检测指令
  • MAINTAINER : 镜像维护者的姓名和邮箱地址
  • ONBUILD: 当构建一个被继承的Dockerfile时运行命令,父镜像在被子继承后父镜像的onbuild被触发
  • RUN: 容器构建时需要运行的命令
  • USER: 指定后续执行的用户组和用户
  • VOLUME: 容器数据卷,用于数据保存和持久化工作
  • WORKDIR: 指定在创建容器后,终端默认登陆的进来工作目录,一个落脚点。为 RUN、CMD、ENTRYPOINT、COPY 和 ADD 设置工作目录,就是切换目录

注意

1
ENV MY_PATH /usr/mytest

这个环境变量可以在后续的任何 RUN 指令中使用,这就如同在命令前面指定了环境变量前缀一样;也可以在其它指令中直接使用这些环境变量,

比如:WORKDIR \$MY_PATH

二 DockerFile 命令详解

Base 镜像(scratch) : Docker Hub 中 99% 的镜像都是通过在 base 镜像中安装和配置需要的软件构建出来的

2.1 CMD 命令

CMD类似于 RUN 指令,用于运行程序,但二者运行的时间点不同:

  • CMD 在docker run 时运行。

  • RUN 是在 docker build。

Dockerfile 中可以有多个 CMD 指令,但只有最后一个生效,CMD 会被 docker run 之后的参数替换

1
2
3
CMD <shell 命令> 
CMD ["<可执行文件或命令>","<param1>","<param2>",...]
CMD ["<param1>","<param2>",...] # 该写法是为 ENTRYPOINT 指令指定的程序提供默认参数

例如我们在启动 tomcat 镜像时,执行一下命令

1
docker run -it -p 8888:8080 tomcat ls -l

上述命令会覆盖 tomcat 的 dockerFile 文件最后的启动定义命令

1
CMD ["catalina.sh", "run"]

因此会导致 tomcat 的 Docker 镜像无法启动里面的 tomcat


2.2 ENTRYPOINT 命令

类似于 CMD 指令,但其不会被 docker run 的命令行参数指定的指令所覆盖,而且这些命令行参数会被当作参数送给 ENTRYPOINT 指令指定的程序

但是 如果运行 docker run 时使用了 —entrypoint 选项,将覆盖 ENTRYPOINT 指令指定的程序。

语法格式:

1
ENTRYPOINT ["<executeable>","<param1>","<param2>",...]

ENTRYPOINT 可以搭配 CMD 命令使用:一般是变参才会使用 CMD ,这里的 CMD 等于是在给 ENTRYPOINT 传参

docker run 之后的参数会被当做参数传递给 ENTRYPOINT,之后形成新的命令组合

2.2.1 示例一

例如假设已通过 Dockerfile 构建了 nginx:test 镜像:

1
2
3
4
FROM nginx

ENTRYPOINT ["nginx", "-c"] # 定参
CMD ["/etc/nginx/nginx.conf"] # 变参

1、不传参运行

1
$ docker run  nginx:test

容器内会默认运行以下命令,启动主进程。

1
nginx -c /etc/nginx/nginx.conf

2、传参运行

1
$ docker run  nginx:test -c /etc/nginx/new.conf

容器内会默认运行以下命令,启动主进程(/etc/nginx/new.conf:假设容器内已有此文件)

1
nginx -c /etc/nginx/new.conf

2.2.2 示例二

制作一个查询 IP 的镜像

DockerFile 文件的定义如下:

1
2
3
FROM centos
RUN yum install -y curl
CMD [ "curl", "-s", "http://ip.cn" ]

如果我们希望显示 HTTP 头信息,就需要运行改容器镜像时加上-i参数,我们可以看到可执行文件找不到的报错,executable file not found。之前说过,跟在镜像名后面的是 command,运行时会替换 CMD 的默认值。因此这里的-i替换了原来的 CMD,而不是添加在原来的curl -s http://ip.cn 后面。而-i根本不是命令,所以自然找不到。那么如果我们希望加入-i这参数,可以将 CMD 替换为

新的 DockerFIle 文件的定义如下:

1
2
3
4
5
FROM centos

RUN yum install -y curl

ENTRYPOINT [ "curl", "-s", "http://ip.cn" ]

此时,重新完整的输入这个命令:

1
docker run myip curl -s http://ip.cn -i

可以看到,输出了想要的结果信息

2.3 ONBUILD 命令

用于延迟构建命令的执行。简单的说,就是 Dockerfile 里用 ONBUILD 指定的命令,在本次构建镜像的过程中不会执行(假设镜像为 test-build)。当有新的 Dockerfile 使用了之前构建的镜像 FROM test-build ,这时执行新镜像的 Dockerfile 构建时候,会执行 test-build 的 Dockerfile 里的 ONBUILD 指定的命令。

语法格式:

1
ONBUILD <其它指令>

制作一个包含 onbuild 的镜像,其 DockerFIle 文件内容如下:

1
2
3
4
5
6
7
FROM centos

RUN yum install -y curl

ENTRYPOINT [ "curl", "-s", "http://ip.cn" ]

ONBUILD run echo "我被继承了"

2.4 RUN命令

CMD类似于 RUN 指令,用于运行程序,但二者运行的时间点不同:

  • CMD 在docker run 时运行。
  • RUN 是在 docker build。

RUN:用于执行后面跟着的命令行命令。有以下俩种格式:

shell 格式:

1
2
RUN <命令行命令>
# <命令行命令> 等同于,在终端操作的 shell 命令。

exec 格式:

1
2
3
RUN ["可执行文件", "参数1", "参数2"]
# 例如:
# RUN ["./test.php", "dev", "offline"] 等价于 RUN ./test.php dev offline

注意:Dockerfile 的指令每执行一次都会在 docker 上新建一层。所以过多无意义的层,会造成镜像膨胀过大。例如:

1
2
3
4
FROM centos
RUN yum -y install wget
RUN wget -O redis.tar.gz "http://download.redis.io/releases/redis-5.0.3.tar.gz"
RUN tar -xvf redis.tar.gz

以上执行会创建 3 层镜像。可简化为以下格式:

1
2
3
4
FROM centos
RUN yum -y install wget \
&& wget -O redis.tar.gz "http://download.redis.io/releases/redis-5.0.3.tar.gz" \
&& tar -xvf redis.tar.gz

如上,以 && 符号连接命令,这样执行后,只会创建 1 层镜像。

2.5 COPY命令

复制指令,从上下文目录中复制文件或者目录到容器里指定路径。

语法格式:

1
2
COPY [--chown=<user>:<group>] <源路径1>...  <目标路径>
COPY [--chown=<user>:<group>] ["<源路径1>",... "<目标路径>"]

[--chown=<user>:<group>]:可选参数,用户改变复制到容器内文件的拥有者和属组。

<源路径>:源文件或者源目录,这里可以是通配符表达式,其通配符规则要满足 Go 的 filepath.Match 规则。例如:

1
2
COPY hom* /mydir/
COPY hom?.txt /mydir/

<目标路径>:容器内的指定路径,该路径不用事先建好,路径不存在的话,会自动创建

2.6 ADD命令

ADD 指令和 COPY 的使用格类似(同样需求下,官方推荐使用 COPY)。功能也类似,不同之处如下:

  • ADD 的优点:在执行 <源文件> 为 tar 压缩文件的话,压缩格式为 gzip, bzip2 以及 xz 的情况下,会自动复制并解压到 <目标路径>
  • ADD 的缺点:在不解压的前提下,无法复制 tar 压缩文件。会令镜像构建缓存失效,从而可能会令镜像构建变得比较缓慢。具体是否使用,可以根据是否需要自动解压来决定

2.7 ENV命令

设置环境变量,定义了环境变量,那么在后续的指令中,就可以使用这个环境变量。

语法格式:

1
2
ENV <key> <value>
ENV <key1>=<value1> <key2>=<value2>...

以下示例设置 NODE_VERSION = 7.2.0 , 在后续的指令中可以通过 $NODE_VERSION 引用:

1
2
3
4
ENV NODE_VERSION 7.2.0

RUN curl -SLO "https://nodejs.org/dist/v$NODE_VERSION/node-v$NODE_VERSION-linux-x64.tar.xz" \
&& curl -SLO "https://nodejs.org/dist/v$NODE_VERSION/SHASUMS256.txt.asc"

2.8 ARG命令

构建参数,与 ENV 作用一致。不过作用域不一样。ARG 设置的环境变量仅对 Dockerfile 内有效,也就是说只有 docker build 的过程中有效,构建好的镜像内不存在此环境变量。

构建命令 docker build 中可以用 —build-arg <参数名>=<值> 来覆盖。

语法格式:

1
ARG <参数名>[=<默认值>]

2.9 VOLUME命令

定义匿名数据卷。在启动容器时忘记挂载数据卷,会自动挂载到匿名卷。

作用:

  • 避免重要的数据,因容器重启而丢失,这是非常致命的。
  • 避免容器不断变大。

语法格式:

1
2
VOLUME ["<路径1>", "<路径2>"...]
VOLUME <路径>

在启动容器 docker run 的时候,我们可以通过 -v 参数修改挂载点。

2.10 EXPOSE

仅仅只是声明端口。

作用:

  • 帮助镜像使用者理解这个镜像服务的守护端口,以方便配置映射。
  • 在运行时使用随机端口映射时,也就是 docker run -P 时,会自动随机映射 EXPOSE 的端口。

格式:

1
EXPOSE <端口1> [<端口2>...]

2.11 WORKDIR

指定工作目录。用 WORKDIR 指定的工作目录,会在构建镜像的每一层中都存在。以后各层的当前目录就被改为指定的目录,如该目录不存在,WORKDIR 会帮你建立目录。

docker build 构建镜像过程中的,每一个 RUN 命令都是新建的一层。只有通过 WORKDIR 创建的目录才会一直存在。

格式:

1
WORKDIR <工作目录路径>

2.12 USER

用于指定执行后续命令的用户和用户组,这边只是切换后续命令执行的用户(用户和用户组必须提前已经存在)。

格式:

1
USER <用户名>[:<用户组>]

2.13 HEALTHCHECK

用于指定某个程序或者指令来监控 docker 容器服务的运行状态。

格式:

1
2
3
4
HEALTHCHECK [选项] CMD <命令>:设置检查容器健康状况的命令
HEALTHCHECK NONE:如果基础镜像有健康检查指令,使用这行可以屏蔽掉其健康检查指令

HEALTHCHECK [选项] CMD <命令> : 这边 CMD 后面跟随的命令使用,可以参考 CMD 的用法。

三 构建自定义tomcat镜像

这里演示自定义 tomcat的docker镜像

  1. 在/docker_images 目录下创建一个名为 c.txt 的文本文件
  2. 将 tomcat 和 jdk 的安装包拷贝到上述目录
  3. 在该目录下创建一个 DockerFIle 文件

    DockerFIle 文件内容如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
FROM         centos
MAINTAINER yishui<zhiyubujian@163.com>
#把宿主机当前上下文的c.txt拷贝到容器/usr/local/路径下
COPY c.txt /usr/local/cincontainer.txt
#把java与tomcat添加到容器中,并解压
ADD jdk-8u144-linux-x64.tar.gz /usr/local/
ADD apache-tomcat-9.0.16.tar.gz /usr/local/
#安装vim编辑器
RUN yum -y install vim
#设置工作访问时候的WORKDIR路径,登录落脚点
ENV MYPATH /usr/local
WORKDIR $MYPATH
#配置java与tomcat环境变量
ENV JAVA_HOME /usr/local/jdk1.8.0_144
ENV CLASSPATH $JAVA_HOME/lib/dt.jar:$JAVA_HOME/lib/tools.jar
ENV CATALINA_HOME /usr/local/apache-tomcat-9.0.16
ENV CATALINA_BASE /usr/local/apache-tomcat-9.0.16
ENV PATH $PATH:$JAVA_HOME/bin:$CATALINA_HOME/lib:$CATALINA_HOME/bin
#容器运行时监听的端口
EXPOSE 8080
#启动时运行tomcat
# ENTRYPOINT ["/usr/local/apache-tomcat-9.0.16/bin/startup.sh" ]
# CMD ["/usr/local/apache-tomcat-9.0.16/bin/catalina.sh","run"]
CMD /usr/local/apache-tomcat-9.0.16/bin/startup.sh && tail -F /usr/local/apache-tomcat-9.0.16/bin/logs/catalina.out

此时,文件的目录内容如下:

1
2
3
4
5
6
7
8
yishui@yishui:~/桌面/docker_images$ pwd
/home/yishui/桌面/docker_images

yishui@yishui:~/桌面/docker_images$ ls
apache-tomcat-9.0.16.tar.gz Dockerfile jdk-8u144-linux-x64.tar.gz
c.txt jdk1.8.0_144

yishui@yishui:~/桌面/docker_images$

3.1 构建命令

1
docker build -t yishui_tomcat .

或者

1
docker build -f Dockerfile -t yishui_tomcat .

注意: 因为这里 DockerFile 文件的名字为 Dockerfile,所以可以的省略-f 命令

执行命令,得到的结果如下:

yishui@yishui:~/桌面/docker_images$ docker build -t yishui_tomcat
"docker build" requires exactly 1 argument.
See 'docker build --help'.

Usage:  docker build [OPTIONS] PATH | URL | -

Build an image from a Dockerfile
yishui@yishui:~/桌面/docker_images$ docker build -t yishui_tomcat .
Sending build context to Docker daemon  573.8MB
Step 1/15 : FROM         centos
 ---> 1e1148e4cc2c
Step 2/15 : MAINTAINER    yishui<zhiyubujian@163.com>

注意 命令的最后一个点,一定要加,否则会出问题

3.2 查看是否有镜像生成

yishui@yishui:~/桌面/docker_images$ docker images
REPOSITORY           TAG                 IMAGE ID            CREATED             SIZE
yishui_tomcat        latest              b359862a31e7        2 minutes ago       726MB
yishui/tomcat-demo   1.0                 1323b27a2652        2 days ago          463MB
<none>               <none>              7cfe70fc34b0        2 days ago          463MB
tomcat               latest              168588387c68        3 weeks ago         463MB
centos               latest              1e1148e4cc2c        2 months ago        202MB

3.3 运行构建的镜像

yishui@yishui:~/桌面/docker_images$ docker run -it -p 8000:8080 b359862a31e7
Using CATALINA_BASE:   /usr/local/apache-tomcat-9.0.16
Using CATALINA_HOME:   /usr/local/apache-tomcat-9.0.16
Using CATALINA_TMPDIR: /usr/local/apache-tomcat-9.0.16/temp
Using JRE_HOME:        /usr/local/jdk1.8.0_144
Using CLASSPATH:       /usr/local/apache-tomcat-9.0.16/bin/bootstrap.jar:/usr/local/apache-tomcat-9.0.16/bin/tomcat-juli.jar
Tomcat started.
tail: cannot open '/usr/local/apache-tomcat-9.0.8/bin/logs/catalina.out' for reading: No such file or directory
tail: cannot watch parent directory of '/usr/local/apache-tomcat-9.0.8/bin/logs/catalina.out': No such file or directory
tail: inotify cannot be used, reverting to polling

通过http://localhost:8000/ 访问,发现可以看到 tomcat 管理界面,镜像构建成功

或者运行以下命令

docker run -d -p 9080:8080 —name myt9 -v /demo/tomcat9/test:/usr/local/apache-tomcat-9.0.16/webapps/test -v /demo/tomcat9/tomcat9logs/:/usr/local/apache-tomcat-9.0.16/logs —privileged=true b359862a31e7

注意     -v   该命令表示挂载容器卷

Docker 挂载主机目录 Docker 访问出现 cannot open directory .: Permission denied

解决办法:在挂载目录后多加一个—privileged=true 参数即可

四 自定义 centos

4.1. 编写 DockerFile 文件

先查看官方文件,得知,自定义镜像具备以下情况

  • ​ 登陆后的默认路径
  • ​ vim编辑器
  • ​ 查看网络配置ifconfig支持

编写好自定义的 DockerFile 文件,内容如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
FROM centos
MAINTAINER yishui<zhiyubujian@163.com>

ENV MYPATH /usr/local
WORKDIR $MYPATH

RUN yum -y install vim
RUN yum -y install net-tools

EXPOSE 80

CMD echo $MYPATH
CMD echo "success--------------ok"
CMD /bin/bash

4.2. 构建 Docker 镜像

1
docker build -t 新镜像的名字:TAG .

注意 上述命令最后的 . 表示的是当前路径

具体的命令如下:

1
docker build -f /demo/myDockerFile -t mycentos/centos:1.0 .

4.3. 查询是否构建成功

使用 docker images 命令查询是否构建成功

1
docker images

4.4. 运行构建的镜像

1
docker run -it mycentos/centos:1.0

4.5. 列出镜像的变更历史

1
docker history 镜像名