文章目录
一、什么是Namespace1.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 常见命名空间
下面是一些常见的命名空间及其对应的系统调用参数和功能:
命名空间系统调用参数被隔离的全局系统资源引入内核版本UTSCLONE_NEWUTS主机名和域名2.6.19IPCCLONE_NEWIPC信号量、消息队列和共享内存(进程间通信)2.6.19PIDCLONE_NEWPID进程编号2.6.24NetworkCLONE_NEWNET网络设备、网络栈、端口等2.6.29MountCLONE_NEWNS文件系统挂载点2.4.19UserCLONE_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 转换为 ASCIIebcdic:将 ASCII 转换为 EBCDICibm:将 ASCII 转换为 IBM 格式的 EBCDICblock:将每行转换为长度为 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 进程退出并终止,新的命名空间也会被销毁。新命名空间中的所有挂载和文件都会被销毁,因为命名空间的生命周期与执行该命令的进程相关。当进程退出时,整个命名空间也会被清理,从而删除其中的所有文件。