通过Dockerfile制作一个属于自己的镜像!

从前面的文章中我们看到,无论是启动一个 或者 ,都是基于一个镜像然后启动一个容器就将服务构建好了,我们不仅对镜像产生很大兴趣,为什么能这么方便?所以这篇文章我们来看看镜像时怎么生成,同时基于 中生成的helloworld二进制文件去制作一个helloworld的镜像!


1.Nginx的Dockerfile

在介绍Dockerfile之前,我们先看一个例子,看看Nginx的Dockerfile是怎么写的,然后我们在介绍Dockerfile中各个命令的作用是什么意思。如下图:

通过Dockerfile制作一个属于自己的镜像!

上面就是生成一个Nginx的镜像的Dockerfile,看起来内容很少吧?但是从上面可以看到最前面大写标颜色的字符,如FROM、MAINTAINER、ENV等等,这些究竟是什么意思呢?这就是我们接下来要介绍的Dockerfile文件详解。


2.Dockerfile文件详解

Dockerfile包含创建镜像所需要的全部指令。上面最前面大写标颜色的字符都是Dockerfile的指令。下面介绍Dockerfile中各个指令的用法以及意思。指令不区分大小写。但是,命名约定为全部大写。

1)FROM

用法:FROM [:]

解释:制作的镜像是基于哪个镜像,FROM指令必须是整个Dockerfile的第一个指令,如果指定的镜像本地不存在时默认会自动从Docker Hub上下载。FROM在一个Dockerfile里面可以有多个。


2)MAINTAINER

用法:MAINTAINER

解释:MAINTAINER指令允许你给将要制作的镜像设置作者信息,程序员都推崇开源,但是很多时候都是这样的:东西你可以免费用,但是你要承认这玩意是我弄出来的,所以这个作者的信息一般都会有。


3)RUN

用法:

①RUN #将会调用/bin/sh -c

②RUN ["executable", "param1", "param2"] # 将会调用exec执行,以避免有些时候shell方式执行时的传递参数问题,而且有些基础镜像可能不包含/bin/sh

解释:RUN指令会在一个新的容器中执行任何命令,然后把执行后的改变提交到当前镜像,提交后的镜像会被用于Dockerfile中定义的下一步操作,RUN中定义的命令会按顺序执行并提交,这正是Dockerfile的提交和可以基于镜像的任何一个历史点创建容器的好处,就像版本控制工具一样。


4)CMD

用法:

①CMD ["executable", "param1", "param2"] # 将会调用exec执行,首选方式

②CMD ["param1", "param2"] #当使用ENTRYPOINT指令时,为该指令传递默认参数

③CMD [ | ] #将会调用/bin/sh -c执行

解释:CMD指令中指定的命令会在镜像运行时执行,在Dockerfile中只能存在一个,如果使用了多个CMD指令,则只有最后一个CMD指令有效。当出现ENTRYPOINT指令时,CMD中定义的内容会作为ENTRYPOINT指令的默认参数,也就是说可以使用CMD指令给ENTRYPOINT传递参数。

这里我们看到了RUN和CMD都是执行命令,那它们有什么差异呢?其实RUN中定义的命令会在执行docker build命令创建镜像时执行,而CMD中定义的命令会在执行docker run命令运行镜像时执行,另外使用第一种语法也就是调用exec执行时,命令必须为绝对路径。


5)EXPOSE

语法:EXPOSE [ ...]

解释:EXPOSE指令用来告诉Docker这个容器在运行时会监听哪些端口,Docker在连接不同的容器(使用–link参数)时使用这些信息。这些端口一般都是服务端口,比如之前的MySQL的3306端口。


6)ENV

用法:ENV

解释:ENV指令用于设置环境变量,在Dockerfile中这些设置的环境变量也会影响到RUN指令,当运行生成的镜像时这些环境变量依然有效,如果需要在运行时更改这些环境变量可以在运行docker run时添加–env =参数来修改。

注意:最好不要定义那些可能和系统预定义的环境变量冲突的名字,否则可能会产生意想不到的结果。


7)ADD

用法:ADD

