Docker底层原理浅析 | namespace+cgroups+文件系统

本文目录

1. Linux Namespace

Linux系统里是否只能有一个pid为1的进程?

先来考虑个问题,linux中pid为1的进程就是init进程,也就是系统的创始进程,如果创建了新的进程,那么这个pid就会向上增长。比如下面这张图的结构示意。

但是这样理解是不全面的,因为linux中有一个namespace机制,所以这个时候架构变成了四层。不同namespace中的进程列表是独立的,每个namespace中的pid都是从1开始的。

所以有namespace时候,应该讨论的是某某namespace中pid为1的进程。

在一般情况下正常使用linux的时候,一般都会说pid为1的进程,这个时候的pid为1的进程是在一个默认的namespace下的。

namespace机制

namespace机制提供一种资源隔离和虚拟化特性,基于这些特性,PID、IPC、Network等系统资源不再是全局性的,而是某个特定的namespace下面,每个namespace下面的资源对于其他的namespace是不可见的。因此操作系统的角度来看,就会有多个pid进程。但是从用户的角度来看,只能看到属于自己的namespace下的资源,这个时候使用ps命令只能列出自己namespace下的进程。这样每个namespace看上去就像一个单独的linux系统。

k8s底层用了docker技术、docker底层又使用了namespace机制,来实现docker和docker之间的隔离。

namespace有7种类型,分别是下面7种。

UTS表示是主机名和域名,所以在系统里面,启动docker之后,每个docker都有一个独立的hostname,说明docker之间、docker和主机之间的UTS是隔离的。也就是属于不同namespace的。

PID是进程编号,也就是上面提到的pid是独立的,都可以有pid为1的进程。

network类型的namespace导致网络、端口这些是独立的。

7种类型的namespace描述的都是系统资源,有了namespace,这些系统资源就不是全局的了。

查看namespace

通过 lsns 命令可以查看 系统里面所有namespace的类型。

可以看到有两种不同类型的net,所以这两个net下面的namespace是隔离的。

使用命令 lsns | grep init 可以看到init进程所属的namespace,有7种不同的namespace。

init进程是系统启动后的第一个进程,它的进程ID(PID)通常是1。init进程负责启动用户空间的初始化和系统服务的管理。在现代Linux系统中,init进程可能会创建多个命名空间(namespaces),以实现进程隔离和资源管理。

namespace机制测试

通过命令 unshare --frok --pid --mount-proc bash 可以验证namespace机制。

unshare 是一个 Linux 命令,它允许你启动一个新的进程,这个进程在某些方面与当前进程(通常是你的 shell)是隔离的。

这个命令的作用是在一个新的、独立的命名空间中启动一个新的 bash shell。这个新的 bash shell 将拥有自己的 PID 空间,并且可以看到该命名空间内进程的 /proc 信息。这种隔离可以用于测试、调试或创建一个隔离的环境,而不影响宿主机的其他进程。

如果两个进程所属的某一类型的namespace是一样的,那么会共享该资源,否则就是隔离的。

比如说Network的namespace是一样的,那么两个进程是可以直接通信的。

docker就是基于这种linux namespace机制去隔离的。

使用Docker验证namespace机制

首先输入命令 lsns > /tftpboot/lsns 把当前的namespace的输出保存到文件中。

运行命令 docker run -it ubuntu:18.04 /bin/bash,启动一个docker,运行这个命令时,Docker 会启动一个新的 Ubuntu 18.04 容器,并打开一个交互式的 Bash shell,允许你输入命令并立即看到结果。

然后通过 docker ps查看是否启动成功。

通过命令 lsns > /tftpboot/lsns-ubuntu 来把容器的输出存到文件中。

然后通过命令 sudo meld /tftpboot/lsns /tftpboot/lsns-ubuntu命令对比查看启动docker前后的文件是否有哪些不一样。

可以发现新创了5个namespace,并且这5个namespace都是docker里面的bash命令创建的,mnt、uts、ipc、pid、net这五种类型的namespace,所以跟主机在上面5个类型是自成一套的。

2. Dcoerk网络模式

使用命令 docker run创建容器的时候,可以用 --net 选项指定容器的网络模式,有以下4种网络模式可以选定:

1、host模式:和宿主机共用一个network namespace,容器中的网络环境,比方说ip地址、路由等,和宿主机的网络环境一模一样。直接通过 --net=host指定。

2、none模式:关闭了docker的网络功能。通过 --net =none指定。

3、bridge模式:默认设置,使用独立的network的 namespace,并连接到docker0虚拟网桥,通过iptables nat表配置和宿主机进行通信。通过命令--net=bridge指定。

在 Docker 中,虚拟网桥(如 docker0)用于连接容器和宿主机的网络。每个容器都会被分配一个虚拟网络接口(如 veth 接口),这个接口的一端连接到容器的网络命名空间,另一端连接到虚拟网桥。这样,容器就可以通过虚拟网桥与宿主机和其他容器进行通信。

Docker 中,iptables NAT 表配置用于实现容器与宿主机之间的网络通信。具体来说,iptables 会在宿主机上设置 NAT 规则,将发往容器 IP 地址的数据包转发到容器的虚拟网络接口上。这样,容器就可以通过宿主机的网络接口访问外部网络。

所以总的来说就是:在 Docker 中,bridge 模式是默认的网络模式。在这种模式下,Docker 会为每个容器创建一个独立的网络命名空间,并将其连接到一个虚拟网桥(如 docker0)。这样,容器就可以通过虚拟网桥与宿主机和其他容器进行通信。

