Docker是一个由go语言编写的,开源的应用容器引擎。
开发者可以打包他们的应用以及依赖包到一个可移植的镜像[1]中,然后发布到任何流行的Linux[2]或Windows[3]机器上,也可以实现虚拟化[4]。
容器是完全使用沙箱[5]机制,相互之间不会有任何接口。
Docker背后实现原理是什么?Docker架构实现一个完整的容器,需要Namespace、Cgroups和联合文件系统这三项Linux技术的支撑。
图源百度图片NamespaceNamespace是Linux内核用来隔离内核资源的一种方式。通过Namespace可以对某些进程进行限制,使得其只能访问与自身相关的那部分资源,其他资源不可见。
如此,不同进程之间也是相互无感知的。这种隔离覆盖面十分广泛,包括但不限于:隔离进程ID、主机名、用户ID、文件名、网络访问和进程间通信等相关资源。在Docker的世界中,主要用到以下六种命名空间。
pidnamespace:隔离进程ID
netnamespace:隔离网络接口
mntnamespace:隔离文件系统挂载点,可在不同进程中看到不同挂载目录,且不影响主机挂载目录
ipcnamespace:隔离进程间通信(如:信号量,消息队列和共享内存)
utsnamespace:隔离主机名和域名。
usernamespace:隔离用户和用户组,可实现docker内拥有root权限而在主机上只是普通用户
正是由于Docker使用了Linux的这些Namespace技术,才实现了Docker容器的隔离。
CgroupsCgroups是控制组群(controlgroups)的简写,可用于限制、控制与分离一个进程组的资源(如CPU、内存、磁盘输入输出等)。
在Docker的世界中,Cgroups通常用来限制容器的CPU和内存等资源的使用。
资源限制:限制资源的使用量(如通过限制某个业务的内存上限,保护主机其他业务的安全运行)。
优先级控制:不同的组可以有不同的资源(CPU、磁盘IO等)使用优先级。
审计:计算控制组的资源使用情况。
运行控制:控制进程的挂起或恢复。
图源掘金小册-开发者必备的Docker实践指南联合文件系统联合文件系统是一种分层、轻量级并且高性能的文件系统,它支持对文件系统的修改作为一次提交来一层层的叠加,同时可以将不同目录挂载到同一个虚拟文件系统下。
在Docker的世界中,联合文件系统为容器提供构建层,使得容器可以实现写时复制以及镜像的分层构建和存储。
镜像本身是只读的,在运行某个容器的时候,会在镜像外层附加一个可读写层,这也是基于联合文件系统实现的。
FROMbusyboxCOPYtest/tmp/testRUNmkdir/tmp/testdir
上述每一行命令都会生成一个镜像层,当不同的镜像之间有相同的镜像层时,便可以实现不同的镜像之间共享镜像层的效果。(比如基于base镜像扩展的镜像)
图源拉钩教育镜像层共享这个功能十分强大,每一层都可以共享。
图源百度图片操作系统层面的虚拟化以上这种对进程进行封装隔离的技术,属于操作系统层面的虚拟化技术[6]。隔离的进程独立于宿主和其它的隔离的进程,因此也称其为容器。容器本质是一个受限制的进程,它没办法像直接运行在主机上的进程那样获取主机上的进程、环境变量、网络等信息。
该技术最初实现是基于LXC[7](LinuxContainer),从0.7版本以后开始去除LXC,转而使用自行开发的libcontainer[8]。从自1.11版本开始,进一步演进为使用runC[9]和containerd[10]。
客户端/服务端模式图源拉钩教育docker如上图所示:Docker和MySQL一样,也是客户端-服务端的模式。Docker客户端其实是一种泛称,用户可通过docker命令或者直接请求RESTAPI的方式与Docker服务端交互。
dockerdDocker服务端是Docker所有后台服务的统称。其中dockerd是一个非常重要的后台管理进程,它负责响应和处理来自Docker客户端的请求,然后将客户端的请求转化为Docker的具体操作。这个过程中dockerd会通过grpc(一个rpc框架)与containerd通信,containerd作为dockerd的下层服务。
runCrunC是一个根据OCI(开放容器标准)规范生成和运行容器的CLI工具,可通过命令行的形式控制容器的销毁与创建。
containedcontainerd是dockerd和runC之间的一个中间交流组件,docker对容器的管理和操作基本都是通过containerd完成的。例如:容器生命周期管理、镜像管理。
containerd-shimcontainerd通过containerd-shim启动并管理runC,containerd-shim主要作用是可以在不中断容器运行的情况下升级或重启dockerd。
Docker与虚拟化技术虚拟化技术指的是通过隐藏特定计算平台的实际物理特性,为用户提供抽象、统一、模拟的计算环境。这种计算环境,也被称为虚拟机。
Docker是对Linux容器的一种封装,具有高性能,低开销,接口简单易用等特点。真正使其超过其他同类产品,迅速占领市场,成为目前最流行的Linux容器技术解决方案的根本是Docker引入了镜像功能。
与系统级别的虚拟机不同,容器技术的虚拟其实是进程级别的。
图源极客时间从上图中可以看出,虚拟机是通过在宿主机上启动一个虚拟机监视器HyperVisor来管理控制虚拟机的,最经典的HyperVisor就是VirtualBox。与容器相比,虚拟机包含了整个客户操作系统,这也是二者性能差异的主要成因。
从启动角度来看,进程级别的容器启动肯定是比系统级别的虚拟机启动要快得多。从资源占用角度来看,容器是按需占用,虚拟机是全量占用。从资源利用率角度来看,多容器可以共享资源,而虚拟机是独享资源。从构建角度来看,容器是按需构建,体积小;虚拟机是全量构建,体积大。图源掘金小册-开发者必备的Docker实践指南Docker解决了什么问题?Docker是容器技术的一种体现,而容器技术的根本是为了抹平软件运行的环境差异。
如何抹平?Docker通过build命令将应用程序和其依赖构建成镜像文件,镜像是只读的,这意味着构建完成后,镜像不会再发生任何变化。如此,基于该镜像启动的容器环境必然一致。同理,跑在这个容器中的应用程序所处环境也一致。
此外,容器是完全使用沙箱机制,容器相互之间完全隔离,互不影响。
Docker有哪些适用场景?提供应用运行环境,辅助CI/CD工作流
作为Serverless的底层依赖,动态扩缩容
搭建微服务架构,可通过多容器运行多服务的形式对复杂项目进行拆解
部署环境迁移
other...
核心概念镜像Docker镜像是一个特殊的文件系统,它包含了容器运行时所需要的所有基础文件和配置信息,是容器启动的基础。这就意味着,必须先有镜像才能启动容器。
镜像可以自己构建也可以从镜像仓库[11]拉取。
容器镜像和容器的关系就好像类和实例的关系。一个类可以有很多实例,一个镜像也可以有很多容器。
容器不可以脱离镜像存在。事实上,可以理解为容器=容器层(可写)+镜像(只读)。
图源拉钩教育写时复制
这部分涉及到一个写时复制(copy-on-write)的技术,能够有效的提高磁盘的利用率。尤其是对读操作,十分的友好。
写时复制会将要修改的内容从镜像拷贝到容器的可读写层,并不会直接修改镜像的源文件。
这意味着即便是写的时候有其他容器对同一镜像资源进行读取,也不会发生资源不一致的情况。
多容器操作同一个文件也是各自在自己的容器空间创建读写层并完成修改,相互隔离,互不影响。
生命周期
每一个容器完整生命周期都具有创建,运行,停止,暂停,删除这五个状态。
图源百度图片创建:容器被创建,所需资源已就绪,容器中的应用程序尚未运行。运行:容器中的应用程序正在运行。
暂停:容器中的所有应用程序都处于暂停(非停止)状态。
停止:容器中的应用程序均已停止,但所占用资源不会释放。
删除:容器已删除,所占用资源全部释放。
值得注意的是:如果要删除一个镜像,必须保证没有容器占用,停止状态的容器也不行。
仓库Docker的镜像仓库(public/private)类似于代码仓库,可以用来存储和分发Docker镜像。
镜像,容器,代码仓库三者关系如下:
安装官方安装方式DockerDesktopDockerDesktop其本质是借助windows和macOS的平台特性,搭建了一个Linux环境,进而运行docker。
图源掘金小册-开发者必备的Docker实践指南mac[12]windows[13]镜像加速为使得在国内从DockerHub拉取镜像更快,可以配置镜像加速器。
阿里云加速器\(控制台-登录账号-应用搜索容器镜像服务-侧边栏选择镜像加速器-复制地址\)[14]网易云加速器