容器-资源隔离机制
一. 引言:
大家都知道,在一台机器上,可以运行任意(根据系统资源)个容器实例。且各容器间是相互独立,不做任何关联的。那么,docker是通过什么方式来实现容器隔离的呢? 接下来我们了解下。
二. 关于容器隔离机制:
容器的隔离机制是实现 “轻量级虚拟化” 的核心,通过Linux 内核原生技术与容器运行时(如 docker、containerd)的封装,在共享宿主机内核的同时,为每个容器提供独立的资源、网络、文件系统等环境,避免容器间相互干扰。其本质是 “在同一内核中为进程组划定资源与权限边界”,而非像虚拟机那样完全隔离操作系统。
主要分为一下三个大方向:
Namespace 可实现容器间的隔离
Cgroups 可限制容器的资源使用
Chroot 文件系统的隔离(挂载到物理根目录(文件系统)下的子目录(虚拟文件系统))
1. 关于namespace:
Linux 内核从版本 2.4.19 开始陆续引入了 namespace 的概念。其目的是将某个特定的全局系统资源(global system resource)通过抽象方法使得namespace 中的进程看起来拥有它们自己的隔离的全局系统资源实例。Linux 内核中实现了六种 namespace,按照引入的先后顺序,列表如下:
namespace | 引入的相关内核版本 | 被隔离的全局系统资源 | 在容器语境下的隔离效果 |
---|---|---|---|
Mount | Linux 2.4.19 | 文件系统挂接点 | 每个容器能看到不同的文件系统层次结构 |
UTS | Linux 2.6.19 | 主机名和域名 | 每个容器可以有自己的 hostname 和 domainame |
IPC | Linux 2.6.19 | System V IPC:消息队列,信号量,共享内存 | 每个容器有其自己的 System V IPC 和消息队列文件系统,因此,只有在同一个 IPC namespace 的进程之间才能互相通信 |
PID | Linux 2.6.24 | 进程 ID(编号) | 每个 PID namespace 中的进程可以有其独立的 PID; 每个容器可以有其 PID 为 1 的root 进程;也使得容器可以在不同的 host 之间迁移,因为 namespace 中的进程 ID 和 host 无关了。这也使得容器中的每个进程有两个PID:容器中的 PID 和 host 上的 PID。 |
Network | 始于Linux 2.6.24 完成于 Linux 2.6.29 | 网络相关的系统资源 | 每个容器用有其独立的网络设备,IP 地址,IP 路由表,/proc/net 目录,端口号等等。这也使得一个 host 上多个容器内的同一个应用都绑定到各自容器的 80 端口上。 |
User | 始于 Linux 2.6.23 完成于 Linux 3.8) | 用户和组 ID 空间 | 在 user namespace 中的进程的用户和组 ID 可以和在 host 上不同; 每个 container 可以有不同的 user 和 group id;一个 host 上的非特权用户可以成为 user namespace 中的特权用户; |
Linux内核实现namespace的主要目的就是为了实现轻量级虚拟化(容器)服务。在同一个namespace下的进程可以感知彼此的变化,而对外界的进程一无所知。这样就可以让容器中的进程产生错觉,仿佛自己置身于一个独立的系统环境中,以此达到独立和隔离的目的。
2. Cgroups:
容器通过namespace机制实现了环境上的隔离。但只有运行环境隔离还不够,因为这些进程还是可以不受限制地使用系统资源,比如网络、磁盘、CPU以及内存 等。一方面,是为了防止它占用了太多的资源而影响到其它进程;另一方面,在系统资源耗尽的时候,linux 内核会触发 OOM,这会让一些被杀掉的进程成了无辜的替死鬼。因此,为了让容器中的进程更加可控,Docker 使用 Linux cgroups 来限制容器中的进程允许使用的系统资源。
Linux Cgroup 可为系统中所运行任务(进程)的用户定义组群分配资源 — 比如 CPU 时间、系统内存、网络带宽或者这些资源的组合。可以监控您配置的 cgroup,拒绝 cgroup 访问某些资源,甚至在运行的系统中动态配置您的 cgroup。它以一组进程为目标进行系统资源分配和控制。
它主要提供了以下功能:
Resource limitation: 限制资源使用,比如内存使用上限以及文件系统的缓存限制。
Prioritization: 优先级控制,比如:CPU利用和磁盘IO吞吐。
Accounting: 资源统计,一些审计或一些统计,主要目的是为了计费。
Control: 进程控制,挂起进程,恢复执行进程。
使用 cgroup,系统管理员可更具体地控制对系统资源的分配、优先顺序、拒绝、管理和监控。可更好地根据任务和用户分配硬件资源,提高总体效率。在实际中,系统管理员一般会利用CGroup做下面这些事:
隔离一个进程集合(比如:nginx的所有进程),并限制他们所消费的资源,比如绑定CPU的核。
为这组进程分配其足够使用的内存
为这组进程分配相应的网络带宽和磁盘存储限制
限制访问某些设备(通过设置设备的白名单)
3. Chroot:
是容器的 “独立文件系统根目录”,通过分层挂载(UnionFS) 实现,核心作用是:
1. 隔离性:容器内的/目录与宿主机完全独立,修改容器内文件(如/etc/passwd)不会影响宿主机。
2. 轻量性:采用 “基础镜像层 + 容器层” 的分层结构,多个容器可共享基础镜像层。
3. 一致性:每个容器启动时都基于相同的基础镜像,确保运行环境一致(避免 “本地能跑,线上跑不了”)。
关键技术: UnionFS(联合文件系统)
常用的 UnionFS 实现包括overlay2(Docker 默认)、aufs、btrfs等,以overlay2为例,其分层结构如下:
lowerdir:基础镜像层(可多层,如 “操作系统层 + Nginx 层”),仅可读;
upperdir:容器层,容器运行时的所有写操作(如创建 / 修改文件)都在这一层;
merged:容器内看到的 “根目录”,是lowerdir和upperdir的联合挂载结果;
workdir:临时工作目录,用于 UnionFS 的内部操作(用户不可见)。
三. 容器隔离 vs 虚拟机隔离:
对比维度 | 容器 | 虚拟机 |
---|---|---|
隔离核心 | 共享宿主机内核,通过 namespace/cgroup 隔离进程 | 完全隔离操作系统(含内核),通过 hypervisor 虚拟化硬件 |
资源开销 | 轻量(MB 级内存,秒级启动),共享宿主机资源 | 重量级(GB 级内存,分钟级启动),每个 vm 需独立分配资源 |
安全性 | 较弱(共享内核,存在 “内核漏洞突破隔离” 风险) | 较强(完全隔离内核,VM 内攻击难以影响宿主机) |
兼容性 | 依赖宿主机内核版本(如容器内无法运行与宿主机内核不兼容的程序) | 兼容性强(VM 内可运行任意操作系统,与宿主机内核无关) |
四. 无法隔离的资源:
- 内核版本:容器内的内核版本与宿主机完全一致,无法在容器内升级内核;
- 内核模块:容器无法加载 / 卸载宿主机内核模块(需宿主机提前加载);
- 系统时间:默认情况下,容器内修改系统时间会同步修改宿主机时间。
- 硬件资源:部分硬件资源(如 CPU 缓存、NUMA 节点)无法精细隔离,可能存在 “缓存侧信道攻击” 风险;
- 内核漏洞:若宿主机内核存在漏洞(如 Dirty COW),容器内的恶意进程可能利用漏洞突破隔离,获取宿主机权限。
- --------------------------------------------------------------------------------------------------------------------------
深耕运维行业多年,擅长运维体系建设,方案落地。欢迎交流!
“V-x”: ywjw996
《 运维经纬 》