深度解析 Docker 底层原理:核心技术与架构全揭秘

容器技术,作为一种操作系统层面的虚拟化技术,其核心在于利用 Linux 内核的 cgroup(控制组)和 namespace(命名空间)等关键技术,对进程进行精细的封装与隔离,从而实现多个容器在同一操作系统上独立运行,互不干扰,极大地提高了资源利用率和应用部署效率。

容器技术从简单到复杂的演化过程示意图

Docker 采用 Client-Server(客户端 - 服务器)架构,Docker 守护进程在主机上运行,负责管理容器的创建、启动、停止等操作。客户端通过 Socket 连接与 Docker 守护进程进行通信,发送指令并接收反馈,实现对容器的各种操作,这种架构使得用户可以方便地在本地或远程主机上管理和操作 Docker 容器。

Docker 详细介绍可查看 Docker 指南

Dockers 核心技术

Docker 的实现离不开 Linux 的三大核心技术支撑:命名空间(Namespace)、控制组(cgroups)和联合文件系统(UnionFS)。 正是这三大技术的巧妙结合,使得 Docker 能够以轻量级的方式实现容器的隔离与管理,成为容器化技术的代表之一,推动了现代云计算和微服务架构的发展。

  • namespace:命名空间,容器隔离的基础,保证A容器看不到B容器.
  • cgroups(Control Group):控制组,容器资源统计和隔离。
  • UnionFS:联合文件系统,aufs/overlayfs,分层镜像实现的基础。

实际上 Docker 是使用了很多 Linux 的隔离功能,让容器看起来像一个轻量级虚拟机在独立运行,容器的本质是被限制了的 Namespaces,cgroup,具有逻辑上独立文件系统,网络的一个进程。

Docker 依赖的三大核心技术及其作用示意

Namespaces 命名空间

在 Linux 系统中,Namespace(命名空间)是一种在内核级别对系统资源进行抽象封装的机制。它通过将系统资源分配到不同的命名空间中,实现资源的隔离。处于不同命名空间中的进程,各自拥有一份独立的系统资源视图,从而确保容器之间的资源相互隔离,互不干扰,为容器的独立运行提供了基础保障。

Linux 的命名空间机制提供了以下七种不同的命名空间,包括 :

Linux 命名空间

通过这七个选项, 我们能在创建新的进程时, 设置新进程应该在哪些资源上与宿主机器进行隔离。具体如下:

NamespaceFlagPageIsolates
CgroupCLONE_NEWCGROUPcgroup_namespacesCgroup root directory
IPCCLONE_NEWIPCipc_namespacesSystem V IPC,POSIX message queues 隔离进程间通信
NetworkCLONE_NEWNETnetwork_namespacesNetwork devices,stacks, ports, etc. 隔离网络资源
MountCLONE_NEWNSmount_namespacesMount points 隔离文件系统挂载点
PIDCLONE_NEWPIDpid_namespacesProcess IDs 隔离进程的ID
TimeCLONE_NEWTIMEtime_namespacesBoot and monotonic clocks
UserCLONE_NEWUSERuser_namespacesUser and group IDs 隔离用户和用户组的ID
UTSCLONE_NEWUTSuts_namespacesHostname and NIS domain name 隔离主机名和域名信息

对于docker来说,最为直接的,是PID隔离

PID隔离

Process ID

要深入理解 PID(进程 ID)的命名空间隔离,我们可以从 Linux 进程的基础系统调用函数 fork() 开始探讨。虽然 fork() 函数本身并不直接属于命名空间的 API,但它在进程创建和隔离过程中起着关键作用。当一个进程调用 fork() 函数时,系统会创建一个新的子进程,子进程继承了父进程的大部分资源,但在 PID 命名空间隔离的情况下,子进程在自己的命名空间中拥有唯一的 PID,与父进程的 PID 相互独立,这为容器中进程的管理和识别提供了重要依据。

当程序调用fork()函数时,系统会创建新的进程,为其分配资源,例如存储数据和代码的空间。然后把原来的进程的所有值都复制到新的进程中,只有少量数值与原来的进程值不同,相当于克隆了一个自己。那么程序的后续代码逻辑要如何区分自己是新进程还是父进程呢?

fork()的神奇之处在于它仅仅被调用一次,却能够返回两次(父进程与子进程各返回一次),通过返回值的不同就可以进行区分父进程与子进程。它可能有三种不同的返回值:

  • 在父进程中,fork返回新创建子进程的进程ID
  • 在子进程中,fork返回0
  • 如果出现错误,fork返回一个负值

网络隔离

虽然 Docker 容器通过 Linux 的命名空间实现了与宿主机进程的网络隔离,但如果容器无法借助宿主机的网络与整个互联网进行通信,其应用范围将受到极大限制。因此,Docker 提供了多种网络模式,如 Host 模式、Container 模式、None 模式和 Bridge 模式,以满足不同场景下容器与外部网络的连接需求,确保容器内的应用能够正常访问互联网资源,发挥其应有的作用。

所以 Docker 虽然可以通过命名空间创建一个隔离的网络环境,但是 Docker 中的服务仍然需要与外界相连才能发挥作用。每一个使用 docker run 启动的容器其实都具有单独的网络命名空间,Docker 为我们提供了四种不同的网络模式:

  • Host、
  • Container、
  • None
  • Bridge 模式。

