Docker学习记录二:Docker的基本概念、Docker与虚拟化的区别

3.3 基本概念

3.3.1 镜像(Image)

操作系统分为内核层和用户空间层,当Linux内核启动后,root文件系统会被Linux内核读取并挂载,为用户提供空间支持,根目录,dev\etc\tmp\var\bin等目录在这一层得以体现,
而Docker镜像(Image),就相当于一个小的root文件系统,特殊的文件系统,里面除了包含有提供容器运行时所需要的程序、库、资源、配置等文件外,还有一些容器运行时的配置参数(环境变量、用户、匿名卷等),镜像不会包含任何动态的数据,镜像内容也不会被改变。
 镜像的分层结构:镜像分层最大的一个好处就是资源共享,当多个镜像都是有一个基础镜像构建而来的时候,那么Docker主机就只需要在磁盘保存一份基础镜像,同时在内存中也只会加载一份基础镜像给容器使用,且镜像的每一层都可以被共享。
 如果多个容器共享一份基础镜像,当某个容器修改了基础镜像,其他镜像是不会受到影响的,修改的内容只会影响当前容器,这个就是容器的Copy-on -Write(写时复制)特性。

在这里插入图片描述

镜像设计时采用了Union FS技术,将其设计为分层存储的架构,所以严格来说,镜像并不是一个ISO文件,不是一个文件组成的,而是由一组文件系统组成的,或者说由多层文件系统联合组成,镜像在构建时是一层一层构建的,前面是后面的基础,每一层构建后不在改变,后一层的改变只发生在自己这一层。
比如,删除前一层文件的操作,实际不是真的删除前一层的文件,而是仅在当前层标记当前层文件已删除,在最终容器运行的时候虽然不会看见这个文件,但事实是该文件一直跟随镜像,因此,在构建镜像的时候需要额外小心,每一层只需要添加自己需要的东西。当容器启动时,一个新的可写层被加载到镜像的顶部,这一层通常被称作为“容器层”,“容器层”之下都叫做“镜像层”。

在这里插入图片描述

所有对容器的改动 - 无论添加、删除、还是修改文件都只会发生在容器层中。只有容器层是可写的,容器层下面的所有镜像层都是只读的。镜像层数量可能会很多,所有镜像层会联合在一起组成一个统一的文件系统。如果不同层中有一个相同路径的文件,比如 /a.txt,上层的 /a.txt 会覆盖下层的 /a.txt,也就是说用户只能访问到上层中的文件 /a.txt。在容器层中,用户看到的是一个叠加之后的文件系统。
文件操作说明
添加文件在容器中创建文件时,新文件被添加到容器层中。
读取文件在容器中读取某个文件时,Docker 会从上往下依次在各镜像层中查找此文件。一旦找到,立即将其复制到容器层,然后打开并读入内存。
修改文件在容器中修改已存在的文件时,Docker 会从上往下依次在各镜像层中查找此文件。一旦找到,立即将其复制到容器层,然后修改之。
修改文件在容器中删除文件时,Docker 也是从上往下依次在镜像层中查找此文件。找到后,会在容器层中记录下此删除操作。(只是记录删除操作)
只有当需要修改时才复制一份数据,这种特性被称作 Copy-on-Write。可见,容器层保存的是镜像变化的部分,不会对镜像本身进行任何修改。这样就解释了我们前面提出的问题:容器层记录对镜像的修改,所有镜像层都是只读的,不会被容器修改,所以镜像可以被多个容器共享。

3.3.2 容器(Container)

镜像和容器就像类和实例的关系,镜像运行之后就会产生一个容器,容器可以被创建、启动、删除、停止等。
容器的实际是进程,与宿主机的进程不同,容器进程运行属于自己的独立的命名空间,因此容器可以拥有自己的root文件系统、自己的网络配置、自己的进程空间,甚至是自己的用户ID空间,容器内的进程是运行在一个隔离的环境中,使用起来就像是独立于宿主机的操作系统下一样,这种特性使得容器封装的应用比直接在宿主机上运行更安全。
同样,容器也是使用的分层存储,每一个容器运行时,是以镜像为基础层,在其上创建一个当前容器的存储层,我们可以称这个味容器运行时读写二准备的存储层为容器存储层,容器存储层的生命周期和容器一样,容器消亡时,容器存储层也会随之消亡,因此任何保存在容器存储层的信息都会随容器删除而丢失。
按照 Docker 最佳实践的要求,容器不应该向其存储层内写入任何数据,容器存储层要保持无状态化。所有的文件写入操作,都应该使用 数据卷(Volume)、或者 绑定宿主目录,在这些位置的读写会跳过容器存储层,直接对宿主(或网络存储)发生读写,其性能和稳定性更高。
数据卷的生存周期独立于容器,容器消亡,数据卷不会消亡。因此,使用数据卷后,容器删除或者重新运行之后,数据却不会丢失。	

