基本架构

Docker 采用了 C/S 架构,包括客户端和服务端。Docker 守护进程作为服务端接收来自客户端的请求,并处理这些请求(创建、运行、分发容器等)。

客户端和服务端可以运行在一个机器上,也可以通过 socket 或者 RESTful API 来进行通信。

Docker 守护进程一般在宿主机后台运行,等待接收来自客户端的请求。

Docker 客户端则为用户提供了一系列命令,用户可以使用这些命令来与 Docker 守护进程进行交互。

命名空间

命名空间是 Linux 内核一个强大的特性。每个容器都有自己独立的命名空间,运行在其中的应用都像是在独立的系统中运行一样。命名空间保证了容器之间彼此隔离,不会相互影响。

PID 命名空间

不同用户的进程就是通过 PID 命名空间来隔离的,且不同命名空间中的进程 ID 可以相同。所有 LXC 进程都是 Docker 守护进程的子进程,每个 LXC 进程具有不同的命名空间。同时由于允许嵌套,可以实现容器内运行容器的功能,提供更灵活的部署方式。

LXC: Linux Container,是一种操作系统级别的虚拟化技术。

NET 命名空间

网络隔离通过 NET 命名空间实现,每个 NET 命名空间都有自己的网络设备、IP 地址、路由表、防火墙规则等。这样就可以实现容器之间的网络隔离。Docker 默认使用 Veth 的方式,将容器中的虚拟网卡同 Host 上的一个 Docker 网桥 docker0 相连。

IPC 命名空间

在容器中,进程间通信(IPC)采用了 Linux 中常见的方式,如信号量、消息队列和共享内存等。与虚拟机(VM)不同,容器内的进程实际上是运行在宿主机(Host)上,并共享相同的进程 ID(PID)命名空间。因此,在申请 IPC 资源时,需要加入命名空间信息以区分不同容器。每个 IPC 资源都有一个唯一的 32 位标识符(ID)。

IPC: Inter-Process Communication,进程间通信。

MNT 命名空间

类似 chroot,将一个进程放到一个特定的目录执行,MNT 命名空间允许不同命名空间的进程看到不同的文件系统层次结构。这样就可以实现容器之间的文件系统隔离。和 chroot 不同的是,每个命名空间中的容器在 /proc/mounts 中只包含所在命名空间的挂载点信息。

chroot: 一种改变进程根目录的技术,可以将进程的根目录改变为其他目录,这样进程就无法访问根目录之外的文件。

UTS 命名空间

UTS 命名空间主要用于隔离主机名和域名。每个 UTS 命名空间都有自己的主机名和域名信息。使其在网络上可以被视作一个独立的节点而不是主机上的一个进程。

UTS: Unix Timesharing System,Unix 分时系统。

USER 命名空间

用户和组 ID 是相对于容器内部的文件系统和进程空间的。这意味着一个容器内的用户和组 ID 可能与宿主机上的用户和组 ID 完全不同。当在容器内运行程序时,程序将以容器内部的用户身份执行,而不是宿主机上的用户。

控制组

控制组(Cgroup)是 Linux 内核的一个特性,用于限制、记录和隔离一组进程的资源使用。Cgroup 可以限制 CPU、内存、磁盘 I/O、网络带宽等资源的使用。避免当多个容器同时运行时,对系统资源的竞争。

联合文件系统

联合文件系统(UnionFS)是一种分层、轻量级的文件系统,它允许将多个文件系统挂载到同一个目录下。Docker 使用联合文件系统来实现镜像的分层存储和容器的文件系统。

联合文件系统是 Docker 镜像的基础,它允许 Docker 镜像由多个只读层叠加在一起,形成一个新的镜像。这样就可以实现镜像的复用,减少存储空间。

Docker 中使用的 AUFS 就是一种联合文件系统。AUFS 支持为每一个成员目录设定只读、读写和写出权限,同时 AUFS 里有一个类似分层的概念,对只读权限的分支可以逻辑上进行增量地修改(不影响只读部分)。

目前支持的联合文件系统包括:OverlayFS、Aufs、DeviceMapper、Btrfs、ZFS 等。

各 Linux 发行版 Docker 推荐使用的存储驱动如下表:

发行版推荐存储驱动
UbuntuOverlay2
DebianOverlay2, AUFS, DeviceMapper
CentOSOverlay2
FedoraOverlay2

推荐使用 Overlay2 存储驱动,Overlay2 是目前 Docker 默认的存储驱动,以前是 AUFS。

容器格式

最初 Docker 使用的容器格式是 LXC,后来 Docker 开发了自己的容器格式 libcontainer。现在已经演变成 runC 和 Containerd。

网络

Docker 的网络实现其实就是利用了 Linux 上的网络命名空间和虚拟网络设备(特别是 Veth Pair)。 TODO