UnionFS 联合文件系统

什么是镜像?

那么问题来了,没有操作系统,怎么运行程序?

可以在Docker中创建一个centos的镜像文件,这样就能将centos系统集成到Docker中,运行的应用就都是centos的应用。

Docker Image(镜像)是 Docker 部署的核心组件,它包含了应用程序及其依赖的文件和运行环境。从外部来看,Docker 镜像以一个文件的形式呈现,更准确地说,它是一个 mount 点(挂载点),通过联合文件系统(UnionFS)将多个文件系统层合并在一起,形成一个完整的文件系统视图,为容器的运行提供了必要的文件和目录结构。

从右侧以 UFS 的视角来看,lowerdir 和 upperdir 是两个不同的目录,UFS 将二者合并起来,得到 merged 层展示给调用方。从左侧的 docker 角度来理解,lowerdir 就是镜像,upperdir 就相当于是容器默认的可写层。

容器可以近似理解为镜像的运行时实例,默认情况下也算是在镜像层的基础上增加了一个可写层。所以,一般情况下如果你在容器内做出的修改,均包含在这个可写层中。

UnionFS 与AUFS

UnionFS 其实是一种为 Linux 操作系统设计的用于把多个文件系统『联合』到同一个挂载点的文件系统服务。

AUFS(Advanced UnionFS)其实就是 UnionFS 的升级版,它能够提供更优秀的性能和效率。

AUFS 作为先进联合文件系统,它能够将不同文件夹中的层联合(Union)到了同一个文件夹中,这些文件夹在 AUFS 中称作分支,整个『联合』的过程被称为联合挂载(Union Mount)。

总结:Dockers = LXC + AUFS

Docker为 LXC + AUFS 组合:

  • LXC 负责资源管理;
  • AUFS 负责镜像管理;

而LXC包括cgroup,namespace,chroot等组件,并通过cgroup资源管理,那么,从资源管理的角度来看,Docker、Lxc、Cgroup三者的关系是怎样的呢?

cgroup是在底层落实资源管理,LXC在cgroup上面封装了一层,随后,docker有在LXC封装了一层;

Cgroup其实就是linux提供的一种限制,记录,隔离进程组所使用的物理资源管理机制;也就是说,Cgroup是LXC为实现虚拟化所使用资源管理手段,我们可以这样说,底层没有cgroup支持,也就没有lxc,更别说docker的存在了,这是我们需要掌握和理解的,三者之间的关系概念

我们在把重心转移到LXC这个相当于中间件上,上述我们提到LXC是建立在cgroup基础上的,我们可以粗略的认为LXC=Cgroup+namespace+Chroot+veth+用户控制脚本;LXC利用内核的新特性(cgroup)来提供用户空间的对象,用来保证资源的隔离和对应用系统资源的限制;

Docker容器的文件系统最早是建立在Aufs基础上的,Aufs是一种Union FS,简单来说就是支持将不同的目录挂载到同一个虚拟文件系统之下

并实现一种laver的概念,

由于Aufs未能加入到linux内核中,考虑到兼容性的问题,便加入了Devicemapper的支持,Docker目前默认是建立在Devicemapper基础上,

**devicemapper用户控件相关部分主要负责配置具体的策略和控制逻辑,**比如逻辑设备和哪些物理设备建立映射,怎么建立这些映射关系等,而具体过滤和重定向IO请求的工作有内核中相关代码完成,因此整个device mapper机制由两部分组成--内核空间的device mapper驱动,用户控件的device mapper库以及它提供的dmsetup工具;

Docker Vs. VM

VM (VMware),虚拟机。

架构上:

  • 与传统的虚拟机(VM)相比,Docker 具有更少的抽象层。
  • Docker 直接利用宿主机的操作系统内核,而虚拟机则需要在宿主机上安装完整的 Guest OS(客户操作系统)。这使得 Docker 容器在启动速度、资源占用和性能等方面具有显著优势,能够更快速地启动和运行应用,同时占用更少的系统资源,提供接近原生的性能表现。

Docker Vs VM

流程上:

  • VM(VMware )在宿主机器、宿主机器操作系统的基础上创建虚拟层、虚拟化的操作系统、虚拟化的仓库,然后再安装应用;
  • Docker容器(Container) ,在宿主机器、宿主机器操作系统上创建Docker引擎,在引擎的基础上再安装应用。

所以说,新建一个容器的时候,docker不需要像虚拟机一样重新加载一个操作系统,避免引导。docker是利用宿主机的操作系统,省略了这个复杂的过程,秒级;虚拟机是加载Guest OS ,这是分钟级别的。

总览

特性容器虚拟机
启动速度秒级分钟级
硬盘使用一般为MB一般为GB
性能接近原生弱于原生
系统支持量单机支持上千个容器一般几十个

扩展内容

Docker 镜像分层机制

Docker Image 是有一个层级结构的,最底层的 Layer 为 BaseImage(一般为一个操作系统的 ISO 镜像),然后顺序执行每一条指令,生成的 Layer 按照入栈的顺序逐渐累加,最终形成一个 Image。

镜像分层机制

Docker Image 如何而来呢?

简单来说,一个 Image 是通过一个 DockerFile 定义的,然后使用 docker build 命令构建它。

DockerFile 中的每一条命令的执行结果都会成为 Image 中的一个 Layer。

参照