3.3.3 仓库(Repository)

镜像构建完成后可以很容易的在宿主机上运行,如果需要在其他宿主机或者服务器上运行的话,就需要进行移植,移植到一个用来存储、分发的地方,这个地方就是仓库( Docker Repository)。类似于yum仓库。
一个Docker Repository中可以包含多个仓库,每个仓库可以包含多个标签(Tag),每个标签对应一个镜像,通常一个仓库会包含统一软件的不同版本,而标签就对应软件的各个版本,一般通过<仓库名称>:<标签名称>的格式来定义该软件的那个版本镜像,如果不给出Tag,默认是最新版本,举个例子CentOS是仓库名,6.7是标签,CentOS:6.7就是镜像。仓库名通常是两段式路径出现的,比如jess/nginx,通常情况下jess 是Docker Registory多用户环境下的用户名,后者是软件名,但并不是绝对的,取决于所使用的具体的Docker Registory的软件或者服务。
Docker Registory分为公开服务和私有服务。
公开服务是给用户使用,允许用户管理镜像的服务,一般这类公开服务允许用户免费上传、下载公开的镜像,也有可能提供收费服务供用户管理私有镜像,最常用的是Docker Hub(官方仓库),除此之外还有Red Hat 的 Quay.io;Google 的 Google Container Registry,Kubernetes 的镜像使用的就是这个服务;代码托管平台 GitHub 推出的 ghcr.io。
由于某些原因,在国内访问这些服务可能会比较慢。国内的一些云服务商提供了针对 Docker Hub 的镜像服务(Registry Mirror),这些镜像服务被称为 加速器。常见的有 阿里云加速器、DaoCloud 加速器 等。使用加速器会直接从国内的地址下载 Docker Hub 的镜像,比直接从 Docker Hub 下载速度会提高很多,安装好Docker后会在配置文件中配置一个加速器的地址。国内也有一些云服务商提供类似于 Docker Hub 的公开服务。比如 网易云镜像服务、DaoCloud 镜像市场、阿里云镜像库 等。
私有 Docker Registry,除了使用公开服务外,用户还可以在本地搭建私有 Docker Registry。Docker 官方提供了 Docker Registry 镜像,可以直接使用做为私有 Registry 服务。在 私有仓库 一节中,会有进一步的搭建私有 Registry 服务的讲解。开源的 Docker Registry 镜像只提供了 Docker Registry API 的服务端实现,足以支持 docker 命令,不影响使用。但不包含图形界面,以及镜像维护、用户管理、访问控制等高级功能。除了官方的 Docker Registry 外,还有第三方软件实现了 Docker Registry API,甚至提供了用户界面以及一些高级功能。比如,Harbor 和 Sonatype Nexus。
本地还有一个本地仓库,存储使用过的镜像。

3.4 容器与虚拟化

3.4.1 什么是虚拟化

虚拟化技术提供了在一个较大的服务器的物理环境框架内存在多个较小的虚拟服务器。在母机上运行的虚拟化软件为每个虚拟服务器分配资源。虚拟服务器也有他们自己的:操作系统(OS)、驱动程序、数据库、应用程序等。这些虚拟服务器之间是相互隔离的。每个小型服务器都存在于一个由其他服务器组成的虚拟化平台中。它们也参与到一个与其他虚拟机共享的资源池中。

3.4.2 虚拟化与Docker区别

在这里插入图片描述

Docker虚拟化
在进程级别上安全性和隔离性较差完全隔离,安全性更高
轻量级,资源使用率低重量级,资源使用率高
操作系统虚拟化硬件级虚拟化
所有的容器共享主机操作系统每一个虚拟机都运行在自己的操作系统
启动时间以秒为单位,快速启动启动时间以分钟为单位,启动缓慢

