1 镜像
(1)什么是镜像
- Docker 的镜像概念类似虚拟机的镜像。是一个只读的模板,一个独立的文件系统,包括运行容器所需的数据,可以用来创建新的容器。
例如:一个镜像可以包含一个完整的CentOS操作系统环境,里面仅安装了nginx或用户需要的其他应用程序。
-
Docker镜像实际上是由一层一层的系统文件组成,这种层级的文件系统被称为UnionFS( Union file system 统一文件系统),镜像可以基于Dockerfile构建,Dockerfile是一个描述文件,里面包含了若干条命令,每条命令都会对基础文件系统创建新的层次结构。
-
Docker提供了一个很简单的机制来创建镜像或更新现有的镜像。用户甚至可以从其他人那里下载一个已经做好的镜像直接使用。(镜像是只读的,可以理解为静态文件)
(2)镜像的分层结构
- 所有的镜像都是在host基础上建立的,所以镜像和宿主机共享宿主机的内核;
- base镜像提供的是最小的Linux发行版,即只提供根目录;
- 在同一个docker上可以运行多种Linux发行版,因为像redhat、CentOS等Linux发行版,它们的内核都相同,不同的只是文件系统,而我们的Docker就是运行在Linux系统上的,并且和宿主机共享内核,所以可以支持多个Linux发行版;
- 而资源共享怎么理解呢,这个要重点理解一下,下面做一个小实验,我们来观察一下,便可理解“资源共享”得含义
我们在docker上分别拉取nginx最新版本的镜像和nginx 1.1.6的镜像,我们会发现它们有很多层的资源是相同的
而当我们删除其中一个镜像时,只删除资源不相同的层,而不是全部删除,因为资源共享,还有一个nginx镜像要使用这些相同层的资源
在下载时也是同样的道理,如果已经有一个nginx的镜像存在于我们的docker中,那么下载其他版本的nginx镜像时,只下载不同层的资源,其他相同层的资源与已经存在的nginx镜像共享。所以我们可以进一步理解“资源共享”的优点就是减少磁盘的占有量,以及网络带宽的消耗。
2 构建镜像的两种方式
(1)docker commit构建新镜像
利用commit构建镜像的具体过程:
步骤一:拉取一个镜像
步骤二:利用Ubuntu镜像建立容器v1,并建立文件,然后ctrl+p+q退出
步骤三:在上面的基础上删除v1容器,然后重新建立容器v1,查看刚才建立的文件是否还存在
可以看到,重新建立的容器里没有之前创建的目录和文件。
如果我们得到一个原始的镜像后,需要往上百台的之机上构建容器并部署相关服务,做好的镜像没办法保存打包,那将是一件非常痛苦的事情;这个时候我们可以使用commit命令将容器构建成一个新的镜像。
步骤四:利用commit封装镜像
注意:这里可以更加明显的看到之前提到的分层结构,由于我们在原有的ubuntu的镜像上创建了v1容器,并且将v1打包构建成了新的镜像,所以我们发现新构建的容器比原来的基础容器多了一层。
注意:使用新封装的镜像构建容器,保存了之前操作的痕迹
(2)编写Dockerfile构建镜像
Dockerfile常用指令
- FROM
指定base镜像,如果本地不存在会从远程仓库下载
- MAINTAINER
设置镜像作者,比如用户邮箱等
- COPY
把文件从build context复制到镜像。支持两种形式:COPY src dest 和 COPY [ “src”, “dest”] src必须指定build context中的文件目录
- ADD
用法与COPY类似,不同的是src可以是归档压缩文件,文件会被自动解压到dest,也可以自动下载URL并拷贝到镜像:
ADD html.tar /var/www
ADD http://ip/html.tar /var/www
- ENV
设置环境变量,变量可以被后续的指令使用:
ENV HOSTNAME server1.example.com
- EXPOSE
如果容器中运行应用服务,可以把服务端口暴露出去:
EXPOSE 80
- VOLUME
申明数据卷,通常指定的是应用的数据挂载点:
VOLUME [ “/var/www/html”]
- WORKDIR
为RUN、CMD、ENTRYPOINT、ADD和COPY指令设置镜像中的当前目录,如果目录不存在会自动创建
- RUN
在容器中运行命令并创建新的镜像层,常用于安装软件包:
RUN yunm install vim -y
- CMD与ENTRYPOINT
这两个指令都是用于设置容器启动后执行的命令,但CMD会被docker run后面的命令覆盖,而ENTRYPOINT不会被忽略,一定会被执行。docker run后面的参数可以传递给ENTRYPOINT指令当做参数。
Dockerfile中只能指定一个ENTRYPOINT,如果指定了很多,只有最后一个生效。
Shell和exec格式的区别:
- Shell格式底层会调用/bin/sh -c来执行命令,可以解析变量,形式如下:
ENTRYPOINT echo "hello,$name"
- exec和Shell的格式不同,必须写成下面的格式才能够正确解析变量
ENTRYPOINT ["/bin/sh","-c","echo hello,$name"]
- 使用Exec格式时,ENTRYPOINT可以通过CMD提供额外参数,CMD的额外参数可以在容器启动时动态替换。在Shell格式时,ENTRYPOINT会忽略任何CMD或docker run提供的参数
编写Dockerfile构建镜像
建立一个空目录,在这个目录中创建一个文件(即我们的Dockerfile),然后在其中编辑dockerfile指令
mkdir docker
ls
vim dockerfile
接下来我们看一下之前讲的Dockerfile的指令的具体效果
- COPY
- RUN
-
ADD
-
ENV
-
EXPOSE
-
VOLUME
docker inspect 6a64c3ba275f #查看容器的具体信息
注意:所有创建的容器数据均存放在宿主机的/var/lib/docker中,当你创建容器时,宿主机的文件系统会根据你的操作自动在本地文件系统中生成相应的目录来存放容器的数据。
我们可以做一个小实验,在上面创建的容器的data目录中建立一个文件,我们在宿主机的文件系统中观察能否查询到
docker attach 6a64c3ba275f #进入刚刚退出的容器demo:v6
同样在宿主机的容器对应目录中写入文件内容,在容器中同样可以看到
docker volume ls #查看当前容器中的卷
docker volume prune #清除当前未被容器所使用的卷
- WORKDIR
- CMD
- ENTRYPOINT
注意:但是这两个指令还有些许差别,我们在前面提到过CMD会被docker run后面的命令覆盖,而ENTRYPOINT不会被忽略,一定会被执行,我们看下面这个小实验。
- CMD与ENTRYPOINT的区别
- CMD和ENTRYPOINT的exec形式
我们上面写的CMD与ENTRYPOINT都是它们的Shell形式,其实它们还有另外一种写法exec,我们来看一下