Dockerfile
一. Dockerfile是什么
Dockerfile是docker中镜像文件的的描述文件,说的直白点就是镜像文件到底是由什么东西一步步构成的。例如我们在淘宝上买了一件商品,但是这个商品需要组装才能使用,于是卖家就给了你一张图纸,你就按照图纸一步一步的组装起来,然后就成了你所需要的样子。那么Dockerfile就是这张图纸,镜像文件就是你需要的商品。Dockerfile名字可以随便命名,但是不建议你这做,还是按照规范来使用,首字母要大写。下面给出我们前几个章节使用到的centos为例,来看看它的Dockerfile是怎么样的,如下图所示:
二. Dockerfile、镜像、容器
Dockerfile:是镜像的构建文件,描述的镜像是一步步怎么来的。
镜像:是通过Dockerfile做出来的,包含操作系统基础文件和软件运行环境,它使用分层的存储方式。
容器:是运行起来的镜像,简单理解,Docker镜像相当于程序,容器相当于进程。
三. Dockerfile的基本语法及执行流程
3.1 Dockerfile的基本语法
a.每个保留字必须放在每一行的开头,可以大写也可以小写,但是建议大写;
b.指令按照顺序,从上往下依次执行;
c.#表示注解;
d.每条执行指令都会创建一个新的镜像,并且执行提交。
3.2 Dockerfile的执行流程
a.docker从基础镜像中执行一个容器;
b.执行一条指令对镜像进行修改;
c.执行docker commit命令,提交新的镜像;
d.在基于刚刚提交的镜像运行一个新的容器;
e.执行Dockerfile中的下一条指令,按照b、c、d依次循环下去,知道所有的指令执行完毕。
3.3 演示讲解
FROM centos #指定要生成的镜像的基础镜像,开头第一句话必须也只能是FROM MAINTAINER zhengjingmao@163.com #指定作者是谁 RUN yum install -y vim #执行 yum install -y vim 命令,安装vim RUN yum install -y net-tools #执行 yum install -y net-tools, 安装net-tools工具
WORKDIR /dev/ #启动容器后,如果启动交互模式,直接进入到哪个目录 CMD ["/bin/bash"] #启动容器的时候,进入到/bin/bash这种命令行
如上代码所示,FROM、MAINTAINER、RUN、WORKDIR、CMD均为关键字,按照标准的规范需要大写;FROM centos表示我们的基础镜像是centos,然后会启动centos这个容器,执行第二行代码又会生成新的镜像文件,然后提交,周而复始。
四. Dockerfile关键字
关键字 | 作用 |
FROM | 指定基础镜像 |
MAINTAINER | 作者的信息 |
RUN | 执行什么命令 |
EXPOSE | 容器对外暴露的端口 |
WORKDIR | 进入到容器后进入到哪个目录 |
ENV | 配置环境变量 |
ADD | 将文件拷贝到镜像中并解压 |
COPY | 将文件拷贝到镜像中 |
VOLUME | 配置数据卷 |
CMD | 容器启动时候执行的命令 |
ENTRYPOINT | 容器启动时候执行的命令 |
a.ADD指令,我们现在定义这样一个Dockerfile,代码如下所示:
FROM centos MAINTAINER zhengjingmao@163.com RUN mkdir /datas ADD jdk-8u60-linux-x64.tar.gz /datas/ WORKDIR /datas/ CMD ["/bin/bash"]
首先必须将jdk-8u60-linux-x64.tar.gz文件拷贝到Dockerfile同级目录下,如下图所示:
执行命令:docker build -t mycentos . 构建mycentos镜像,如下图所示:
然后启动容器,进入到 /datas/目录下,会发现 jdk1.8.0_60 目录,表示add命令还执行了解压命令,如下图所示:
b.COPY命令,我们定义一个Dockerfile, 代码如下:
FROM centos MAINTAINER zhengjingmao@163.com RUN mkdir /datas ADD jdk-8u60-linux-x64.tar.gz /datas/ WORKDIR /datas/ CMD ["/bin/bash"]
执行命令:docker build -t mycentos . 构建mycentos镜像。
然后启动容器,进入到 /datas/目录下,会看到jdk-8u60-linux-x64.tar.gz文件,并没有解压,如下图所示:
c.ENV命令,配置环境变量,我们还是用上面提到的jdk为例,做过Java开发的朋友都知道,jdk需要配置环境变量,Dockerfile的内容如下所示:
FROM centos MAINTAINER zhengjingmao@163.com RUN mkdir -p /datas/ ADD jdk-8u60-linux-x64.tar.gz /datas/ ENV JAVA_HOME=/datas/jdk1.8.0_60 #配置JAVA_HOME ENV PATH=$JAVA_HOME/bin:$PATH #配置PATH CMD ["/bin/bash"]
因为内容的重复性,在这里就不赘述了。当我们容器启动后,我们输入:java -version命令,就能看到我们熟悉的内容了。
d.CMD关键字,在镜像构建阶段不执行,在容器启动阶段执行(而我们的RUN关键字定义的指令在容器构建阶段就会执行,请记住它与CMD的区别)。如果一个Dockerfile中有多个CMD命令,后面的会覆盖前面的,说白了只有最后一个生效,如下代码和注释:
............省略............ CMD echo "<<<<<<<<<<<nice to meet you>>>>>>>>>>" CMD /bin/bash CMD echo "==========How are you?=============" #当容器启动的时候只有该行代码会执行,会将前两行代码覆盖
CMD还有一个问题,就是当我们使用 docker run命令的时候,我们可以在整个docker命令的最后加上其他额外的命令,那么额外的命令会覆盖Dockerfile中所有的CMD命令,例如我们执行如下命令:docker run -it centos ls / -l,最终的结果如下图所示:
e. ENTRYPOINT指令,和CMD命令差不多,如果一个Dockerfile中有多个ENTRYPOINT,只有最后一个生效。但是他们还是有区别的,如果ENTRYPOINT后面有CMD,当以exec的方式运行的时候,CMD的值会作为参数给ENTRYPOINT,可能很多人看到这句话不大理解,那么我们来两个例子:
例一:
FROM centos MAINTAINER zhengjingmao@163.com RUN mkdir -p /datas/ ENTRYPOINT ["echo", "hello"] CMD ["world"] #会将world作为echo hello的参数,最后的命令其实为echo hello world
例二:
FROM centos MAINTAINER zhengjingmao@163.com RUN mkdir -p /datas/ ENTRYPOINT ["echo", "hello"]
总结:当我们理解了CMD和ENTRYPOINT两个命令的区别后,以后在使用的过程中就不会出现各种问题了。