3.4.3 为什么使用Docker

更高效的利用系统资源

由于容器不需要进行硬件虚拟以及运行完整操作系统等额外开销,Docker 对系统资源的利用率更高。无论是应用执行速度、内存损耗或者文件存储速度,都要比传统虚拟机技术更高效。因此,相比虚拟机技术,一个相同配置的主机,往往可以运行更多数量的应用。

更快速的启动时间

传统的虚拟机技术启动应用服务往往需要数分钟,而 Docker 容器应用,由于直接运行于宿主内核,无需启动完整的操作系统,因此可以做到秒级、甚至毫秒级的启动时间。大大的节约了开发、测试、部署的时间。

一致的运行环境

开发过程中一个常见的问题是环境一致性问题。由于开发环境、测试环境、生产环境不一致,导致有些 bug 并未在开发过程中被发现。而 Docker 的镜像提供了除内核外完整的运行时环境,确保了应用运行环境一致性,从而不会再出现 「这段代码在我机器上没问题啊」 这类问题。

持续交付和部署

对开发和运维(DevOps)人员来说,最希望的就是一次创建或配置,可以在任意地方正常运行。
使用 Docker 可以通过定制应用镜像来实现持续集成、持续交付、部署。开发人员可以通过 Dockerfile 来进行镜像构建,并结合 持续集成(Continuous Integration) 系统进行集成测试,而运维人员则可以直接在生产环境中快速部署该镜像,甚至结合 持续部署(Continuous Delivery/Deployment) 系统进行自动部署。
而且使用 Dockerfile 使镜像构建透明化,不仅仅开发团队可以理解应用运行环境,也方便运维团队理解应用运行所需条件,帮助更好的生产环境中部署该镜像。

更轻松的迁移

由于 Docker 确保了执行环境的一致性,使得应用的迁移更加容易。Docker 可以在很多平台上运行,无论是物理机、虚拟机、公有云、私有云,甚至是笔记本,其运行结果是一致的。因此用户可以很轻易的将在一个平台上运行的应用,迁移到另一个平台上,而不用担心运行环境的变化导致应用无法正常运行的情况。

更轻松的维护和扩展

Docker 使用的分层存储以及镜像的技术,使得应用重复部分的复用更为容易,也使得应用的维护更新更加简单,基于基础镜像进一步扩展镜像也变得非常简单。此外,Docker 团队同各个开源项目团队一起维护了一大批高质量的 官方镜像,既可以直接在生产环境使用,又可以作为基础进一步定制,大大的降低了应用服务的镜像制作成本。

3.4.4 Docker架构

在这里插入图片描述
在这里插入图片描述

用户是使用 Docker Client 与 Docker Daemon 建立通信,并发送请求给后者;Docker Daemon 作为 Docker 架构中的主体部分,首先提供 Docker Server 的功能使其可以接受 Docker Client 的请求;Docker Engine 执行 Docker 内部的一系列工作,每一项工作都是以一个 Job 的形式的存在;Job 的运行过程中,当需要容器镜像时,则从 Docker Registry 中下载镜像,并通过镜像管理驱动 Graphdriver 将下载镜像以 Graph 的形式存储;当需要为 Docker 创建网络环境时,通过网络管理驱动 Networkdriver 创建并配置 Docker容器网络环境。;当需要限制 Docker 容器运行资源或执行用户指令等操作时,则通过 Execdriver 来完成;Libcontainer 是一项独立的容器管理包,Networkdriver 以及 Execdriver 都是通过 Libcontainer 来实现具体对容器进行的操作。

3.4.5 Docker核心组件

3.4.5.1 Docker Client
Docker Client是和Docker Daemon 建立通信的客户端。用户使用的可执行文件为 docker(一个命令行可执行文件),docker 命令使用后接参数的形式来实现一个完整的请求命令(例如:docker images,docker 为命令不可变,images 为参数可变)。Docker Client 可以通过以下三种方式和 Docker Daemon 建立通信:tcp://host:port、unix://pathtosocket 和 fd://socketfd。
Docker Client 发送容器管理请求后,由 Docker Daemon 接受并处理请求,当 Docker Client 接收到返回的请求相应并简单处理后,Docker Client 一次完整的生命周期就结束了。(一次完整的请求:发送请求→处理请求→返回结果),与传统的 C/S 架构请求流程并无不同。
3.4.5.2 Docker Daemon(后台守护进程)

