Michael.W谈Docker 第五期-Dockerfile的编写

1 什么是Dockerfile?

从宏观的角度开,你可以认为Dockerfile就是一个shell脚本文件。在Dockerfile中可以批量执行docker的操作命令,以此来制作docker的镜像文件
所以Dockerfile中要写的就是制作镜像的步骤。

2 Dockerfile使用中要注意的事项

  1. Dockerfile对应的文件名首字母一定要大写
  2. 做到将Dockerfile文件放到一个空的目录中。目的是为了排除其他文件在视觉上对你产生干扰,让你在制作的过程中视觉上产生舒适。
  3. 你制作的镜像,最后都要运行成容器。制作出来的镜像在功能上要保持单一性。每一个镜像就单独实现一个功能。比如redis的镜像只实现redis,mysql的镜像只实现mysql,不要把redis和mysql安装到一个镜像内。(如果有特殊需求除外)
  4. Dockerfile中执行的命令越少越好,这样可以节省空间。(如果你是不在乎存储空间的土豪就当我没说)

3 Dockerfile的工作流程

  1. 编写Dockerfile。
  2. 执行一个docker命令,启动Dockerfile中的指令,依次执行,开始制作镜像。

从第3步开始后面的动作都是计算机自动执行的,跟我们人类已经没有关系了!

  1. 由于我们制作镜像都是以基础镜像为基础,在其上进行加工。所以计算机会检查你的本地仓库中是否拥有对应的基础镜像。如果没有,会从网上自动pull。
  2. 打开镜像,得到容器并进入其内部,执行Dockerfile中的指令。
  3. 执行完所有的指令后,自动将容器export成镜像,即我们最终需要的镜像。

这样我想将我使用的镜像发送给别人的时候,不再需要拷贝整个大的镜像文件,只要将这个Dockerfile发送给他让他运行一下就可以了

4 Dockerfile的基础指令

4.1 FROM

	FROM 基础镜像名:tag

FROM必须在Dockerfile的第一行(除注释)。 可以连续写多个FROM。

4.2 MAINTAINER

	MAINTAINER 制作人的信息

MAINTAINER后面跟一个字符串。

4.3 RUN

	RUN shell命令
	# 写法1
	RUN mkdir /home/michael.w/blockchain -p
	# 写法2
	RUN ["mkdir","/home/michael.w/blockchain","-p"]

就是执行shell命令,两种写法。注意第一种写法如果shell命令过长需要换行,行末需要加反斜杠\

4.4 EXPOSE

	EXPOSE 容器要开放的端口

由于容器是对外封闭的,如果外界客户端要访问宿主机的容器,那么容器必须开放自己的端口与宿主机形成端口映射。EXPOSE指令的功能就是在Dockerfile制作成的镜像在被建立成容器时,自动开放自己的某一端口。

4.5 如何启动Dockerfile?

	$ docker build -t 新创建出的镜像的名字:tag dockerfile所在路径

我简单写一个小型的Dockerfile:

	# 基于基础ubuntu镜像
	FROM ubuntu
	# 制作人信息
	MAINTAINER Michael.W
	# 创建文件夹
	RUN mkdir michael
	# 更新apt源
	RUN sed -i 's/archive.ubuntu.com/mirrors.ustc.edu.cn/g' /etc/apt/sources.list
	RUN sed -i 's/security.ubuntu.com/mirrors.ustc.edu.cn/g' /etc/apt/sources.list
	# 更新软件源
	RUN apt-get update
	# 安装vim,-y为所有的弹出的人机交互都默认输入yes
	RUN apt-get install vim -y
	# 开放80端口
	EXPOSE 80

执行docker build -t myimage:V2.0
在这里插入图片描述
中间在更新源的时候会有很多打印信息,当最后出现Successfully的字样的时候表名镜像已制作成功。

执行docker images
在这里插入图片描述
可见刚刚创建的myimage镜像
打开启动容器docker run -it --name test myimage bash,然后执行ls
在这里插入图片描述
可见有michael文件夹,表名镜像已经按照Dockerfile镜像已经制作成功!

5 Dockerfile中作用于容器运行后的指令

5.1 CMD

一个容器在运行起来之后,会默认执行一条指令。以uginx为例:

在这里插入图片描述
在指令中我没有在指令的最后写bash,从docker ps可见nginx在启动后自动了一条指令nginx -g daemon off。这条指令就是在Dockerfile中利用CMD指令实现的。
在这里插入图片描述

	# 写法1
	CMD shell命令
	# 写法2
	CMD ["shell命令", "命令参数1", "命令参数2"...]

CMD后是根据制作的镜像启动容器后默认执行的命令。但是该CMD会被docker run指令中最后指定的shell命令覆盖掉:
在这里插入图片描述
同时,CMD可以写多个,但是只有最后一个才会生效。

5.2 ENTRYPOINT

