本文将介绍 Linux Namespace
。
一提到 容器 技术,随之想到的便是 namespace
是容器化的基石,本篇文章将对 Namespace
进行简单的了解。
本文所依赖的环境为:
root@debian:~# hostnamectl
Static hostname: windows11pro
Icon name: windowsBooks
Chassis: laptop 💻
Operating System: Debian GNU/Linux 12 (bookworm)
Kernel: Linux 6.1.0-26-amd64
Architecture: x86-64
Hardware Vendor: ASUSTeK COMPUTER INC.
Hardware Model: X441UVK
Firmware Version: X441UVK.314
root@debian:~#
Namepace
也称之为命名空间,最早出现在内核 2.4.19
上,后期逐步迭代的,它的作用是将 Linux
的资源隔离开来,可以这样理解, Nmaespace
就是为了虚拟化而存在的。
Namespace
类型众多,这里列举一个表格说明。
内核版本 | Namespace | 系统调用参数 | 含义 |
---|---|---|---|
2.4.19 | Mount Namespace | CLONE_NEWNS | 文件系统的隔离 |
2.6.19 | UTS Namespace | CLONE_NEWUTS | nodename和damainname的隔离 |
2.6.19 | IPC Namespace | CLONE_NEWIPC | 进程间通信资源隔离 |
2.6.24 | PID Namespace | CLONE_NEWPID | 进程ID的隔离 |
2.6.29 | Network Namespace | CLONE_NEWNET | 网络的隔离 |
3.8 | User Namespace | CLIONE_NEWUSER | 用户和组的隔离 |
5.6 | Time Namespace | CLONE_NEWTIME | 系统时钟的隔离 |
前置准备
陆游曾说,”纸上得来终觉浅,绝知此事要躬行。“,实践才是检验真理的唯一标准,所以,在正式了解 Namespace
之前,得先了解下关于 Namespace
相关的工具: unshare
,如果要解释该工具,用 unshare - run program in new namespaces
表达再合适不过了,它可以在新的命名空间中运行要指定的程序。
unshare工具的使用
创建 Namespace
主要的参数有以下几种:
-m
、--mount
:用以创建Mount namespace
。-u
、--uts
:用以创建Uts Namespace
。-i
、--ipc
:用以创建IPC Namespace
。-p
、--pid
:用以创建Pid Namespace
。-n
、--net
:用以创建Network Namespace
。-U
、--user
:用以创建User Namespace
。-T
、--time
:用以创建Time Namespace
。
举一个最简单的例子,在 Network Namespace
启动一个 bash
进程,可以使用如下命令:
在此之前,我们需要先获取一下当前机器的 IP
信息,已做参考:
root@debian:~# ip link
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN mode DEFAULT group default qlen 1000
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
2: enp0s3: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP mode DEFAULT group default qlen 1000
link/ether 08:00:27:7c:92:af brd ff:ff:ff:ff:ff:ff
root@debian:~#
在 Network Namespace
中执行命令
root@debian:~# unshare -n /usr/bin/bash
root@debian-linux:~#
root@debian-linux:~# ip link
1: lo: <LOOPBACK> mtu 65536 qdisc noop state DOWN mode DEFAULT group default qlen 1000
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
root@debian-linux:~#
使用 unshare -n /usr/bin/bash
会创建一个 Network Namespace
,并且在该 Namespace
中执行 bash
操作,使用 exit
即可退出该 Namespace
。
这是 unshare
的最简单的用法。
修改提示符
Linux
的提示符格式都被记录到了 PS1
的内置变量中,可以使用 echo
查看其内容。
root@debian:~# echo $PS1
${debian_chroot:+($debian_chroot)}\u@\h:\w\$
root@debian:~#
为了方便使用 unshare
启动新进程更加直观,所以需要修改一下命令提示符,首先在 /root/.bashrc
文件末尾添加如下代码:
# 打开调试
set -x
# 定义默认的PS1值
export PS1="${debian_chroot:+($debian_chroot)}[\u@\h]:[\w][tty:\l]\\$ "
# 设置别名
alias unshare="UNSHARE=1 unshare "
# 如果变量中的UNSHARE值为1,则修改PS1值
if [ ! -e ${UNSHARE} ] && [ ${UNSHARE} -eq 1 ];then
export PS1="(unshare) ${debian_chroot:+($debian_chroot)}[\u@\h]:[\w][tty:\l]\\$ "
fi
# 关闭调试
set +x
增加上述代码后,重新加载一下 /root/.bashrc
就可以使用 unshare
启动 bash
进程了。
首先先重新加载一下 /root/.bashrc
文件
root@debian:~# source /root/.bashrc
++ export 'PS1=[\u@\h]:[\w][tty:\l]\$ '
++ PS1='[\u@\h]:[\w][tty:\l]\$ '
++ alias 'unshare=UNSHARE=1 unshare '
++ '[' '!' -e ']'
++ set +x
[root@debian]:[~][tty:1]#
使用 unshare
启动 bash
进程
[root@debian]:[~][tty:1]# unshare -n /usr/bin/bash
+ export 'PS1=[\u@\h]:[\w][tty:\l]\$ '
+ PS1='[\u@\h]:[\w][tty:\l]\$ '
+ alias 'unshare=UNSHARE=1 unshare '
+ '[' '!' -e 1 ']'
+ '[' 1 -eq 1 ']'
+ export 'PS1=(unshare) [\u@\h]:[\w][tty:\l]\$ '
+ PS1='(unshare) [\u@\h]:[\w][tty:\l]\$ '
+ set +x
(unshare) [root@debian]:[~][tty:1]#
通过上面设置,就可以更加直观的看出当前 shell
是否在 Namespace
中,后续为了信息更加简洁,所以就暂时将调试给关闭了。
命名空间文件描述符
在 Linux
中,查看2个进程是否属于同一个命名空间,可以查看 /proc/进程ID/ns/*
下面的文件描述符,如果文件描述符一致,则证明是处于同一命名空间的。
例如,如下2个进程的 namespace
文件描述符如下:
[root@debian]:[~][tty:0]# readlink /proc/983/ns/*
cgroup:[4026531835]
ipc:[4026531839]
mnt:[4026531841]
net:[4026531840]
pid:[4026531836]
pid:[4026531836]
time:[4026531834]
time:[4026531834]
user:[4026531837]
uts:[4026531838]
[root@debian]:[~][tty:0]# readlink /proc/998/ns/*
cgroup:[4026531835]
ipc:[4026531839]
mnt:[4026531841]
net:[4026531840]
pid:[4026531836]
pid:[4026531836]
time:[4026531834]
time:[4026531834]
user:[4026531837]
uts:[4026531838]
[root@debian]:[~][tty:0]#
可以看到,其每项 namespace
的文件描述符都是一致的,所以证明该2个进程属于同一命名空间。
如果是使用 unshare
创建的命名空间,其 namespace
文件描述符应该不一致。
# unshare -m /usr/bin/bash
(unshare) [root@debian]:[~][tty:0]# echo $$
1000
(unshare) [root@debian]:[~][tty:0]#
(unshare) [root@debian]:[~][tty:0]# readlink /proc/1000/ns/*
cgroup:[4026531835]
ipc:[4026531839]
mnt:[4026532316]
net:[4026531840]
pid:[4026531836]
pid:[4026531836]
time:[4026531834]
time:[4026531834]
user:[4026531837]
uts:[4026531838]
(unshare) [root@debian]:[~][tty:0]#
在该 mount namespace
中,其 mount namespace
文件描述符为 4026532316。
(unshare) [root@debian]:[~][tty:0]# exit
exit
[root@debian]:[~][tty:0]# echo $$
983
[root@debian]:[~][tty:0]# readlink /proc/983/ns/*
cgroup:[4026531835]
ipc:[4026531839]
mnt:[4026531841]
net:[4026531840]
pid:[4026531836]
pid:[4026531836]
time:[4026531834]
time:[4026531834]
user:[4026531837]
uts:[4026531838]
[root@debian]:[~][tty:0]#
在该宿主机中,其 mount namespace
文件描述符为 4026531841。在宿主机的 namespace
,也称之为默认 namespace
。
可以看到,2个文件描述符不一致,说明2个进程是在不同的 mount namespace
中。
各个Nmaespace介绍
UTS Namespace
UTS Namespace
是用来隔离主机名和域名的,可以使不同的 Namespace
拥有不同的主机名。
使用 unshare
进入 bash
后尝试修改主机名,不会影响到宿主机的,例如如下:
[root@debian]:[~][tty:1]# unshare -u /usr/bin/bash
(unshare) [root@debian]:[~][tty:1]#
(unshare) [root@debian]:[~][tty:1]# echo $$
13858
(unshare) [root@debian]:[~][tty:1]#
(unshare) [root@debian]:[~][tty:1]# hostname new-debian-uts
(unshare) [root@debian]:[~][tty:1]#
(unshare) [root@debian]:[~][tty:1]# hostname
new-debian-uts
(unshare) [root@debian]:[~][tty:1]#
上述命令使用 unshare
创建了一个 uts namespace
,并且将 bash
放入其中执行,首先打印了其进程的 PID
为 13858
、而后进行了主机名设置。
同时在不关闭该 shell
的前提下,打开另一个终端,获取主机名信息。
[root@debian]:[~][tty:5]# hostname
debian
[root@debian]:[~][tty:5]#
[root@debian]:[~][tty:5]# echo $$
13863
[root@debian]:[~][tty:5]#
[root@debian]:[~][tty:5]#
可以看到,在 uts namespace
命名空间中修改主机名,并不影响宿主机环境,同时,还可以查询一下2个进程的 ns
值:
# readlink /proc/{13858,13863}/ns/uts
uts:[4026532256]
uts:[4026531838]
[root@debian]:[~][tty:5]#
可以看到,上述2个进程的 uts namespace
文件描述符并不一致,所以是2个不同的命名空间,所以,修改主机名才不会互相影响。
IPC Namespace
ipc namespace
是隔离进程间的通信资源,但不是所有的进程间通信都会被隔离,它主要隔离 消息队列、信号量、共享内存等。
关于 IPC
通信,是否可以被 ipc namespace
隔离,可以查看如下表格:
IPC机制 | 需求 | IPC Namespace隔离 |
---|---|---|
管道、命名管道 | 进程间简单数据交换 | 不会隔离 |
消息队列 | 进程间异步信息交换 | 隔离 |
信号量 | 进程间同步和互斥 | 隔离 |
共享内存 | 进程间大块数据共享 | 隔离 |
套接字 | 进程间网络通信 | 不会隔离 |
这里举个简单的例子。
在宿主机上查询共享内存段信息。
[root@debian]:[~][tty:0]# ipcs -m
------ Shared Memory Segments --------
key shmid owner perms bytes nattch status
0x00000000 3 lightdm 600 524288 2 dest
0x00000000 4 lightdm 600 33554432 2 dest
[root@debian]:[~][tty:0]#
在宿主机创建一个新的共享内存段。
[root@debian]:[~][tty:0]# ipcmk -M 128
Shared memory id: 6
[root@debian]:[~][tty:0]#
[root@debian]:[~][tty:0]# ipcs -m
------ Shared Memory Segments --------
key shmid owner perms bytes nattch status
0x00000000 3 lightdm 600 524288 2 dest
0x00000000 4 lightdm 600 33554432 2 dest
0x8b111882 6 root 644 128 0
[root@debian]:[~][tty:0]#
而后在 ipc namespace
中来查询共享内存段。
# unshare -i -u /usr/bin/bash
(unshare) [root@debian]:[~][tty:0]#
(unshare) [root@debian]:[~][tty:0]# ipcs -m
------ Shared Memory Segments --------
key shmid owner perms bytes nattch status
(unshare) [root@debian]:[~][tty:0]#
可以看到,在 ipc namespace
中共享内存段都没了,同样的,在该 namespace
中创建共享内存段,其宿主机也不可以间。
(unshare) [root@debian]:[~][tty:0]# ipcmk -M 128
Shared memory id: 0
(unshare) [root@debian]:[~][tty:0]#
(unshare) [root@debian]:[~][tty:0]# ipcs -m
------ Shared Memory Segments --------
key shmid owner perms bytes nattch status
0x4ec24363 0 root 644 128 0
(unshare) [root@debian]:[~][tty:0]#
在上述 namespace
不退出的情况下,在宿主机中查询共享内存段。
[root@debian]:[~][tty:1]# ipcs -m
------ Shared Memory Segments --------
key shmid owner perms bytes nattch status
0x00000000 3 lightdm 600 524288 2 dest
0x00000000 4 lightdm 600 33554432 2 dest
0x8b111882 6 root 644 128 0
[root@debian]:[~][tty:1]#
如上例子说明,在 ipc namespace
中, IPC
资源已被隔离。
Pid Namespace
Pid Namespace
是用来隔离进程 ID
的,在 Pid Namespace
中的进程都有独立的 PID
。
使用 unshare
创建 pid namespace
并且获取当前 PID
[root@debian]:[~][tty:2]# unshare -u -i -p --fork /usr/bin/bash
(unshare) [root@debian]:[~][tty:2]#
(unshare) [root@debian]:[~][tty:2]# echo $$
1
(unshare) [root@debian]:[~][tty:2]#
(unshare) [root@debian]:[~][tty:2]#
创建 pid namespace
的时候,一定要加 --fork
来 fork
一个新的进程,因为只有新的子进程才会分配 pid
,在使用 pid namespace
后,新的命名空间中的 pid
都会从1开始计数。
实际上查询进程树信息,可以发现 linux
是做了一个转换,将宿主机的 pid
会映射进 pid namespace
中的进程 ID
。
[root@debian]:[~][tty:1]# ps axjf | head -n 1
PPID PID PGID SID TTY TPGID STAT UID TIME COMMAND
1 756 756 756 ? -1 SLsl 0 0:00 /usr/sbin/lightdm
756 777 777 777 tty7 777 Ssl+ 0 0:02 \_ /usr/lib/xorg/Xorg :0 -seat seat0 -auth /var/run/lightdm/root/:0 -nolisten tcp vt7 -novtswitch
756 1104 756 756 ? -1 Sl 0 0:00 \_ lightdm --session-child 15 26
1104 1133 1133 1133 ? -1 Ssl 0 0:00 \_ /usr/bin/lxsession -s LXDE -e LXDE
1133 1228 1228 1228 ? -1 Ss 0 0:00 \_ /usr/bin/ssh-agent x-session-manager
1133 1259 1133 1133 ? -1 S 0 0:00 \_ openbox --config-file /root/.config/openbox/lxde-rc.xml
1133 1264 1133 1133 ? -1 Sl 0 0:00 \_ lxpolkit
1133 1266 1133 1133 ? -1 Sl 0 0:00 \_ lxpanel --profile LXDE
1133 1269 1133 1133 ? -1 Sl 0 0:00 \_ pcmanfm --desktop --profile LXDE
1269 1431 1133 1133 ? -1 Sl 0 0:00 | \_ lxterminal
1431 1434 1434 1434 pts/2 1469 Ss 0 0:00 | \_ bash
1434 1468 1468 1434 pts/2 1469 S 0 0:00 | \_ unshare -u -i -p --fork /usr/bin/bash
1468 1469 1469 1434 pts/2 1469 S+ 0 0:00 | \_ /usr/bin/bash
其实该 bash
的进程 id
为 1469
,但是使用了 pid namespace
,所以在该 namesapce
中会被映射为了 1
,此时若在宿主机上 kill -9 1469
,则会影响到命名空间的进程。
[root@debian]:[~][tty:1]# kill -9 1469
[root@debian]:[~][tty:1]#
同样在命名空间中的进程会被 kill
掉。
(unshare) [root@debian]:[~][tty:2]# unshare: sigprocmask unblock failed: Invalid argument
[root@debian]:[~][tty:2]#
Mount Namespace
Mount Namespace
是最早出现的 Namespace
,可以看到其系统调用参数为 CLONE_NEWNS
,和后续出现的 Namespace
系统调用参数都不同,这是因为最开始的时候,大佬们认为,不会在出现其他 Namespace
了,所以就以此为命名,后面的事情,大家就都知道了,为了兼容,所以该 Mount Namespace
还是以 CLONE_NEWNS
来命名的。
Mount Namespace
的作用是实现文件系统的隔离和管理。
使用 unshare
创建 mount namespace
并且重新挂载 proc
分区。
[root@debian]:[~][tty:2]# unshare -u -i -p -m --fork /usr/bin/bash
(unshare) [root@debian]:[~][tty:2]#
(unshare) [root@debian]:[~][tty:2]# echo $$
1
(unshare) [root@debian]:[~][tty:2]#
(unshare) [root@debian]:[~][tty:2]# mount -t proc proc /proc
(unshare) [root@debian]:[~][tty:2]#
(unshare) [root@debian]:[~][tty:2]# ps aux
USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
root 1 0.0 0.1 7196 3996 pts/2 S 03:40 0:00 /usr/bin/bash
root 3 0.0 0.2 11084 4456 pts/2 R+ 03:40 0:00 ps aux
(unshare) [root@debian]:[~][tty:2]#
上述使用 unshare
创建了 mount namespace
,并且将 bash
放入其执行,在该 mount namespace
中,使用 mount
重新挂载了 proc
,接着查询所有进程,由于 pid namesapce
隔离,所以只能查询到当前命名空间中的进程了。
默认的 mount namespace
会继承宿主机的命名空间,若不进行重新挂载 proc
,查看到的信息就是宿主机的信息。
同样的,在该 namesapce
中设置的挂载,在宿主机是隔离开来的,例如:
(unshare) [root@debian]:[~][tty:2]# df -h
Filesystem Size Used Avail Use% Mounted on
/dev/sda1 19G 3.9G 14G 23% /
udev 953M 0 953M 0% /dev
tmpfs 984M 4.0K 984M 1% /dev/shm
tmpfs 197M 1004K 196M 1% /run
tmpfs 5.0M 8.0K 5.0M 1% /run/lock
tmpfs 197M 44K 197M 1% /run/user/1000
tmpfs 197M 44K 197M 1% /run/user/0
/dev/sda5 6.4G 348M 5.8G 6% /var
/dev/sda8 71G 1.2G 67G 2% /home
/dev/sda7 1.2G 68K 1.1G 1% /tmp
(unshare) [root@debian]:[~][tty:2]# mount /dev/sda1 /data
(unshare) [root@debian]:[~][tty:2]# mount | grep /data
/dev/sda1 on /data type ext4 (rw,relatime,errors=remount-ro)
(unshare) [root@debian]:[~][tty:2]#
在不退出当前 mount namesapce
环境下,查询宿主机关于 /data
的挂载。
[root@debian]:[~][tty:1]# mount /dev/sda1 /data
[root@debian]:[~][tty:1]#
如此, mount namespace
隔离了文件系统挂载。
Network Namespace
network namespace
隔离的是网络环境,网络通信是较为复杂的操作,这里先仅仅证明 network namesapce
隔离性。
使用 unshare
创建 network namespace
,并且使 bash
在其运行。
(unshare) [root@debian]:[~][tty:2]# unshare -u -i -p -m -n --fork /usr/bin/bash
(unshare) [root@debian]:[~][tty:2]#
(unshare) [root@debian]:[~][tty:2]# ip a
1: lo: <LOOPBACK> mtu 65536 qdisc noop state DOWN group default qlen 1000
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
(unshare) [root@debian]:[~][tty:2]#
创建了 network namespace
之后,查询其 ip
信息,只有本地回环地址,状态还是关闭的,说明 network namespace
隔离了其网络环境。
User Namespace
user namespace
隔离的是用户和用户组,换句话说,不同的 user namespace
中的 user id
和 group id
可以是不同的。最为常用的是在宿主机以一个非 root
用户运行创建了一个 user namespace
,然后在该 user namespace
中却被映射成了 root
用户。
使用 unshare
创建 user namespace
,并且使 bash
在其运行。
[root@debian]:[~][tty:2]# unshare -u -i -p -m -n -U --fork /usr/bin/bash
(unshare) [nobody@debian]:[~][tty:2]$
(unshare) [nobody@debian]:[~][tty:2]$ id
uid=65534(nobody) gid=65534(nogroup) groups=65534(nogroup)
(unshare) [nobody@debian]:[~][tty:2]$
可以看到,增加了 user namespace
后,其用户被映射为了 nobody
用户。
如果想要修改其容器中的 userid
值,可以使用 newuidmap
进行 uid
映射。
首先,需要找到其 user namespace
下运行进程的 pid
。
[root@debian]:[~][tty:0]# ps axjf
PPID PID PGID SID TTY TPGID STAT UID TIME COMMAND
1 756 756 756 ? -1 SLsl 0 0:00 /usr/sbin/lightdm
756 2547 2547 2547 tty7 2547 Ssl+ 0 0:01 \_ /usr/lib/xorg/Xorg :0 -seat seat0 -auth /var/run/lightdm/root/:0 -nolisten tcp vt7 -novtswitch
756 2633 756 756 ? -1 Sl 0 0:00 \_ lightdm --session-child 15 26
2633 2637 2637 2637 ? -1 Ssl 0 0:00 \_ /usr/bin/lxsession -s LXDE -e LXDE
2637 2729 2729 2729 ? -1 Ss 0 0:00 \_ /usr/bin/ssh-agent x-session-manager
2637 2749 2637 2637 ? -1 S 0 0:00 \_ openbox --config-file /root/.config/openbox/lxde-rc.xml
2637 2750 2637 2637 ? -1 Sl 0 0:00 \_ lxpolkit
2637 2752 2637 2637 ? -1 Sl 0 0:00 \_ lxpanel --profile LXDE
2637 2754 2637 2637 ? -1 Sl 0 0:00 \_ pcmanfm --desktop --profile LXDE
2754 2886 2637 2637 ? -1 Sl 0 0:00 | \_ lxterminal
2886 2889 2889 2889 pts/2 2916 Ss 0 0:00 | \_ bash
2889 2915 2915 2889 pts/2 2916 S 0 0:00 | \_ unshare -u -i -p -m -n -U --fork /usr/bin/bash
2915 2916 2916 2889 pts/2 2916 S+ 0 0:00 | \_ /usr/bin/bash
可以看到,其真实的 pid
是 2916
,则在宿主机上需要执行如下映射。
[root@debian]:[~][tty:1]# newuidmap 2916 0 0 1
[root@debian]:[~][tty:1]#
此时,再回到 user namespace
中,查看用户信息。
(unshare) [nobody@debian]:[~][tty:2]$ id
uid=0(root) gid=65534(nogroup) groups=65534(nogroup)
(unshare) [nobody@debian]:[~][tty:2]$
(unshare) [nobody@debian]:[~][tty:2]$ mount -t proc proc /proc
(unshare) [nobody@debian]:[~][tty:2]$ ps aux
USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
root 1 0.0 0.1 7196 3876 pts/2 S 04:42 0:00 /usr/bin/bash
root 8 0.0 0.2 11084 4328 pts/2 R+ 04:49 0:00 ps aux
(unshare) [nobody@debian]:[~][tty:2]$
其 uid
则变为了 root
,也可以进行磁盘挂载。
当然,在普通用户下,也是同理。
使用普通用户使用 unshare
创建 user namespace
并且启动 bash
命令。
[wangli@debian]:[~][tty:2]$ unshare -u -i -p -m -n -U --fork /usr/bin/bash
(unshare) [nobody@debian]:[~][tty:2]$
(unshare) [nobody@debian]:[~][tty:2]$ id
uid=65534(nobody) gid=65534(nogroup) groups=65534(nogroup)
(unshare) [nobody@debian]:[~][tty:2]$
(unshare) [nobody@debian]:[~][tty:2]$
查询其真实的 pid
。
[root@debian]:[~][tty:0]# ps axjf
PPID PID PGID SID TTY TPGID STAT UID TIME COMMAND
1 756 756 756 ? -1 SLsl 0 0:00 /usr/sbin/lightdm
756 2992 2992 2992 tty7 2992 Ssl+ 0 0:01 \_ /usr/lib/xorg/Xorg :0 -seat seat0 -auth /var/run/lightdm/root/:0 -nolisten tcp vt7 -novtswitch
756 3077 756 756 ? -1 Sl 0 0:00 \_ lightdm --session-child 14 26
3077 3083 3083 3083 ? -1 Ssl 1000 0:00 \_ /usr/bin/lxsession -s LXDE -e LXDE
3083 3169 3169 3169 ? -1 Ss 1000 0:00 \_ /usr/bin/ssh-agent x-session-manager
3083 3185 3083 3083 ? -1 S 1000 0:00 \_ openbox --config-file /home/wangli/.config/openbox/lxde-rc.xml
3083 3186 3083 3083 ? -1 Sl 1000 0:00 \_ lxpolkit
3083 3189 3083 3083 ? -1 Sl 1000 0:00 \_ lxpanel --profile LXDE
3083 3192 3083 3083 ? -1 Sl 1000 0:00 \_ pcmanfm --desktop --profile LXDE
3192 3387 3083 3083 ? -1 Sl 1000 0:00 | \_ lxterminal
3387 3390 3390 3390 pts/2 3418 Ss 1000 0:00 | \_ bash
3390 3417 3417 3390 pts/2 3418 S 1000 0:00 | \_ unshare -u -i -p -m -n -U --fork /usr/bin/bash
3417 3418 3418 3390 pts/2 3418 S+ 1000 0:00 | \_ /usr/bin/bash
查询到其 PID
为3418,在另外的终端,执行 newuidmap
操作
[wangli@debian]:[/root][tty:1]$ id
uid=1000(wangli) gid=1000(wangli) groups=1000(wangli),24(cdrom),25(floppy),29(audio),30(dip),44(video),46(plugdev),100(users),106(netdev),111(lpadmin),114(scanner)
[wangli@debian]:[/root][tty:1]$
[wangli@debian]:[/root][tty:1]$ newuidmap 3418 0 1000 1
[wangli@debian]:[/root][tty:1]$
请注意,这里 loweruid
设置为了1000,这是因为 wangli
这个用户的 uid
为1000。
此时再到 user namespace
中查看 id
信息。
(unshare) [nobody@debian]:[~][tty:2]$ id
uid=0(root) gid=65534(nogroup) groups=65534(nogroup)
(unshare) [nobody@debian]:[~][tty:2]$
(unshare) [nobody@debian]:[~][tty:2]$ mount -t proc proc /proc
(unshare) [nobody@debian]:[~][tty:2]$
(unshare) [nobody@debian]:[~][tty:2]$ ps aux
USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
root 1 0.0 0.2 8008 4720 pts/2 S 04:55 0:00 /usr/bin/bash
root 6 0.0 0.2 11084 4440 pts/2 R+ 05:02 0:00 ps aux
(unshare) [nobody@debian]:[~][tty:2]$
可以看到,其 uid
已经被映射为了 root
,此时,执行 mount
进行挂载也可以实现。
Time Namespace
Time Nmaespace
允许进程在独立的时间上下文中运行。使用了 time namespace
的进程可以拥有自己独立的时间视图并且和其他 time namespace
和宿主机隔离开来。
下面举一个最简单的例子:
先查询系统启动的秒数
[wangli@debian]:[~][tty:2]$ cat /proc/uptime
11397.34 10829.19
[wangli@debian]:[~][tty:2]$
使用 unshare
启动 time namespace
命名空间,且运行 bash
进程。
[wangli@debian]:[~][tty:2]$ unshare -u -i -p -m -n -U -T --boottime -11397 --fork /usr/bin/bash
(unshare) [nobody@debian]:[~][tty:2]$ uptime
05:36:09 up 0 min, 3 users, load average: 0.00, 0.01, 0.00
(unshare) [nobody@debian]:[~][tty:2]$
(unshare) [nobody@debian]:[~][tty:2]$
在命名空间中,可以看到,其 uptime
的启动时间已经为0了。
在不退出 time namespace
的情况下,开一个新的 shell
来查看 uptime
信息
[wangli@debian]:[~][tty:3]$ uptime
05:39:01 up 3:12, 3 users, load average: 0.00, 0.01, 0.00
[wangli@debian]:[~][tty:3]$
由此证明, time namespace
对于系统时间的隔离性。
总结
首先,在介绍 Linux Nmaespace
之前先进行了前置准备,包括 unshare
工具的使用、修改提示符、命名空间文件描述符的基本说明等。
而后就介绍了 Linux Namespace
基本信息,归纳如下:
Linux Nmaespace
是 Linux
内核一项强大的功能,是容器化的基石,至目前为止,一共有7种类别的 Namespace
,按照发布时间顺序,分别是:
Mount Namespace
:隔离文件系统。UTS Namespace
: 隔离主机名和域名。IPC Namespace
:隔离进程间通信。PID Namespace
:隔离进程ID。Network Namespace
:隔离网络。User Namespace
:隔离用户和组。Time Namespace
:隔离系统时钟。
如上众多的 Namespace
使得在同一台机器上运行多个“独立”的进程环境,称为可能,所以 Linux Namespace
才是容器化的基础。