Docker底层原理
Docker 利用了 Linux 内核的特性来实现轻量级的虚拟化。
以下是Docker实现容器化所依赖的底层Linux技术:
- 命名空间(Namespaces):为容器提供隔离的工作空间,使得容器间互不影响。Docker使用了以下几种命名空间:
- PID命名空间:进程隔离。
- NET命名空间:网络隔离。
- IPC命名空间:进程间通信隔离。
- MNT命名空间:文件系统挂载点隔离。
- UTS命名空间:主机名和域名隔离。
- 控制组(Cgroups):限制、记录和隔离进程组所使用的物理资源(如CPU、内存、磁盘I/O等)。
- 联合文件系统(Union File Systems):允许将多个目录挂载到同一个虚拟文件系统下,形成一种分层的结构,Docker通常使用的是OverlayFS或AUFS。
命名空间
命名空间(Namespaces)是Linux内核的一个特性,它为容器提供隔离的工作环境,使得每个容器都像是在独立的系统中运行一样。
命名空间 关注的是隔离性,确保每个容器都认为自己是在一个独立的环境中运行,与其他容器和宿主机隔离。
1. 进程命名空间(PID Namespace)
进程命名空间允许每个容器拥有自己的进程树,容器内的进程对于容器外的进程来说是不可见的。
在容器内部,进程ID(PID)从1开始,通常1号进程是容器的初始化进程(如init
或bash
)。
在容器中运行的进程在该容器的 PID 命名空间中可能具有 PID 1,但在这个命名空间之外(即宿主机上),它的 PID 会是不同的值。
这样,容器内的进程就可以独立于宿主机和其他容器的进程运行。
2. 网络命名空间(NET Namespace)
网络命名空间为容器提供了独立的网络堆栈,包括网络设备、IP地址、路由表、端口号等。
这意味着每个容器都可以有自己的网络接口、IP地址和端口空间,容器之间的网络默认是隔离的。
Docker通过创建新的网络命名空间,并在其中配置网络接口来实现网络隔离。
3. IPC命名空间(IPC Namespace)
IPC(Inter-Process Communication)命名空间隔离了进程间的通信机制,如消息队列、信号量、共享内存等。
每个IPC命名空间都有自己的IPC标识符和资源,因此容器内的进程只能与同一命名空间内的其他进程进行IPC通信。
4. 挂载命名空间(MNT Namespace)
挂载命名空间提供了独立的文件系统挂载点视图。
每个挂载命名空间都有自己的挂载点列表,这意味着容器可以有自己的文件系统层次结构,而不会影响到宿主机或其他容器的文件系统。
Docker使用挂载命名空间来实现容器的文件系统隔离。
5. UTS命名空间(UTS Namespace)
UTS命名空间允许容器拥有独立的hostname和NIS域名。
这允许容器设置自己的主机名,而不会影响到宿主机或其他容器的设置。
6. 用户命名空间(User Namespace)
用户命名空间允许每个容器有自己的用户和组ID空间。
这意味着容器内的用户和组ID可以映射到宿主机上的不同ID,从而提供了额外的安全性和隔离性。
例如,一个容器内的进程可以以 UID=0 运行,但这并不意味着它具有宿主机上的 root 权限。
容器内的root用户可能映射到宿主机上的非root用户。
7. Cgroup命名空间(Cgroup Namespace)
Cgroup命名空间隔离了cgroup的视图,使得容器内的进程只能看到自己的cgroup树,而看不到宿主机的cgroup树。这有助于限制容器内的进程对宿主机资源的访问。
如何使用命名空间
在Linux系统中,可以通过以下步骤创建和使用命名空间:
- 使用
clone
系统调用或unshare
命令创建新的命名空间。 - 在新的命名空间内执行进程。 例如,以下命令使用
unshare
创建一个新的网络命名空间:
unshare --net bash
在这个新的bash shell中,你可以配置自己的网络堆栈。
命名空间是实现容器隔离的关键技术,它使得容器可以在不影响宿主机和其他容器的情况下运行。Docker和其他容器技术利用这些命名空间来创建轻量级、隔离的运行环境。
控制组
控制组(Control Groups,简称Cgroups)是Linux内核的一个特性,用于限制、记录和隔离进程组使用的物理资源(如CPU、内存、磁盘I/O等)。
Cgroups是 Docker 容器技术实现资源管理的关键组件。
cgroups 通过将进程组织成组,并为这些组设置资源使用限制来工作。每个组可以被视为一个容器,其中包含了相关的进程。cgroups 通过在内核级别为这些进程组定义资源限制,从而实现了对资源的有效管理和监控。
cgroups 关注的是资源管理和限制,确保容器不会过度消耗宿主机资源,并能公平地分配资源。
Cgroups的作用
- 资源限制:为进程组设置资源使用上限,如CPU使用时间、内存使用量等。
- 优先级:设置进程组的优先级,影响它们在资源竞争中的表现。
- 资源监控:记录进程组使用的资源情况,如CPU时间、内存使用量等。
- 隔离:为进程组提供隔离的运行环境,确保它们不会相互干扰。
Cgroups组件
- 任务(Tasks):在Cgroups术语中,任务是指进程或线程。
- 控制组(Cgroups):控制组是资源限制的单位,可以包含一个或多个任务。每个控制组可以设置不同的资源限制。
- 子系统(Subsystem):子系统是资源管理的模块,每个子系统负责一种资源的限制。例如,
cpu
子系统负责CPU时间分配,memory
子系统负责内存使用限制。 - 层级(Hierarchy):层级是控制组的树状结构,子系统可以附加到层级上,从而控制层级中的所有控制组。
Cgroups子系统
- cpu:限制CPU时间分配。
- cpuacct:提供CPU资源使用报告。
- memory:限制内存使用量。
- devices:控制任务对设备的访问。
- freezer:挂起或恢复任务。
- blkio:限制块设备I/O。
- net_cls:标记网络数据包,用于流量控制。
- net_prio:设置网络流量优先级。
- hugetlb:限制大页内存使用。
Cgroups的使用示例
以下是一个简单的示例,创建一个控制组并限制其CPU使用:
查看代码
# 创建一个层级并附加cpu子系统
mkdir /sys/fs/cgroup/cpu/my_cgroup
# 启用CPU使用统计
echo 1 > /sys/fs/cgroup/cpu/my_cgroup/cpuacct.usage
# 限制CPU使用率为50%
echo 50000 > /sys/fs/cgroup/cpu/my_cgroup/cpu.cfs_period_us
echo 25000 > /sys/fs/cgroup/cpu/my_cgroup/cpu.cfs_quota_us
# 将进程添加到控制组
echo $$ > /sys/fs/cgroup/cpu/my_cgroup/tasks
# 现在,当前shell的CPU使用将受到限制
在这个示例中,cpu.cfs_period_us
设置了CPU分配周期(100ms),而cpu.cfs_quota_us
设置了在这个周期内允许使用的CPU时间(50ms),从而实现了50%的CPU使用率限制。
Docker 中的 cgroups 使用
在 Docker 中,cgroups 被用来限制和监控容器使用的资源。当启动一个 Docker 容器时,Docker 会为该容器创建一个 cgroups 组,并根据 Dockerfile 或 docker run
命令中指定的参数来设置资源限制。
例如,如果想要限制一个容器使用的 CPU 时间和内存大小,可以在 docker run
命令中使用如下选项:
docker run --cpu-period=100000 --cpu-quota=50000 --memory=512m image_name
这里:
--cpu-period
设置了 CPU 时间片的周期为 100ms。--cpu-quota
设置了容器可以使用的 CPU 时间为 50%(50ms)。--memory
设置了容器可以使用的最大内存为 512MB。
cgroups 为 Docker 提供了一种强大的机制来限制和管理容器使用的资源。通过设置合适的 cgroups 参数,可以确保容器不会过度消耗宿主机的资源,同时还可以确保容器之间的资源使用不会互相干扰。
联合文件系统
联合文件系统(Union File System,简称UnionFS)是一种文件系统,它可以将多个目录合并成一个逻辑上的单一文件系统。在联合文件系统中,这些目录被称为“分支”,每个分支都可以被读写或只读。联合文件系统允许Docker容器以分层的方式构建和运行。
关键特性
- 层叠: 多个文件系统层可以叠加在一起,形成一个统一的文件系统视图。
- 透明覆盖: 如果顶层文件系统中的文件被修改或删除,那么这个变化会覆盖掉下层文件系统中的相应文件。
- 只读和可写层: 通常有一个或多个只读层作为基础,上面是一个或多个可写层。只读层提供基本的文件系统内容,而可写层允许对文件系统进行修改。
工作原理
联合文件系统的工作原理是将多个文件系统的目录合并为一个。这些目录可以是不同的物理位置,但在用户看来它们就像是一个统一的文件系统。联合文件系统通常采用以下策略来处理这些目录:
- 写时复制(Copy-on-Write, CoW):当需要修改一个文件时,联合文件系统会先将该文件从只读层复制到可写层,然后在可写层进行修改。这样,原始的只读层保持不变,而修改后的文件在可写层中可见。
- 写时分配(Allocate-on-Write):与写时复制类似,但它是针对文件块级别的操作。只有当需要修改文件块时,才会分配新的块进行写入。
联合文件系统的类型
- AUFS(Advanced Multi-Layered Unification Filesystem):Docker早期版本中使用的联合文件系统。它支持多个只读分支和一个可写分支。
- OverlayFS:一个较新的联合文件系统,与AUFS类似,但性能更好,现在是Docker的默认文件系统。
- Device Mapper:一种逻辑卷管理器,也可以用来实现联合文件系统的功能。
- Btrfs(B-Tree Filesystem):一种支持写时复制和快照的文件系统,也可以用作联合文件系统。
- VFS2:是Docker的下一代存储驱动,旨在提高性能和可扩展性。
在容器中的应用
在 Docker 中,联合文件系统主要用于镜像和容器文件系统的管理。
以下是 Docker 中 UnionFS 的几个关键方面:
- 镜像层: Docker 镜像由一系列只读层组成,每个层代表一次变更或提交。当创建一个新的镜像时,每一层都代表了对前一层的增量变更。
- 容器文件系统: 当启动一个容器时,Docker 会在镜像的最后一层之上添加一个可写层。这个可写层就是容器的实际文件系统。容器对文件系统的任何修改都会发生在这个可写层中。
- 分层: Docker 使用分层的概念来提高效率。由于镜像的底层通常不变,因此可以被多个容器共享。这样就减少了磁盘空间的使用,并提高了文件系统的加载速度。
工作流程
- 基础镜像: 一个基础镜像包含操作系统的基本文件和目录。
- 中间层: 在基础镜像之上添加一个或多个中间层,每个层代表安装了一些软件包或进行了其他配置更改。
- 容器文件系统: 当启动容器时,Docker 会在镜像的最顶层添加一个可写层,这就是容器实际运行时的文件系统。
当容器运行时,任何对文件系统的更改都发生在可写层中,而基础镜像和中间层保持不变。这样,即使有多个容器基于相同的镜像运行,它们可以共享相同的底层文件系统,而各自的更改则分别保存在各自的可写层中。
联合文件系统使得 Docker 能够高效地管理和分发镜像,同时支持容器的快速启动和销毁。
通过使用联合文件系统,Docker 实现了高效的文件系统分层和共享,这是 Docker 能够提供高性能和灵活性的关键所在。