ENTRYPOINT的作用和用法跟CMD是一样的,但是ENTRYPOINT后的指令不会docker run指令中最后指定的shell命令覆盖。

	# 写法1
	ENTRYPOINT shell命令
	# 写法2
	ENTRYPOINT ["shell命令", "命令参数1", "命令参数2"]

如果想硬要在docker run中用指令覆盖,可以借助–entrypoint参数。
如果CMD和ENTRYPOINT一起使用时遵循以下原则:
任何docker run最后的命令或者CMD后面的命令,都将作为ENTRYPOINT 指令的命令参数,追加到ENTRYPOINT指令之后。例如:

	ENTRYPOINT mkdir /home/michael.w/hello
	CMD -p

等价于容器启动后执行mkdir /home/michael.w/hello -p
个人感觉这种结合使用毫无卵用!

6 Dockerfile编辑指令

6.1 ADD

比如我想在容器中通过某个源码包安装文件,那么需要将这个源码包拷贝到容器中。这时就用到ADD命令:

	# 写法1
	ADD 宿主机文件 容器目录/文件
	# 写法2
	ADD ["宿主机文件", "容器目录/文件"]

如果宿主机中的文件是tar.gztar.bz2类型的压缩文件,ADD指令会自动解压缩该文件到指定的容器目录。
注:

  1. 一般这个需要拿来拷贝的宿主机文件要放在Dockerfile的同级目录下
  2. 容器目录如果不存在,会自动创建路径。

6.2 COPY

COPY指令和ADD指令类似。是直接将宿主机中的文件复制到容器的某个路径下。只是COPY指令不会做解压缩的工作。

	# 写法1
	COPY 宿主机文件 容器目录/文件
	# 写法2
	COPY ["宿主机文件", "容器目录/文件"]

6.3 VOLUME

VOLUME后面加容器的路径,表示该容器被创建后可以成为一个数据卷容器(天生具有挂载点),其数据卷挂载路径为VOLUME后面的路径。

	VOLUME 容器挂载点路径
	# 例子
	VOLUME ["/backup"]
	#上面等价于
	$ docker run ... -v /backup ...

所以利用上面制作成的镜像生成容器时,不用再输入-v参数就会成为一个数据卷容器

7 Dockerfile环境指令

7.1 ENV

在终端中输入env可以看见当前终端的所有环境变量。都是以键值对的形式存在。
在Dockerfile中有时候会有很多非常长的变量,我们可以将这些值存到环境变量中。

	# 写法1
	ENV 环境变量名 环境变量的值
	# 写法2
	ENV 环境变量名=环境变量的值
	# 写法2可以在一行中设置多个环境变量
	ENV YEAR=2019 NAME=Michael

在RUN 之前设置环境变量,然后在执行RUN 命令时使用。容器启动后这些环境变量都会被使用!

7.2 WORKDIR

WORKDIR功能为切换目录,为后续的RUN、CMD、ENTRYPOINT 服务。相当于linux中的cd指令。

	WORKDIR 要去的进程目录
	# 例子
	WORKDIR /root

例子表示在容器启动后进入到/root路径下。

7.3 USER

指在容器中以哪个用户的身份来执行Dockerfile文件中的指令。
不使用USER指令,默认为root用户

	USER 容器内的用户名

7.4 ARG

ARG与ENV有些类似,可以被后面的其它指令直接使用,但是它并不是环境变量,这意味着将来容器运行时是不会存在ARG变量的。

	# Dockerfile中
	FROM ubuntu
	ARG ARG1
	RUN echo $ARG1
	# docker指令
	$ docker build --build-arg ARG1=michael.w .

在这里插入图片描述
镜像制作过程中打印出了”michael.w“,可见参数ARG1从docker build的指令行传到了Dockerfile中。

8 Dockerfile的触发器指令

8.1 ONBUILD

Dockerfile触发器指令就像有些隔代遗传的遗传疾病。父亲有这种病,但是在儿子身上是不会显现出来,但是到了孙子就会显现出来。
所以,如果你在Dockerfile中使用了ONBUILD指令制作出了镜像A。利用镜像A生成容器时将不会有任何效果,但是当你再用镜像A制作成了镜像B时,这个操作就会显现出来

	ONBUILD [command]
	# 例子
	ONBUILD RUN echo "Michael.W"

在这里插入图片描述
在利用busybox作为基础镜像生成镜像son_image时候,并没有执行Dockerfile中的触发器指令RUN echo "Michael.W"
在这里插入图片描述
修改Dockerfile,利用刚刚创建的镜像son_image来制作镜像grandson_image,此时触发器指令被触发,执行了RUN echo "Michael.W"

ps:
本人热爱图灵,热爱中本聪,热爱V神,热爱一切被梨花照过的姑娘。
以下是我个人的公众号,如果有技术问题可以关注我的公众号来跟我交流。
同时我也会在这个公众号上每周更新我的原创文章,喜欢的小伙伴或者老伙计可以支持一下!
后现代泼痞浪漫主义奠基人
公众号名称:后现代泼痞浪漫主义奠基人

猜你喜欢

转载自blog.csdn.net/michael_wgy_/article/details/88125021