解释:ADD指令用于从指定路径拷贝一个文件或目录到容器的指定路径中,是一个文件或目录的路径,也可以是一个url,路径是相对于该Dockerfile文件所在位置的相对路径,是目标容器的一个绝对路径,例如/home/yunzhongmuge/Docker/Dockerfile这个文件中定义的,那么ADD /data.txt /db/指令将会尝试拷贝文件从/home/yunzhongmuge/Docker/data.txt到将要生成的容器的/db/data.txt,且文件或目录的属组和属主分别为uid和gid为0的用户和组,如果是通过url方式获取的文件,则权限是600。

注意:

①如果执行docker build – < somefile即通过标准输入来创建时,ADD指令只支持url方式,另外如果url需要认证,则可以通过RUN wget …或RUN curl …来完成,ADD指令不支持认证。

路径必须与Dockerfile在同级目录或子目录中,例如不能使用ADD ../somepath,因为在执行docker build时首先做的就是把Dockerfile所在目录包含子目录发送给docker的守护进程。

③如果是一个url且不是以”/“结尾,则会下载文件并重命名为

④如果是一个url且以“/”结尾,则会下载文件到/,url必须是一个正常的路径形式,“http://example.com”像这样的url是不能正常工作的。

⑤如果是一个本地的压缩包且

是以“/”结尾的目录,则会调用“tar -x”命令解压缩,如果有同名文件则覆盖,但是一个url时不会执行解压缩。


8)COPY

用法:COPY

解释:用法与ADD相同,不过它的不支持使用url,所以在使用docker build – < somefile时该指令不能使用。


9)ENTRYPOINT

用法:

①ENTRYPOINT ["executable", "param1", "param2"] #将会调用exec执行,首选方式

②ENTRYPOINT command param1 param2 #将会调用/bin/sh -c执行

解释:ENTRYPOINT指令中指定的命令会在镜像运行时执行,在Dockerfile中只能存在一个,如果使用了多个ENTRYPOINT指令,则只有最后一个指令有效。ENTRYPOINT指令中指定的命令(exec执行的方式)可以通过docker run来传递参数,例如docker run -l启动的容器将会把-l参数传递给ENTRYPOINT指令定义的命令并会覆盖CMD指令中定义的默认参数(如果有的话),但不会覆盖该指令定义的参数,例如ENTRYPOINT ["ls","-a"],CMD ["/etc"],当通过docker run

启动容器时该容器会运行ls -a /etc命令,当使用docker run -l启动时该容器会运行ls -a -l命令,-l参数会覆盖CMD指令中定义的/etc参数。

注意:

①当使用ENTRYPOINT指令时生成的镜像运行时只会执行该指令指定的命令。

②当出现ENTRYPOINT指令时CMD指令只可能(当ENTRYPOINT指令使用exec方式执行时)被当做ENTRYPOINT指令的参数使用,其他情况则会被忽略。


10)VOLUME

用法:VOLUME ["samepath"]

解释:VOLUME指令用来设置一个挂载点,可以用来让其他容器挂载以实现数据共享或对容器数据的备份、恢复或迁移。这个非常有用,可以保持重要数据不丢失,在容器被删掉的情况,只要挂载的卷的目录是一样的就可以将原来的数据同步到新的容器。


11)USER

用法:USER [username|uid]

解释:USER指令用于设置用户或uid来运行生成的镜像和执行RUN指令。


12)WORKDIR

用法:WORKDIR /path/to/workdir

解释:WORKDIR指令用于设置Dockerfile中的RUN、CMD和ENTRYPOINT指令执行命令的工作目录(默认为/目录),该指令在Dockerfile文件中可以出现多次,如果使用相对路径则为相对于WORKDIR上一次的值,例如WORKDIR /data,WORKDIR logs,RUN pwd最终输出的当前目录是/data/logs。


13)ONBUILD

用法:ONBUILD [INSTRUCTION]

解释:ONBUILD指令用来设置一些触发的指令,用于在当该镜像被作为基础镜像来创建其他镜像时(也就是Dockerfile中的FROM为当前镜像时)执行一些操作,ONBUILD中定义的指令会在用于生成其他镜像的Dockerfile文件的FROM指令之后被执行,上述介绍的任何一个指令都可以用于ONBUILD指令,可以用来执行一些因为环境而变化的操作,使镜像更加通用。

注意:

①ONBUILD中定义的指令在当前镜像的build中不会被执行。

②可以通过查看docker inspeat 命令执行结果的OnBuild键来查看某个镜像ONBUILD指令定义的内容。

