Docker 学习篇(六)
1、数据卷容器
(1)数据卷容器和容器数据卷
在上一节,我们讲了何为容器数据卷,当我们使用 docker run -it -v /主机绝对路径目录:/容器内目录 镜像名,-v 就是创建一个容器的数据卷,它的作用是把容器内的数据持久化到宿主机上,当容器内数据发生变化,宿主机相应位置的数据也会发生变化。
有两个容器 dc01 和 dc02,我们使用 dc02 volume-from dc01 这个命令,就可以实现容器1和容器2之间的数据共享,类似于一种继承关系,数据卷容器就是被 volume-from 的那个容器,即 dc01。
(2)举个例子来讲解数据卷容器
1、运行我们创建的新镜像 zhouxuejiao/centos 生成一个容器,命名为 dc01,docker run -it --name dc01 zhouxuejiao/centos。
在 dc01 内部的容器数据卷 dataVolumeContainer2 中在增加一个文件 dc01_add.txt。
2、退出 dc01 容器,只退出,不停止运行,使用 Ctrl+P+Q。
3、让 dc02 和 dc03 分别继承 dc01,
docker run -it --name dc02 --volumes-from dc01 zhouxuejiao/centos
docker run -it --name dc03 --volumes-from dc01 zhouxuejiao/centos
此时 dc02 和 dc01 可以实现数据的共享,dc03 和 dc01 可以实现数据的共享,dc02 和 dc03 也可以实现数据的共享(因为 dc02 和 dc03 都继承自 dc01,dc01 相当于一个中介,所以 dc02 和 dc03 也可以实现数据的共享)。
4、此时提出一个问题,当我们删除父容器 dc01,dc02 和 dc03 还可以进行数据的互享吗?
答案是可以的,删除父容器 dc01 后,dc02 和 dc03 仍然存在且可以进行数据共享。这是因为容器之间信息的传递,数据卷的生命周期一直持续到没有容器使用它为止。
2、DockerFile
(1)DockerFile 是什么?
DockerFile 是用来构建 Docker 镜像的构建文件,是由一系列命令和参数构成的脚本。
(2)DockerFile 构建的三步骤
第一步:编写。手动编写一个 DockerFile 文件,必须符合 DockerFile 的规范。
第二步:构建。有了 DockerFile 文件后,直接执行 docker build DockerFile 这个命令,即可获得一个自定义的镜像。
第三步:运行。使用 docker run 运行上一步得到的镜像。
(3)DockerFile 文件长什么样?
以 centos 镜像的 DockerFile 文件为例,见图3,我们来看一下 DockerFile 文件长什么样。
scratch:所有镜像的父镜像,类似于Java中的Object是所有类的根父类。
MAINTAINER:作者及邮箱。
LABEL:说明。
(4)编写 DockerFile 的规范
(1)每条保留字指令都必须为大写字母,且后面跟随至少一个参数,以FROM scratch为例,FROM就是保留字指令,其后面必须跟一个参数,即scratch,否则会报错,dockerfile的语法有问题。
(2)指令按照从上到下,顺序执行。
(3)#表示注释。
(4)每条指令都会创建一个新的镜像层(之前讲过,镜像是分层的),并对镜像进行提交。
(5)实际开发部署场景中,DockerFile 是如何使用的?
首先开发人员将要部署的程序编写到 DockerFile 中,并将其 build 成一个镜像,然后开发人员将镜像交给运维人员,最后运维人员运行镜像,生成容器实例,此时程序就开始提供服务了。
(6)DockerFile 保留字指令
FROM:当前编写这个镜像是基于哪个镜像创建的,即它的父镜像是谁。
MAINTAINER:镜像维护者的姓名和邮箱。
RUN:容器构建过程中需要执行的一些命令,类似于java程序中的System.ou.println,java中是会打印到控制台,DockerFile 运行的时候,这些语句也会打印在命令行界面里。
EXPOSE:该镜像变成容器以后,对外暴漏的接口,比如 centos 的默认端口是 6379,见图4。
WORKDIR:运行镜像成为一个容器,登录容器终端直接进入到的目录。以下面的centos为例,容器终端直接进入的是容器的根目录/,见图5。
ENV:用来在构建镜像的过程中设置环境变量。比如
ENV MY_PATH /usr/mytest // 定义环境变量 MY_PATH,其值为 /usr/mytest
WORKDIR $MY_PATH // 引用上面定义的环境变量,引用时环境变量前面需要加上$,这句话等价于 WORKDIR /usr/mytest
ADD:拷贝文件或目录进镜像,ADD 命令不仅仅会拷贝,还会解压 tar 压缩包。以 centos 的 DockerFile 为例,它拷贝并解压 docker.tar 到镜像中。
COPY:类似ADD,拷贝文件和目录到镜像中,但是它只是拷贝,不会解压 tar包。COPY 将从构建上下文目录中<源路径>的文件复制到新的一层的镜像内的<目标路径>位置。它的使用方法有两种:COPY src test和COPY ["src","dest"]。
VOLUME:容器数据卷,用于数据保存和持久化工作。
CMD:指定一个容器启动时要运行的命令,比如 centos 中的 CMD ["/bin/bash"] 相当于 docker run -it centos /bin/bash。DockerFile 中可以有多个 CMD 指令,但只有最后一个生效,比如CMD ["/bin/bash"] CMD cat /etc/hosts,最后cat /etc/hosts 会覆盖 /bin/bash。
ENTRYPOINT:指定一个容器启动时要运行的命令,功能与 CMD 一样,和 CMD 不同的是,当有多条 ENTRYPOINT 指令时,不会出现覆盖,而是追加执行。
ONBUILD:父镜像在被子镜像继承的时候,父镜像 DockerFile 中的 ONBUIL 命令会被触发执行。
接下来我们将用下面的几个案例,对这几个保留字进行说明。
3、自定义镜像 mycentos
(1)、编写 DockerFile
阿里云上拉取下来的 centos,进入容器的默认路径是/,且不支持 vim 和 ifconfig 命令。
我们来自定义一个 mycentos 使其具备如下特性:进入容器的默认路径不再是根目录,而是 /usr/local 目录,支持vim编译器,支持ifconfig查看网络配置。
DockerFile如下:
FROM centos // 我们以本地的 centos 为父镜像,增加一些功能,形成新镜像 mycentos。
MAINTAINER zhoxuuejiao<happy_girl@163.com>
ENV MYPATH /usr/local
WORKDIR $MYPATH // 进入容器默认目录是 /usr/local
RUN yum -y install vim
RUN yum -y install net-tools // RUN 容器构建时执行的命令,所以我们让容器运行时,安装 vim 编译器、网络查看工具
EXPOSE 80 // 向外暴漏 80 端口
CMD echo $MYPATH
CMD echo "success.............ok" // 打印
CMD /bin/bash
(2)、根据 DockerFile 构建镜像,docker build -f /mydocker/Dockerfile -t mycentos .
构建过程的日志见图9至图15。
构建完成,使用 dcoker images 查看所有镜像,发现 mycentos 镜像的镜像 ID 和日志中(图15)的一样。
(3)、运行 mycentos 镜像,得到 mycentos 容器
此时,进入容器的默认路径是我们在 Dokcerfile 中指定的 /usr/local,见图17。
此时,在容器中可以使用 vim 编译器,见图18、19,vim test.txt,创建并编译 test.txt 这个文件。
此时,使用 ifconfig 查看网络信息,也是可以的,见图20。
至此,我们使用 DockerFile 创建 mycentos 镜像,创建成功。
(4)、查看镜像的创建历史。
docker histor 镜像ID,见图21,首先根据 centos 生成一个 base 镜像,然后每执行一句 DockerFile 中的指令,就会产生一个新的镜像,包裹在前面生成的镜像的外面,再次验证了镜像的分层结构。
4、DockerFile 中的 CMD 和 ENTRYPOINT 指令
通过上一个案例,我们大致了解了一部分保留字指令,但是对于 CMD 和 ENTRYPOINT 还不是很了解,现在我们通过一个案例来讲解一下这两个保留字。
(1)、二者的共同点
CMD 和 ENTRYPOINT,都是指定一个容器启动时要运行的命令。
(2)、二者的不同点
1、CMD:DockerFile 中可以有多个 CMD 指令,但只有最后一个生效,CMD 会被 docker run 之后的参数替换。
使用一个案例,来解释 “CMD 会被 docker run 之后的参数替换”,这句话是什么意思?
当我们执行 docker run -it -p 7777:8080 tomcat 这条命令启动 tomcat 时,tomcat 之所以能够正常启动,完全是因为其 DockerFile 最后一行的 CMD ["catalina.sh", run]。
如果我们改一下启动命令,docker run -it -p 7777:8080 tomcat ls -l,就相当于在 DockerFile 最后加了一条指令 CMD ls -l,它会覆盖掉 CMD ["catalina.sh", run],导致 tomcat 启动不起来。这就是 CMD 会被 docker run 之后的参数替换。
2、ENTRYPOINT:功能与 CMD 一样,和 CMD 不同的是,当有多条 ENTRYPOINT 指令时,不会出现覆盖,而是追加执行。docker run 之后的参数会被当做参数传给 ENTRYPOINT,之后形成新的命令组合。
使用一个案例来解释,“docker run 之后的参数会被当做参数传给 ENTRYPOINT,之后形成新的命令组合”,这句话是什么意思?
首先我们使用 CMD 编写 DockerFile 制作一个可以查询百度网站首页的容器。
FROM centos
RUN yum install -y curl // 下载 curl 工具,curl 可以用来访问网址,使用 curl http://www.baidu.com,即可获得百度网站首页的信息。
CMD ["curl", "-s", "https://www.baidu.com"] // crul https://www.baidu.com,访问百度网站首页。
然后根据 DockerFile 构建镜像,docker build -f /mydocker/Dockerfile1 -t mybaidu . 。
最后,运行这个就镜像,访问百度网站首页。docker run -it mybaidu。
现在我们想要不光返回百度首页信息,还希望显示 HTTP 请求的头信息,此时就需要加上 -i 参数。docker -it mybaidu -i,这样运行是会失败的,因为 DockerFile 用的是 CMD,加上这个 -i 参数,就相当于在 DockerFile 莫问加上了 CMD -i,它就把 CMD ["curl", "-s", "https://www.baidu.com"] 这最重要的一句覆盖了,自然失败了。
那应该怎么办呢?答案是使用 ENTRYPOINT,因为它不会覆盖,而是追加执行。来我们修改 Dockerfile。
FROM centos
RUN yum install -y curl
ENTRYPOINT ["curl", "-s", "https://www.baidu.com"]
构建镜像,docker build -f /mydocker/Dockerfile2 -t mybaidu2 . 。
运行这个镜像,访问百度网站首页,并且在运行命令后加上参数 -i,获取 HTTP 头信息。docker run -it mybaidu2 -i。这就相当于在 DockerFile 末尾增加一句 ENTRYPOINT -i,因为 ENTRYPOINT 追加执行,所以不会有任何影响。
(9)DockerFile 中的 ONBUILD 指令
ONBUILD 指令:父镜像在被子镜像继承的时候,父镜像 DockerFile 中的 ONBUIL 命令会被触发执行。
使用一个案例,讲解 ONBUILD 这个指令。
首先制作父镜像 mybaidu_father,DockerFile 如下:
FROM centos
RUN yum install -y curl
ENTRYPOINT ["curl", "-s", "https://www.baidu.com"]
ONBUILD RUN echo "father images onbuild--------666"
构建父镜像,docker build -f /mydocker/Dockerfile3 -t mybaidu_father .。
然后我们制作子镜像 mybaidu_son,DockerFile 如下:
FROM mybaidu_father // 指定其父镜像为 mybaidu_father
RUN yum install -y curl
ENTRYPOINT ["curl", "-s", "https://www.baidu.com"]
构建子镜像,docker build -f /mydocker/Dockerfile4 -t mybaidu_son .。
注意注意啦,当我们构建子镜像的时候,会触发父镜像中的 ONBUILD 指令,图38是构建子镜像的过程日志,红色框内就是在触发父镜像中的 ONBUILD 指令。