【Linux】资源隔离机制 — 命名空间(Namespace)详解
文章目录
- 一、什么是Namespace
- 1.1 Namespace 的作用与原理
- 1.2 命名空间的操作
- 1.3 常见命名空间
- 1.4 如何隔离两个容器中的进程
- 二、相关命令
- 2.1 dd 命令
- 2.2 mkfs 命令
- 2.3 df 命令
- 2.4 mount 命令
- 2.5 unshare 命令
- 三、实际操作
- 3.1 PID 隔离
- 3.2 Mount 隔离
一、什么是Namespace
Namespace(命名空间) 是 Linux 内核提供的一种资源隔离机制。
-
它允许将系统资源分隔成多个虚拟的“空间”,每个命名空间内的进程只能访问该命名空间下的资源,而不能访问其他命名空间中的资源。
-
通过
namespace
,不同的进程可以在同一个操作系统内共享硬件资源,但又能感知到各自独立的环境。具体来说,namespace
可以将一个或多个进程的资源隔离到同一个命名空间中,确保这些进程只能看到和操作该命名空间内的资源。
1.1 Namespace 的作用与原理
Linux namespaces
是对全局系统资源的一种封装与隔离。处于不同 namespace
的进程拥有独立的全局系统资源。修改某个 namespace
中的资源(如网络、PID、文件系统等)只会影响该命名空间中的进程,不会影响其他命名空间中的进程。
例如:
- 一个进程可能在一个独立的网络命名空间中运行,无法看到其他命名空间的网络接口和路由。
- 另一个进程可能在不同的用户命名空间中运行,从而拥有不同的用户ID和权限。
1.2 命名空间的操作
Linux 提供了几个系统调用来操作命名空间,常用的 API 包括:
clone()
:创建新进程并指定要使用的命名空间。setns()
:使当前进程加入指定的命名空间。unshare()
:将当前进程从现有的命名空间中分离出来,并创建新的命名空间。
在使用这些 API 时,通常需要指定一些参数来选择隔离的资源类型。
例如,可以通过按位或 |
组合多个命名空间类型来同时隔离多个资源。
1.3 常见命名空间
下面是一些常见的命名空间及其对应的系统调用参数和功能:
命名空间 | 系统调用参数 | 被隔离的全局系统资源 | 引入内核版本 |
---|---|---|---|
UTS | CLONE_NEWUTS | 主机名和域名 | 2.6.19 |
IPC | CLONE_NEWIPC | 信号量、消息队列和共享内存(进程间通信) | 2.6.19 |
PID | CLONE_NEWPID | 进程编号 | 2.6.24 |
Network | CLONE_NEWNET | 网络设备、网络栈、端口等 | 2.6.29 |
Mount | CLONE_NEWNS | 文件系统挂载点 | 2.4.19 |
User | CLONE_NEWUSER | 用户和用户组 | 3.8 |
下面是上面的命名空间容器下的隔离效果:
- UTS:每个容器可以拥有独立的主机名和域名,容器内的进程只看到自己的 hostname,互不干扰。
- IPC:同一个 IPC 命名空间内的进程可以相互通信,而不同的 IPC 命名空间中的进程则无法通信。
- PID:每个 PID 命名空间内的进程有独立的进程编号,因此每个容器可以有一个 PID 为 1 的 root 进程。
- Network:每个容器拥有独立的网络环境,包括网络设备、IP 地址、路由表、端口号等。容器内的网络与主机或其他容器的网络互相隔离。
- Mount:每个容器拥有独立的文件系统层次结构,容器内的进程只能看到自己的文件系统挂载点。
- User:每个容器内的进程可以拥有独立的用户和组 ID,与主机或其他容器的用户和组 ID 隔离。
1.4 如何隔离两个容器中的进程
在容器化环境中,如果我们要隔离容器 A 和容器 B 中的进程,需要从以下几个方面进行资源隔离:
-
PID 隔离:
- 容器内的进程需要拥有独立的 PID(进程编号)。通过 PID 命名空间,容器 A 和容器 B 内的进程将有独立的 PID 系统,彼此无法相互识别或干扰。
-
IPC 隔离:
- 容器 A 和容器 B 中的进程不能共享信号量、消息队列或共享内存等 IPC 资源。通过 IPC 命名空间,每个容器内的进程只能与同一容器中的进程进行通信,而无法访问其他容器的 IPC 资源。
-
Mount 隔离:
- 容器内的文件系统应该是独立的,这样容器 A 的进程不能访问容器 B 的文件系统。通过 Mount 命名空间,每个容器的文件系统挂载点是独立的,不同容器之间的文件系统层次结构互不干扰。
-
网络隔离:
- 容器 A 和容器 B 的网络应该是独立的,容器 A 无法访问容器 B 的网络接口、端口或网络资源。通过 Network 命名空间,每个容器可以拥有独立的网络设备、IP 地址、路由表等资源。
-
主机名隔离:
- 每个容器应该有自己的主机名和域名,这样容器 A 内的进程无法读取容器 B 的主机名。通过 UTS 命名空间,每个容器可以有独立的主机名和域名。
二、相关命令
2.1 dd 命令
Linux 下 dd
命令用于读取、转换并输出数据。
dd 可从标准输入或文件中读取数据,根据指定的格式来转换数据,再输出到文件、设备或标准输出;
- 语法
dd OPTION
- 参数 / 选项:
参数 | 说明 |
---|---|
if=文件名 | 输入文件名,默认为标准输入(指定源文件)。 |
of=文件名 | 输出文件名,默认为标准输出(指定目标文件)。 |
ibs=bytes | 一次读取 bytes 个字节,即指定输入块的大小。 |
obs=bytes | 一次输出 bytes 个字节,即指定输出块的大小。 |
bs=bytes | 同时设置读取和输出的块大小为 bytes 个字节。 |
cbs=bytes | 一次转换 bytes 个字节,即指定转换缓冲区的大小。 |
skip=blocks | 从输入文件开头跳过 blocks 个块后再开始复制。 |
seek=blocks | 从输出文件开头跳过 blocks 个块后再开始复制。 |
count=blocks | 仅复制 blocks 个块,块大小由 ibs 指定。 |
conv=<关键字> | 文件转换方式,支持以下关键字: |
ascii :将 EBCDIC 转换为 ASCII | |
ebcdic :将 ASCII 转换为 EBCDIC | |
ibm :将 ASCII 转换为 IBM 格式的 EBCDIC | |
block :将每行转换为长度为 cbs 的块,短于 cbs 的部分用空格填充 | |
unblock :使每行的长度为 cbs ,短于 cbs 的部分用空格填充 | |
lcase :将大写字符转换为小写字符 | |
ucase :将小写字符转换为大写字符 | |
swap :交换输入中的每对字节 | |
noerror :出错时不停止继续执行 | |
notrunc :不截断输出文件 | |
sync :将每个输入块填充到 ibs 字节,不足部分用 NUL 字符补齐 | |
--help | 显示帮助信息 |
--version | 显示版本信息 |
-
案例
# 生成一个大小为 80MB 的空白镜像文件 image.img,内容是 0 字节。 dd if=/dev/zero of=image.img bs=8k count=10240 # 将 testfile 文件中的所有英文字母转换为大写,然后转成为 testfile_1 文件 dd if=testfile_2 of=testfile_1 conv=ucase
2.2 mkfs 命令
mkfs
命令 用于 在设备上创建 Linux
文件系统,即格式化,比如我们使用 U 盘的时候可以格式化。
- 语法
mkfs [-V] [-t fstype] [fs-options] filesys [blocks]
- 参数解释
-t fstype:指定要建立何种文件系统;如 ext3, ext4filesys :指定要创建的文件系统对应的设备文件名;
blocks:指定文件系统的磁盘块数。
-V : 详细显示模式
fs-options:传递给具体的文件系统的参数
-
案例
# 将 sda6 分区格式化为 ext4 格式 mkfs -t ext4 /dev/sda6 # 格式化镜像文件为 ext4 mkfs -t ext4 ./image.img
2.3 df 命令
Linux df
(disk free) 命令用于显示目前在 Linux 系统上的文件系统磁盘使用情况。
-
语法
df [OPTION]... [FILE]...
-
参数
-
-a
, --all:包含所有的具有 0 Blocks 的文件系统 -
-h
, --human-readable:使用人类可读的格式(预设值是不加这个选项的…) -
-H
, --si:很像 -h, 但是用 1000 为单位而不是用 1024 -
-t
, --type=TYPE:限制列出文件系统的 TYPE -
-T
, --print-type:显示文件系统的形式 -
案例
# 查看磁盘使用情况 df -h # 查看磁盘的系统类型 df -Th
2.4 mount 命令
mount
命令用于加载文件系统到指定的加载点。也常用于挂载光盘,使我们可以访问光盘中的数据,因为将光盘插入光驱中后,Linux 并不会自动挂载,必须使用 mount
命令来手动完成挂载。
Linux 系统下不同目录可以挂载不同分区和磁盘设备,它的目录和磁盘分区是分离的,可以自由组合(通过挂载)不同的目录数据可以跨越不同的磁盘分区或者不同的磁盘设备。
挂载的实质是为磁盘添加入口(挂载点)。
-
语法
mount [-l] mount [-t vfstype] [-o options] device dir
-
参数
-l
:显示已加载的文件系统列表;-t
: 加载文件系统类型支持常见系统类型的ext3 / ext4 / iso9660 / tmpfs / xfs
等,大部分情况可以不指定, mount 可以自己识别;-o [options]
主要用来描述设备或档案的挂接方式。- loop:用来把一个文件当成硬盘分区挂接上系统
- ro:采用只读方式挂接设备
- rw:采用读写方式挂接设备
- device: 要挂接(mount)的设备。
dir
: 挂载点的目录
-
案例
# 将 /dev/hda1 挂在 /mnt 之下。 mount /dev/hda1 /mnt # 将镜像挂载到/mnt/testext4 下面,需要确保挂载点(目录)存在 mkdir -p /mnt/testext4 mount ./fdimage.img /mnt/testext4
2.5 unshare 命令
unshare
命令用于启动一个新的进程并与当前进程分离不同的命名空间。
- 语法
unshare [options] program [arguments]
- 参数
参数 | 含义 |
---|---|
-i, --ipc | 不共享 IPC 空间 |
-m, --mount | 不共享 Mount 空间 |
-n, --net | 不共享 Net 空间 |
-p, --pid | 不共享 PID 空间 |
-u, --uts | 不共享 UTS 空间 |
-U, --user | 不共享用户 |
-V, --version | 查看版本信息 |
--fork | 执行 unshare 的进程 fork 一个新的子进程,在子进程里执行传入的参数 |
--mount-proc | 执行子进程前,将 proc 文件系统优先挂载过去 |
-
案例
# 启动一个完全隔离的进程,既有独立的网络、进程 ID 和挂载空间 unshare --net --pid --mount /bin/bash
三、实际操作
3.1 PID 隔离
-
首先在主机
shell
上执行ps -ef
命令,可以查看进程列表,启动进程PID 为1的是init进程; -
我们另开一个
shell
,新建一个PID的Namespace,执行下面的命令,进行进程隔离:sudo unshare --fork --pid --mount-proc /bin/bash
-
--fork
:新建了一个 bash 进程,如果不创建新进程,新的命名空间会将unshare
的 PID 作为新的空间的父进程。但这个unshare
进程并不在新的命名空间中,就会报错:Cannot allocate memory
。 -
--pid
:表示进程通过 PID 隔离,而其他命名空间并没有隔离。 -
--mount-proc
:在 Linux 下,每个进程都有一个对应的/proc/PID
目录,该目录包含了大量有关当前进程的信息。对于一个 PID 命名空间而言,/proc
目录只包含当前命名空间及其所有子命名空间中的进程信息。创建一个新的 PID 命名空间后,如果想让子进程中的
top
、ps
等依赖/proc
文件系统的命令正常工作,需要挂载/proc
文件系统。文件系统隔离是由 mount 命名空间管理的,因此 Linux 提供了--mount-proc
选项来解决该问题。如果不带这个选项,我们仍然会看到宿主机的进程信息,而不是当前命名空间中的进程信息。
-
-
执行
ps -ef
查看进程信息,可以观察到进程空间内的内容已经更改,启动进程变成了bash进程
- 执行
exit
命令退出命名空间exit
3.2 Mount 隔离
-
打开第一个 shell 窗口 A,执行命令,
df -h
,查看主机默认命名空间的磁盘挂载情况
-
打开新的 shell 窗口 B,执行 Mount 隔离命令:
unshare --mount --fork /bin/bash mkdir -p /data/tmpmount
-
在窗口B 上 添加磁盘挂载:
dd if=/dev/zero of=image.img bs=8k count=10240 mkfs -t ext4 ./image.img mount ./image.img /data/tmpmount
-
在窗口B挂载的磁盘 添加文件:
echo "Hello world!" > /data/tmpmount/hello.txt
-
使用
df -h
命令查看窗口 B 中的磁盘挂载信息:
-
分别查看磁盘A、B的文件信息:
-
磁盘A:
-
磁盘B:
-
-
再次查看窗口A的磁盘挂在信息,输出结果与第一次一致,根据磁盘挂载信息与文件信息,可以看出磁盘B中的文件在磁盘A中不存在,即成功实现了文件隔离。
-
窗口B通过
exit
命令进行退出exit
-
关于文件的生命周期:
- 如果窗口B在新的命名空间中创建了文件,它们的生命周期将由该命名空间控制。
- 当执行 exit 命令时,窗口B的 Bash 进程退出并终止,新的命名空间也会被销毁。
- 新命名空间中的所有挂载和文件都会被销毁,因为命名空间的生命周期与执行该命令的进程相关。当进程退出时,整个命名空间也会被清理,从而删除其中的所有文件。