大约4年前Docker的概念如日中天的时候打算学习学习,就写了一篇Blog,但是由于当时自己的自控力比较差,只写了一篇就烂尾了。现在工作中的应用基本都通过容器化部署到了Docker中,所以也算的上是有些痛点吧,今年的学习任务中也给自己列了这一项,春节期间刚好有空来补齐一下这部分的知识。那么从本篇Blog开始由Docker的基本概念接入,大概会分为这么几篇:Docker基本概念及理论基础、Docker安装与常用命令、Docker镜像拉取与部署、Docker容器数据卷、DockerFile与镜像制作、Docker网络原理、IDEA整合Docker、Docker Compose集群编排、Docker Swarm集群管理,在此基础上,进一步去学习Kubernetes8。一些资料:Docker镜像仓库,Docker官方网站。
Docker基本概念
什么是Docker?Docker 是一个开源的应用容器引擎,基于 Go 语言并遵从 Apache2.0 协议开源。Docker
可以让开发者打包他们的应用以及依赖包到一个轻量级、可移植的容器中,然后发布到任何流行的 Linux 机器上,也可以实现虚拟化。容器是完全使用沙箱机制,相互之间不会有任何接口(类似 iPhone 的 app),更重要的是容器性能开销极低。
Docker为什么会出现
一切新出现的技术都是为了解决现存的问题,只要知道Docker出现前存在什么问题,就知道Docker为什么会出现了。举一个生动形象的例子,之前读研的时候参加了一个共享杯大赛,是一个软件设计大赛。本地开发完代码后需要把代码提交给组委会,那么问题来了,光提交代码不行啊,代码依赖的运行环境:Tomcat、JDK、MySQL等也需要打包发给组委会,于是就有了这样一个包:
以及一份详尽的软件安装说明,包括运行环境以及操作系统环境,安装步骤等。
在这个例子中,我就像是一个开发人员,组委会的工作人员就像一个运维人员,这其间的交互比较麻烦:
- 环境配置麻烦:除了Jar包,依赖的各种容器和数据库都需要运维人员安装,可能还存在各种版本问题,而且如果改天应用做迁移,又得在另一个服务器上再装一遍,麻烦的很。
- 环境不隔离:假如组维护的电脑上需要运行各种参赛作品,我这里的Jdk是1.8的版本,万一别人的代码是基于Jdk1.7的,或者tomcat版本不一致,这就会导致运行环境冲突。
- 性能开销高:如果组委会的服务器上同时运行大量的应用,由于资源的分配是物理机级或者操作系统级的,各种应用对资源的压榨是不充分的,性能开销会高一些。
这些问题可以用三个词来概括:麻烦、冲突、性能低,而Docker被发明出来就是解决这三个问题的。其实从单纯的物理机部署到容器化的Docker技术之间,还隔着一个容器化技术,先了解下虚拟化技术如何解决这些问题。
虚拟机技术VS容器化技术
在比较这两个技术之前我们需要先了解一个前置概念:虚拟化技术,什么是虚拟化技术呢?就是将当前物理主机【裸金属机器】虚拟出多个隔离的环境,看起来就像是一个主机上有多个电脑一样。虚拟机技术和容器化技术都属于虚拟化技术,只不过隔离粒度不同。
什么是虚拟机技术?传统虚拟机,虚拟出硬件,以及一个完整的操作系统,然后在这个系统上安装和运行软件,这就是虚拟机技术。之前我有两篇Blog详细介绍过如何通过VMWare安装一个虚拟机集群:【分布式集群搭建 一】虚拟机配置(VMware+Centos7+SecureCRT+AppNode)和【分布式集群搭建 二】克隆虚拟机并配置集群,其实从这两篇Blog的篇幅上就可以看的出光是搭建和配置就耗费了不少力气。
虚拟机技术有什么问题呢?传统的虚拟机需要模拟整台机器包括硬件,每台虚拟机都需要有自己的操作系统,虚拟机一旦被开启,预分配给他的资源将全部被占用。每一个虚拟机包括应用,必要的二进制和库,以及一个完整的用户操作系统。可以看的出来非常笨重并且有较大的资源占用。
1 虚拟机的架构
上图左侧部分展示虚拟机的架构,其中Guest OS
层和Hypervisor
层在docker中被Docker Engine层所替代。虚拟机实现资源隔离的方法是利用独立的OS,并利用Hypervisor虚拟化CPU、内存、IO设备等实现的。
- 虚拟机的
Guest OS
即为虚拟机安装的操作系统,它是一个完整操作系统内核; - 虚拟机的
Hypervisor
层可以简单理解为一个硬件虚拟化平台,它在Host OS是以内核态的驱动存在的。
为了虚拟CPU:Hypervisor会为每个虚拟的CPU创建一个数据结构,模拟CPU的全部寄存器的值,在适当的时候跟踪并修改这些值。需要指出的是在大多数情况下,虚拟机软件代码是直接跑在硬件上的,而不需要Hypervisor介入。只有在一些权限高的请求下,Guest OS需要运行内核态修改CPU的寄存器数据,Hypervisor会介入,修改并维护虚拟的CPU状态。
为了虚拟内存:Hypervisor会创建一个shadow page table。正常的情况下,一个page table可以用来实现从虚拟内存到物理内存的翻译。在虚拟化的情况下,由于所谓的物理内存仍然是虚拟的,因此shadow page table就要做到:虚拟内存->虚拟的物理内存->真正的物理内存。
为了虚拟IO设备:当Hypervisor接到page fault,并发现实际上虚拟的物理内存地址对应的是一个I/O设备,Hypervisor就用软件模拟这个设备的工作情况,并返回。比如当CPU想要写磁盘时,Hypervisor就把相应的数据写到一个host OS的文件上,这个文件实际上就模拟了虚拟的磁盘
2 Docker的架构
上图右侧部分展示Docker的架构。Docker容器内的应用直接运行在宿主机的内核,容器是没有自己的内核的,也没有虚拟硬件,是内核级别的虚拟化技术。每个容器都是相互隔离的,每个容器都有属于自己的文件系统,互不影响。
Docker Engine
可以简单看成对Linux的NameSpace、Cgroup、镜像管理文件系统操作的封装
。Docker并没有和虚拟机一样利用一个完全独立的Guest OS实现环境隔离,它利用的是目前linux内核本身支持的容器方式实现资源和环境隔离。
简单的说,Docker是利用namespace实现系统环境的隔离;利用Cgroup实现资源限制;利用镜像实现根目录环境的隔离。
3 虚拟机和Docker对比
Docker和虚拟机有各自擅长的领域,在软件开发、测试场景和生产运维场景中各有优劣,我们虽然学习Docker,但其实也不能完全否定虚拟机技术
- 启动速度:Docker启动快速属于秒级别。虚拟机通常需要几分钟去启动。
- 性能:Docker需要的资源更少,Docker在操作系统级别进行虚拟化,Docker容器和内核交互,几乎没有性能损耗,性能优于通过Hypervisor层与内核层的虚拟化。
- 内存占用:Docker更轻量,Docker的架构可以共用一个内核与共享应用程序库,所占内存极小。同样的硬件环境Docker运行的镜像数远多于虚拟机数量,对系统的利用率非常高。
- 隔离程度:与虚拟机相比,Docker隔离性更弱,Docker属于进程之间的隔离,虚拟机可实现系统级别隔离。
- 安全性:Docker的安全性也更弱。Docker的租户root和宿主机root等同,一旦容器内的用户从普通用户权限提升为root权限,它就直接具备了宿主机的root权限,进而可进行无限制的操作。虚拟机租户root权限和宿主机的root虚拟机权限是分离的,并且虚拟机利用如Intel的VT-d和VT-x的ring-1硬件隔离技术,这种隔离技术可以防止虚拟机突破和彼此交互,而容器至今还没有任何形式的硬件隔离,这使得容器容易受到攻击。
- 可管理性:Docker的集中化管理工具有K8S。各种虚拟化技术都有成熟的管理工具,例如VMware vCenter提供完备的虚拟机管理能力。
- 高可用和可恢复性:Docker对业务的高可用支持是通过快速重新部署实现的。虚拟化具备负载均衡、高可用、容错、迁移和数据保护等经过生产实践检验的成熟保障机制,VMware可承诺虚拟机99.999%高可用,保证业务连续性。
- 快速创建、删除:虚拟化创建是分钟级别的,Docker容器创建是秒级别的,Docker的快速迭代性,决定了无论是开发、测试、部署都可以节约大量时间。
- 交付、部署:虚拟机可以通过镜像实现环境交付的一致性,但镜像分发无法体系化;Docker在Dockerfile中记录了容器构建过程,可在集群中实现快速分发和快速部署。
可以看到,其实虚拟机技术和Docker都有各自的优势,最好的使用方式就是分场景分别应用。
Docker的应用场景
了解了历史问题以及虚拟机和Docker的对比,可以再回到之前的问题了,Docker的优势是什么,或者这么说,Docker在哪些具体的应用场景下是有比较大的优势的?
- 应用更快速的交付和部署:传统方式需要一堆帮助文档和安装程序,跨环境部署和上线都比较麻烦,不得不依赖公司内部开发的一些服务平台和人工的配置核对。有了Docker,打包镜像、发布测试、发布生产环境等一键部署
- 更便捷的升级和扩缩容:使用Docker我们部署应用就和搭积木一样,项目打包为一个镜像,可以发布多个服务器,进行水平扩展。升级也可以按照容器镜像整体升级,不需要单独升级以及考虑组件兼容性。
- 更简单的系统运维:容器化之后,开发测试和生产环境都是高度一致的,不用担心环境之间的差异。
- 更高效的计算资源利用:Docker是内核级别的虚拟化,可以在物理机上部署很多容器,极致压缩服务器性能
所以Docker对于一个开发人员来说,可以将开发快速升级为开发+运维,系统部署、容器扩缩容,开发都可以做到。运维也不需要花费大量精力去做低效的人工核对而抽出来去做一些运维工具开发。
Docker的组成部分
一个完整的Docker由三大块组成:Docker客户端、Docker服务端、Docker远程镜像仓库,这三大部分里又包含如下概念。
Docker客户端
- Client Docker(客户端Docker的用户界面),可以接受用户命令和配置标识,并且Docker daemon通信
Docker服务端
- Docker daemon(守护进程),运行在宿主机(DOCKER_HOST)的后台进程,可通过Docker客户端与之通信。
- Images (镜像)是一个只读模板,可以通过这个模版去创建很多容器
- Container (容器)是镜像的可运行实例。镜像与容器类似与面向对象中类与对象的关系。可通过Docker API或者CLI命令起停,移动,删除等。
Docker远程镜像仓库
- Register (镜像中心)是一个集中存储与分发镜像的服务。构建完Docker镜像后,就可在当前宿主机上运行。但如果想在其他机器上运行这个镜像,就需要手动复制。此时可以借助Docker Register避免复制。 一个Docker Register可以包含多个Docker仓库,每个仓库可包含多个镜像标签,每个标签对应一个Docker镜像。
其中仓库可以分为公有的和私有的概念
以上概念之间的关系如上图所示。
总结一下
Docker简单理解起来就是一个隔离的运行环境,把独立运行环境的隔离粒度在虚拟机的基础之上再降低,更低的内存占用,更快的启动速度,更简单的运维与扩展升级,更方便的镜像分发部署,真正的解放了开发运维的双手。技术的更新迭代是为了解决更新迭代的问题,归根到底虚拟机技术和Docker都归于虚拟化技术思想,二者并没有绝对意义的优劣,只是各自有不同的时代使命,如果能更好的兼顾分场景使用才是最佳打开方式。