③ONBUILD中定义的指令会当做引用该镜像的Dockerfile文件的FROM指令的一部分来执行,执行顺序会按ONBUILD定义的先后顺序执行,如果ONBUILD中定义的任何一个指令运行失败,则会使FROM指令中断并导致整个build失败,当所有的ONBUILD中定义的指令成功完成后,会按正常顺序继续执行build。

④ONBUILD中定义的指令不会继承到当前引用的镜像中,也就是当引用ONBUILD的镜像创建完成后将会清除所有引用的ONBUILD指令。

⑤ONBUILD指令不允许嵌套,例如ONBUILD ONBUILD ADD . /data是不允许的。

⑥ONBUILD指令不会执行其定义的FROM或MAINTAINER指令。


3.编写一个helloworld的Dockerfile

通过上面的这么啰里啰嗦的介绍,相信大家对Dockerfile应该会有一个大概的理解,下面我们来写个最简单的helloworld的Dockerfile,就是把我们在前面 这篇文章中编译的好二进制文件放到镜像里面,然后容器启动时就执行一下这个二进制。下面我们来实战一下。

1)先在home目录创建一个helloworld目录

mkdir -p /home/docker-images/helloworld

2)在这个目录新建一个Dockerfile文件

touch /home/docker-images/helloworld/Dockerfile

3)将二进制helloWord复制到helloworld这个目录

cp /home

4)在Dockerfile里面输入如下信息:

FROM scratch

MAINTAINER yunzhongmuge "[email protected]"

COPY helloworld /

CMD ["/helloworld"]

上面的步骤操作如下图:

通过Dockerfile制作一个属于自己的镜像!

这里介绍一个这个基础镜像scratch--docker自带的一个非常小的镜像,这个镜像非常有意思,其实就是一个空文件,还记得当初我刚刚使用这个镜像的时候,因为在公司因为环境用不了外网,所以都是先在一个能下载镜像的地方将镜像下载好了,然后倒入到环境里面的,所以这个镜像也是使用docker pull scratch,然后神奇的事情出现了,直接报错(Error response from daemon: 'scratch' is a reserved name)!但是到docker hub上面看这个镜像确实也存在啊!然后看到这个镜像下面都是一堆拉不了这个镜像的评论,不过上面有个评论贴提到docker的一个使用文档,而这文档里面说到(While scratch appears in Docker’s repository on the hub, you can’t pull it, run it, or tag any image with the name scratch. Instead, you can refer to it in your Dockerfile.)大概的意思就是这个镜像虽然放在docker hub上面,但是你不能拉它到你的本地库里面或者是基于它运行容器以及给它打标签,不过你可以直接在Dockerfile里面使用它。

而第二行就是作者的信息,一般就是作者的名称,后面加上作者的联系方式,由于头条不允许出现邮件等信息,所以这里就随便写一个。

第三行就是把helloworld这个二进制文件复制到docker镜像里面。

第四行就是定义容器启动时的操作,就是执行一次helloworld这个二进制。

5).构建镜像

进入到helloworld这个目录,运行一下以下命令:docker build -t yunzhongmuge/helloworld .

这里解释一下这个命令的意思,build就是表示构建的意思,-t 就是制定这个生成的镜像时叫做什么,这里需要特别注意最后标点符号[.],这表示是在当前目录构建。如下图:

通过Dockerfile制作一个属于自己的镜像!

这样我们的镜像就生成好了!下面我们来测试一下这个镜像是否可用并且是否按照我们之前说的执行一下helloworld这个二进制。


4.测试镜像


直接运行一个容器然后查看容器的日志,所以在shell终端输入如下命令:

docker run -d --name helloworld yunzhongmuge/helloworld:latest

如下图:

通过Dockerfile制作一个属于自己的镜像!

从上面我们可以看到我们启动容器以及down掉了,这是因为我们的镜像里面定义就是执行一次helloworld然后就结束了,所以容器就执行一次helloworld这个二进制文件就退出了,而我们看到logs里面真实输出了hello world!这个信息,这说明了我们制作的这个镜像是成功的!


非常好,到这里我们就对docker的使用应该是算入门级别了!下一篇我们使用docker来构建一个consul的集群!好了本次教程到此结束,good luck!


May you share freely, never taking more than you give.


既然都看到这里,关注、评论、收藏、转发一下呗!谢谢!


分享到:


相關文章: