Docker(3)--Docker镜像的分层结构和两种常见的镜像构建方式(commit、Dockerfile)

1.镜像的基础知识

  • 镜像由多个层组成,每层叠加之后,从外部看来就如一个独立的对象
  • 镜像内部是一个精简的操作系统(OS),同时还包含应用运行所必须的文件和依赖包
  • 因为容器的设计初衷就是快速和小巧,所以镜像通常都比较小
  • 拉取操作会将镜像下载到本地 Docker 主机,可以使用该镜像启动一个或者多个容器
  • 镜像就像停止运行的容器。实际上,可以停止某个容器的运行,并从中创建新的镜像
  • 在该前提下,镜像可以理解为一种构建时(build-time)结构,而容器可以理解为一种运行时(run-time)结构,如下图所示:
    Docker(3)--Docker镜像的分层结构和两种常见的镜像构建方式(commit、Dockerfile)_第1张图片
  • 一旦容器从镜像启动后,二者之间就变成了互相依赖的关系,并且在镜像上启动的容器全部停止之前,镜像是无法被删除的
  • 尝试删除镜像而不停止或销毁使用它的容器,会导致出错
  • 容器目的就是运行应用或者服务,这意味着容器的镜像中必须包含应用/服务运行所必需的操作系统和应用文件
  • 但是,容器又追求快速和小巧,这意味着构建镜像的时候通常需要裁剪掉不必要的部分,保持较小的体积
  • Dockerfile中的每条命令都会在文件系统中创建一个新的层次结构
  • 文件系统在这些层次上构建起来,镜像就构建于这些联合的文件系统之上
  • 当容器启动后,所有镜像都会统一合并到一个进程中
  • 联合文件系统中的文件被删除时, 它们只是被标记为已删除,但实际上仍然存在
  • 仓库是用来存放镜像的,容器中的配置数据保存到镜像中

2.镜像的分层结构

Docker(3)--Docker镜像的分层结构和两种常见的镜像构建方式(commit、Dockerfile)_第2张图片

  • Docker 镜像由一些松耦合的只读镜像层组成
  • Docker 负责堆叠这些镜像层,并且将它们表示为单个统一的对象
  • 在添加额外的镜像层的同时,镜像始终保持是当前所有镜像的组合
  • linux的发行版:内核(kernel)都为linux,不同的发行版的文件系统不同
  • 服务器端主流服务器企业版:rhel > oel > centos
  • 通常底层选择rhel,节点可选择centos
  • 客户端:ubuntu debian,客户端版本比较小
  • 共享宿主机的kernel
  • base镜像提供的是最小的linux发行版
  • 同一docker主机支持运行多种linux发行版
  • 采用分层结构的最大好处是:共享资源
  • 多个镜像之间可以并且确实会共享镜像层。这样可以有效节省空间并提升性能
  • Docker 通过存储引擎(新版本采用快照机制)的方式来实现镜像层堆栈,并保证多镜像层对外展示为统一的文件系统
    Docker(3)--Docker镜像的分层结构和两种常见的镜像构建方式(commit、Dockerfile)_第3张图片
Copy-on-Write 可写容器层
容器层以下所有镜像层都是只读的
docker从上往下依次查找文件
容器层保存镜像变化的部分,并不会对镜像本身进行任何修改
一个镜像最多127层
拉取时,每层都有自己的id,只是在官方进行,不影响使用
再次下载不同版本时,只是下载不同的层,不用下载公共的层,共享了资源
删除时,只是删除不同的层
最上面的最早生效
如果想修改下面的文件信息,就会运用copy-on-write将下层数据copy到上层进行修改,即会将修改的内容保存到刻写容器层中
镜像层越多,镜像越大,磁盘空间消耗越大,消耗带宽越大

3.两种常用的镜像构建方法

1>两种方式构建镜像的主要步骤:

  • 从容器构建镜像(commit命令)
  • (1)运行容器:创建一个容器,比如使用 tomcat:latest 镜像创建一个tomcat-test容器
  • (2)修改容器:修改tomcat-test容器的文件系统,比如修改tomcat的server.xml文件中的默认端口
  • (3)将容器保存为新的镜像:使用commit命令提交镜像
  • 缺点:
  • 效率低、可重复性弱、容易出错
  • 使用者无法对镜像进行审计,存在安全隐患
  • 使用Dockerfile构建镜像(编写Dockerfile)文件
  • (1)编写Dockerfile文件
  • (2)使用build命令构建镜像
    Docker(3)--Docker镜像的分层结构和两种常见的镜像构建方式(commit、Dockerfile)_第4张图片

2>两种构建方式的区别

  • 容器镜像的构建者可以任意修改容器的文件系统后进行发布,这种修改对于镜像使用者来说是不透明的,镜像构建者一般也不会将对容器文件系统的每一步修改,记录进文档中,供镜像使用者参考
  • 容器镜像不能(更准确地说是不建议)通过修改,生成新的容器镜像
  • 从镜像运行容器,实际上是在镜像顶部上加了一层可写层,所有对容器文件系统的修改,都在这一层中进行,不影响已经存在的层
  • 比如在容器中删除一个1G的文件,从用户的角度看,容器中该文件已经没有了,但从文件系统的角度看,文件其实还在,只不过在顶层中标记该文件已被删除,当然这个标记为已删除的文件还会占用镜像空间
  • 从容器构建镜像,实际上是把容器的顶层固化到镜像中
  • 也就是说, 对容器镜像进行修改后,生成新的容器镜像,会多一层,而且镜像的体积只会增大,不会减小,长此以往,镜像将变得越来越臃肿
  • Docker提供的 export 和 import 命令可以一定程度上处理该问题,但也并不是没有缺点
  • 容器镜像依赖的父镜像变化时,容器镜像必须进行重新构建。如果没有编写自动化构建脚本,而是手工构建的,那么又要重新修改容器的文件系统,再进行构建,这些重复劳动其实是没有价值的
  • Dockerfile镜像是完全透明的,所有用于构建镜像的指令都可以通过Dockerfile看到,甚至还可以递归找到本镜像的任何父镜像的构建指令
  • 也就是说,你可以完全了解一个镜像是如何从零开始,通过一条条指令构建出来的
  • Dockerfile镜像需要修改时,可以通过修改Dockerfile中的指令,再重新构建生成,没有任何问题
  • Dockerfile可以在GitHub等源码管理网站上进行托管,DockerHub自动关联源码进行构建
  • 当你的Dockerfile变动,或者依赖的父镜像变动,都会触发镜像的自动构建,非常方便

你可能感兴趣的:(Docker学习)