Docker daemon 架构图:
在这里插入图片描述

Docker Server 架构图:
在这里插入图片描述

Docker Server 相当于 C/S 架构的服务端。功能为接受并调度分发 Docker Client 发送的请求。接受请求后,Docker Server 通过路由与分发调度,找到相应的 Handler 来执行请求。
在 Docker 的启动过程中,通过包 gorilla/mux 创建了一个 mux.Router 来提供请求的路由功能。在 Golang 中 gorilla/mux 是一个强大的 URL 路由器以及调度分发器。该 mux.Router 中添加了众多的路由项,每一个路由项由 HTTP 请求方法(PUT、POST、GET 或DELETE)、URL、Handler 三部分组成。
创建完 mux.Router 之后,Docker 将 Server 的监听地址以及 mux.Router 作为参数来创建一个 httpSrv=http.Server{},最终执行 httpSrv.Serve() 为请求服务。
在 Docker Server 的服务过程中,Docker Server 在 listener 上接受 Docker Client 的访问请求,并创建一个全新的 goroutine 来服务该请求。在 goroutine 中,首先读取请求内容并做解析工作,接着找到相应的路由项并调用相应的 Handler 来处理该请求,最后 Handler 处理完请求之后回复该请求。
3.4.5.3 Docker Engine
Docker Engine 是 Docker 架构中的运行引擎,同时也 Docker 运行的核心模块。它扮演 Docker Container 存储仓库的角色,并且通过执行 Job 的方式来操纵管理这些容器。
在 Docker Engine 数据结构的设计与实现过程中,有一个 Handler 对象。该 Handler 对象存储的都是关于众多特定 Job 的 Handler 处理访问。举例说明: Docker Engine 的Handler 对象中有一项为:{“create”: daemon.ContainerCreate,},则说明当名为”create” 的 Job 在运行时,执行的是 daemon.ContainerCreate 的 Handler。
一个 Job 可以认为是 Docker 架构中 Docker Engine 内部最基本的工作执行单元。Docker 可以做的每一项工作,都可以抽象为一个 Job。例如:在容器内部运行一个进程,这是一个 Job;创建一个新的容器,这是一个 Job。Docker Server 的运行过程也是一个 Job,名为 ServeApi。Job 的设计者,把 Job 设计得与 Unix 进程相仿。比如说:Job 有一个名称、有参数、有环境变量、有标准的输入输出、有错误处理,有返回状态等。
3.4.5.4 Docker Registry(镜像注册中心)
Docker Registry 是一个存储容器镜像的仓库(注册中心),可理解为云端镜像仓库。按 Repository 来分类,docker pull 按照 [repository]:[tag] 来精确定义一个具体的 Image。
在 Docker 的运行过程中,Docker Daemon 会与 Docker Registry 通信,并实现搜索镜像、下载镜像、上传镜像三个功能,这三个功能对应的 Job 名称分别为:“search”、”pull” 与 “push”。
 Docker Registry 可分为公有仓库( Docker Hub)和私有仓库。
3.4.5.5 Graph 「Docker 内部数据库」

Graph 架构图:
在这里插入图片描述

Repository
已下载镜像的保管者(包括下载的镜像和通过 Dockerfile 构建的镜像)。一个 Repository 表示某类镜像的仓库(例如:Ubuntu),同一个 Repository 内的镜像用 Tag 来区分(表示同一类镜像的不同标签或版本)。一个 Registry 包含多个Repository,一个 Repository 包含同类型的多个 Image。镜像的存储类型有 Aufs、Devicemapper、Btrfs、Vfs等。其中 CentOS 系统 7.x 以下版本使用 Devicemapper 的存储类型。同时在 Graph 的本地目录中存储有关于每一个的容器镜像具体信息,包含有:该容器镜像的元数据、容器镜像的大小信息、以及该容器镜像所代表的具体 rootfs。
GraphDB
已下载容器镜像之间关系的记录者。GraphDB 是一个构建在 SQLite 之上的小型数据库,实现了节点的命名以及节点之间关联关系的记录。
3.4.5.6 Driver 「执行部分」
Driver 是 Docker 架构中的驱动模块。通过 Driver 驱动,Docker 可以实现对 Docker 容器执行环境的定制。即 Graph 负责镜像的存储,Driver 负责容器的执行。