同时,Docker 还会在宿主机上配置 iptables NAT 规则,使得容器可以通过宿主机的网络接口访问外部网络。具体来说,iptables 会在 POSTROUTING 链上设置 SNAT(Source Network Address Translation,源网络地址转换)规则,将容器发出的数据包的源地址转换为宿主机的 IP 地址。这样,外部网络就无法直接访问容器的 IP 地址,而是通过宿主机的 IP 地址进行通信。

4、container模式:指定新创建的容器和已经存在的一个容器共享一个network namespace,而不是和宿主机进行共享,新创建的容器不会创建自己的网卡,配置自己的ip,而是和指定的一个容器共享ip+端口范围等。通过 --net=container:NAME_OR_ID指定。

3.Control groups

Docker 引擎在 Linux 上还依赖另一种称为控制组(cgroups)的技术。控制组将应用程序限制在一组特定的资源上。控制组允许 Docker 引擎将可用的硬件资源分配给容器,并可选地强制执行限制和约束。比如可以限制特定容器可用的内存。

控制组(cgroups)是 Linux 内核提供的一种特性,用于限制、记录和隔离进程组(process groups)使用的物理资源(如 CPU、内存、磁盘 I/O 等)。cgroups 允许系统管理员定义资源分配策略,以优化系统资源的使用和提高系统性能。

在 Docker 中,cgroups 用于实现以下几个关键功能:

资源限制:cgroups 可以限制容器可以使用的资源量,例如 CPU 时间、内存、磁盘 I/O 等。这有助于防止单个容器占用过多资源,从而影响其他容器或宿主机的性能。

资源分配:cgroups 允许 Docker 引擎将硬件资源公平地分配给所有容器,确保每个容器都能获得所需的资源。

隔离性:cgroups 提供了资源隔离,使得容器之间的资源使用不会相互影响。这意味着一个容器的资源使用不会影响其他容器的运行。

监控和记录:cgroups 还可以记录容器的资源使用情况,这对于监控和分析容器的性能非常有用。

所以一句话解释就是说,合理分配系统的资源(内存、cpu、网卡、块设备的读写等),能够将服务器的资源发挥到极致。

4.文件系统(联合文件系统)

联合文件系统,也可以称为 UnionFS,是一种通过创建层来运作的文件系统,这使得它们非常轻量级和快速。Docker 引擎使用 UnionFS 来提供容器的构建模块。Docker 引擎可以使用多种 UnionFS 变体,包括 AUFS(最开始docker用的文件系统,最开始叫做another,后面改成adavance,linus觉得这个代码写的很烂,就没有合到内核里边)、btrfs、vfs (虚拟文件系统)和 DeviceMapper。

可以先理解一下文件系统是什么,比如输入ll的时候,会展示当前路径下的文件,文件系统就是通过一种形式,把文件和文件信息展示出来,比如文件目录、文件展示关系等。

联合文件系统(UnionFS)是一种允许将多个文件系统联合挂载到同一个挂载点的文件系统服务。它通过层(layers)的概念来工作,这些层可以看作是文件系统的快照或版本。UnionFS 的关键特性是可以将不同的层叠加在一起,形成一个统一的文件系统视图,而不需要实际地将所有层的内容合并到一个物理位置。

在Docker中,UnionFS 用于实现容器的文件系统层叠:

层叠文件系统:Docker 容器的文件系统是由多个只读层和一个可写层组成的。这些层可以是基础镜像层、用户添加的层以及容器运行时产生的层。

节省存储空间:由于多个容器可以共享相同的只读层,这大大减少了存储空间的消耗。只有当容器需要修改某个文件时,才会在可写层中创建一个副本。

提高构建速度:当构建 Docker 镜像时,每一层的更改都会作为新的层添加,这样可以快速地构建和更新镜像,而不需要重新构建整个文件系统。

支持多种变体:Docker 支持多种 UnionFS 的实现,包括 AUFS(Another UnionFS)、btrfs(一种支持快照功能的文件系统)、vfs(虚拟文件系统)和 DeviceMapper(一种基于块设备的存储映射技术)。这些变体各有优缺点,适用于不同的场景。

5. 容器格式

Docker 引擎利用 Linux 内核的特性(如命名空间、控制组和 UnionFS)来创建和管理容器, Docker 默认使用的容器格式是 libcontainer,未来可能会支持其他容器格式。

包装器Wrapper指的是 Docker 引擎将命名空间、控制组和 UnionFS 这些技术组合在一起,形成一个完整的容器运行环境。

相关推荐
大数据005 小时前
Docker慢慢学
mysql·docker·kafka·n8n
唐可盐5 小时前
解决docker运行zentao 报错:ln: failed to create symbolic link ‘/opt/zbox/tmp/mysq
运维·docker·容器
泯泷10 小时前
编写 Dockerfile:从入门到精通
后端·docker·容器
苏近之11 小时前
微服务版Hello World之k8s集群部署
运维·微服务·容器
方渐鸿13 小时前
【2025】使用docker compose一键部署项目到服务器(4)
java·docker·运维开发·持续部署
HalukiSan14 小时前
Docker_Desktop开启k8s
docker·容器
❀͜͡傀儡师14 小时前
完成一个可交互的k8s管理平台的页面开发
容器·kubernetes·交互
毒爪的小新16 小时前
Docker基础命令
linux·运维·docker·容器·centos
86Eric17 小时前
Docker 部署 Python 的 Flask项目
python·docker·flask·dockerfile
David爱编程19 小时前
彻底搞懂容器启动、停止、调试的每一个细节!
后端·docker·容器