namespace及相关命令
1. namspace
1.1 namespace介绍
namespace 是 Linux 的一种用于实现进程隔离的机制,是 Linux 内核容器化技术(如 Docker 和 Kubernetes)的核心组件之一。它允许系统将资源划分为彼此隔离的单元,通过 namespace 可以让一些进程只看到与自己相关的一部分资源,而另外一些进程也只能看到与它们自己相关的资源,使得不同的进程组可以独立操作这些资源,而互不干扰。
namespace 是对全局系统资源的一种封装机制,处于不同 namespace 的进程拥有独立的全局系统资源,且改变一个 namespace 中的系统资源只会影响当前 namespace 里的进程,对其他 namespace 中的进程没有影响。
1.2 namespace属性
namespace 中有许多进程和系统相关的属性,Linux 也提供了一些 API 来操作操作 namespace:
属性 | 系统调用参数 | 被隔离的全局系统资源 | 引入内核版本 |
---|---|---|---|
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 的 namespace 的进程之间可以互相通信,不同的 IPC namespace 之间不能通信。
PID: 每个 PID 的 namespace 中的进程可以有独立的 PID ,每个容器可以有 PID 为 1 的 root 进程。
Network: 每个容器有独立的网络设备、IP 地址、IP 路由表、/proc/net 目录、端口号。
mount: 每个容器可以看到不同的文件系统层次结构。
User: 每个容器可以有不同的 user 和 group id。
2. 常用命令
注意,namespace 的隔离能力不是由 docker 提供的,而是 Linux 操作系统内核提供的基本能力。
2.1 dd命令
Linux 的 dd 命令用于读取、转换并输出数据。 dd 命令可以从标准输入或文件中读取数据,根据指定的格式来转换数据,再输出到文件、设备或标准输出中。
dd [option]
选项:
选项 | 含义 |
---|---|
if=filename | 源文件的文件名,即输入的文件。(默认 stdin) |
of=filename | 目的文件的文件名,即输出的文件。(默认 stdout) |
ibs=bytes | 一次读取 bytes \text{bytes} bytes 个字节,即指定一个块,大小为 bytes \text{bytes} bytes 个字节。 |
obs=bytes | 一次输出 bytes \text{bytes} bytes 个字节,即指定一个块,大小为 bytes \text{bytes} bytes 个字节。 |
bs=bytes | 同时设置读入/输出的块大小为 bytes \text{bytes} bytes 个字节。 |
cbs=bytes | 一次转换 bytes \text{bytes} bytes 个字节,即指定转换缓冲区大小。 |
skip=blocks | 从输入文件开头跳过 blocks \text{blocks} blocks 个块后再开始复制。 |
seek=blocks | 从输出文件开头跳过 blocks \text{blocks} blocks 个块后再开始复制。 |
count=blocks | 仅拷贝 blocks \text{blocks} blocks 个块,块大小等于 ibs 指定的字节数。 |
–help | 显示帮助信息。 |
–version | 显示版本信息。 |
conv=[关键字] | 这个选项用于转换数据内容,详细看下表。 |
conv 关键字:
关键字 | 含义 | 关键字 | 含义 |
---|---|---|---|
conversion | 用指定的参数转换文件。 | lcase | 把大写字符转换为小写字符。 |
ascii | 将 ebcdic 转换为 ascii。 | ucase | 把小写字符转换为大写字符。 |
ebcdic | 将 ascii 转换为 ebcdic。 | swap | 交换输入的每对字节。 |
ibm | 将 ascii 转换为 alternate ebcdic。 | noerror | 出错时不停止。 |
block | 将输入的 \n 转换为固定长度的空格填充记录。 |
notrunc | 不截断输出文件,即写入新信息不清空原内容(但新内容仍会从偏移量开始向后覆盖)。 |
unblock | 将输入的固定长度记录转换为 \n 结尾的记录。 |
sync | 将每个输入块填充到 ibs 个字节,不足的部分用空(NUL)字符填充。 |
block 和 unblock 用于控制文件每行的显示,只是方法不同。假如指定每行内容为 80 80 80 字节(
cbs=80
)。block 表示每行内容被调整为 80 80 80 字节宽,不足的部分用空格填充;unblock 表示删除 block 设置的空格,并在每 80 80 80 字节长度的记录尾部添加换行符(\n
),转换为普通文本。
使用:
创建一个镜像文件。
dd if=/dev/zero of=test.img bs=8k count=10240
/dev/zero 是一个特殊设备文件,提供源源不断的零值字节。
这段命令的意思是,拷贝无限制的 0 到 test.img 文件中,每个块大小 8 K B 8\,\rm KB 8KB,共 10240 10240 10240 个块,文件总大小为 8 K B × 1024 = 80 M B 8\,\rm KB \times{1024}=80\,\rm MB 8KB×1024=80MB 。
2.2 mkfs命令
mkfs 用于在设备上创建 Linux 文件系统,即格式化。
mkfs [option] filesys [blocks]
选项:
选项 | 含义 |
---|---|
-t fstype | 指定要建立何种文件系统,如 ext3,ext4 。 |
filesys | 指定要创建的文件系统对应的设备文件名。 |
blocks | 指定文件系统的磁盘块数。 |
-V | 详细显示模式。 |
fs-option | 传递给具体的文件系统的参数。 |
使用:
将镜像文件 test.img 格式化为 ext4 文件系统。
mkfs -t ext4 ./test.img
2.3 df命令
df 用于显示在 Linux 系统上的文件系统磁盘使用情况。
df [option] [file]
选项:
选项 | 含义 |
---|---|
-a,-all | 显示所有文件系统,包括 0 Blocks 的。 |
-h,–human-readable | 使用人类可读的格式,即把单位都换算成 KB、MB 等。 |
-H,–si | 与 -h 类似,但进制为 1000 而不是 1024。 |
-t,–type=TYPE | 限制列出文件系统的 TYPE。 |
-T,–print-type | 显示文件系统的形式。 |
2.4 mount命令
mount 用于加载文件系统到指定的加载点。挂载的实质就是为磁盘添加入口(挂载点)。Linux 系统下不同目录可以挂载不同分区和磁盘设备,它的目录和磁盘分区是分离的,可以自由组合,不同的目录数据可以跨越不同的磁盘分区或者不同的磁盘设备。
此命令也常用于挂载光盘(U 盘),使我们可以访问光盘中的数据。因为在 Linux 中,将光盘插入光驱后,系统不会自动挂载,必须使用 mount 命令手动完成挂载。
#常见用法
mount -l
mount [-o option] [device] dir
选项:
选项 | 含义 |
---|---|
-l | 显示已经加载的文件系统列表。 |
-t | 加载文件系统类型,支持常见系统类型如 ext3、ext4、ios9660、tmpfs、xfs 等。大部分情况下不需要指定,mount 可以自己识别。 |
-o option | 主要用于描述设备或档案的挂载方式,选项看下表。 |
device | 要挂载(mount)的设备。 |
dir | 挂载点的目录。 |
-o选项:
选项 | 含义 |
---|---|
loop | 用于把一个文件当成磁盘分区挂接上系统。 |
ro | 采用只读方式挂接设备。 |
rw | 采用读写方式挂接设备。 |
使用:
mount ./test.img ./mnt/testext4
如果挂载点不存在(即目录不存在)会报错:
2.5 unshare命令
unshare 主要用于使用与父进程不共享的 namespace 运行程序。
unshare [option] program [arguments]
选项:
选项 | 含义 |
---|---|
-i,–ipc | 不共享 IPC 空间。 |
-m,–mount | 不共享 Mount 空间。 |
-n,–net | 不共享 Net 空间。 |
-p,–pid | 不共享 PID 空间。 |
-u,–uts | 不共享 UTS 空间。 |
-U,–user | 不共享用户。 |
-V,–version | 查看版本。 |
–fork | 执行 unshare 的进程 fork 一个新的子进程,在子进程里执行 unshare 传入的参数。 |
–mount-proc | 执行子进程前,将 proc 优先挂载过去。 |
–mount-proc 的存在主要是因为,每一个进程里都有一个对应的 /proc/PID 目录,该目录包含大量的有关当前进程的信息。如果我们不使用这个选项,fork 一个 bash 进程到新的 namespace 里,就会将原先 bash 进程里所有的子进程信息都复制了过来,相当于新的 namespace 还是能看见全局的进程信息。通过这个选项,我们才能实现进程信息隔离。
3. PID隔离
新建一个 PID namespace 完成进程 PID 的隔离,具体表现为执行 ps -ef
命令看不到全局的进程信息。
使用 unshare 命令启动 bash 将 PID 隔离:
unshare -p /bin/bash
执行后,文件会报如下错误:
原因是,在启动 unshare 进程时,新的 namespace 会使用 unshare 的 PID 作为新的空间的父进程,但这个 unshare 进程并不在新的 namespace 中,所以会报 Cannot allocate memory 这个错误。
解决方法是,创建一个子进程,子进程在这个 namespace 中就可以了:
unshare -pf /bin/bash
执行后,使用 ps -ef
命令可以看到进程信息有很多,而且 1 号进程并不是 /bin/bash
:
原因是 /bin/bash
进程中的 /proc/PID 目录包含的所有进程信息也被子进程拷贝到了新的 namespace 里,使用 ps -ef
命令时,将这些信息全都打印出来了,所以还是没有实现 PID 的隔离。
解决方法是,使用 --mount-proc
选项:
unshare -pf --mount-proc /bin/bash
执行后,使用 ps -ef
命令可以看到进程信息只有两条,且 1 号进程是 /bin/bash
说明 PID 隔离成功:
4. Mount隔离
Mount 隔离的表现是,在外部使用 df -h
查看主机默认命名空间的磁盘挂载情况时,看不到被隔离的文件系统挂载情况,且在外部无法访问到被隔离的文件。
在执行 Mount 隔离之前,执行 df -h
,可以看到在外部是能看到 testext4 的挂载情况的:
使用 unshare 命令执行 Mount 隔离:
unshare -mf /bin/bash
在被隔离的文件系统中添加一个文本文件:
echo "hello world" > ./test.txt
可以看到在文件系统内部是可以正常访问该文本文件的:
在外部执行 df -h
命令,可以看到在外部看不到该文件系统挂载情况:
注意不要直接使用上一个 PID 的例子直接做该测试,否则还是会显示文件挂载情况。
在外部使用 cat
命令也无法查看到该文本文件:
说明 Mount 隔离已经成功完成。