Graphdriver 架构图:
在这里插入图片描述

Graphdriver 主要用于完成容器镜像的管理,包括存储与获取。
存储:docker pull 下载的镜像由 Graphdriver 存储到本地的指定目录( Graph 中 )。获取:docker run(create)用镜像来创建容器的时候由 Graphdriver 到本地 Graph中获取镜像。

Networkdriver 架构图:
在这里插入图片描述

Networkdriver 的用途是完成 Docker 容器网络环境的配置,其中包括:Docker 启动时为 Docker 环境创建网桥。Docker 容器创建时为其创建专属虚拟网卡设备。Docker 容器分配IP、端口并与宿主机做端口映射,设置容器防火墙策略等。

Execdriver 架构图:
在这里插入图片描述

Execdriver 作为 Docker 容器的执行驱动,负责创建容器运行命名空间、容器资源使用的统计与限制、容器内部进程的真正运行等。现在 Execdriver 默认使用 Native 驱动,不依赖于 LXC。
3.4.5.7 Libcontainer 「函数库」

Libcontainer 架构图:
在这里插入图片描述

Libcontainer 是 Docker 架构中一个使用 Go 语言设计实现的库,设计初衷是希望该库可以不依靠任何依赖,直接访问内核中与容器相关的 API。Docker 可以直接调用 Libcontainer 来操纵容器的 Namespace、Cgroups、Apparmor、网络设备以及防火墙规则等。Libcontainer 提供了一整套标准的接口来满足上层对容器管理的需求。或者说 Libcontainer 屏蔽了 Docker 上层对容器的直接管理。
3.4.5.8 Docker Container 「服务交付的最终形式」

Docker Container 架构:
在这里插入图片描述

Docker Container( Docker 容器 )是 Docker 架构中服务交付的最终体现形式。
Docker 按照用户的需求与指令,订制相应的 Docker 容器:
用户通过指定容器镜像,使得 Docker 容器可以自定义 rootfs 等文件系统。
用户通过指定计算资源的配额,使得 Docker 容器使用指定的计算资源。
用户通过配置网络及其安全策略,使得 Docker 容器拥有独立且安全的网络环境。
用户通过指定运行的命令,使得 Docker 容器执行指定的工作。
3.4.5.9 docker Volume「数据卷」
实际上我们的容器就好像是一个简易版的操作系统,只不过系统中只安装了我们的程序运行所需要的环境,前边说到我们的容器是可以删除的,那如果删除了,容器中的程序产生的需要持久化的数据怎么办呢?容器运行的时候我们可以进容器去查看,容器一旦删除就什么都没有了。所以数据卷就是来解决这个问题的,是用来将数据持久化到我们宿主机上,与容器间实现数据共享,简单的说就是将宿主机的目录映射到容器中的目录,应用程序在容器中的目录读写数据会同步到宿主机上,这样容器产生的数据就可以持久化了,比如我们的数据库容器,就可以把数据存储到我们宿主机上的真实磁盘中。

参考手册

·官方文档地址:https://www.docker.com/get-started
·中文参考手册:https://docker_practice.gitee.io/zh-cn/
·本笔记参考B站视频https://www.bilibili.com/video/BV1gR4y1B7KC/?p=23&spm_id_from=333.1007.top_right_bar_window_history.content.click&vd_source=53c0c433320bb51004d1aa224cdcf7d5
  本笔记部分内容来自CSDN
·https://blog.csdn.net/m0_63808944/article/details/125382298
·https://blog.csdn.net/weixin_41605937/article/details/120695769
·https://blog.csdn.net/huwh_/article/details/71308236
·Dockerfile 官方文档:https://docs.docker.com/engine/reference/builder/
·Dockerfile 最佳实践文档:https://docs.docker.com/develop/develop-images/dockerfile_best-practices/
·Docker 官方镜像 Dockerfile:https://github.com/docker-library/docs
·https://blog.csdn.net/Wei_Naijia/article/details/125543056
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值