Linux Namespace 是一种 Linux Kernel 提供的资源隔离方案。使得处于不同namespace的进程拥有独立的全局系统资源,改变一个namespace中的系统资源只会影响当前namespace里的进程,对其他namespace中的进程没有影响。
我们可以通过 ls -l /proc/$$/ns 查看服务器有哪些 namespace
[root@linjian ~]# ls -l /proc/$$/ns
total 0
lrwxrwxrwx 1 root root 0 Jan 2 16:25 ipc -> ipc:[4026531839]
lrwxrwxrwx 1 root root 0 Jan 2 16:25 mnt -> mnt:[4026531840]
lrwxrwxrwx 1 root root 0 Jan 2 16:25 net -> net:[4026531956]
lrwxrwxrwx 1 root root 0 Jan 2 16:25 pid -> pid:[4026531836]
lrwxrwxrwx 1 root root 0 Jan 2 16:25 user -> user:[4026531837]
lrwxrwxrwx 1 root root 0 Jan 2 16:25 uts -> uts:[4026531838]
clone: 创建一个新的进程并把他放到新的namespace中
setns:?将当前进程加入到已有的namespace中
unshare: 使当前进程退出指定类型的namespace,并加入到新创建的namespace(相当于创建并加入新的namespace)
nsenter -t <pid> -n ip addr: 进入某 namespace 运行命令
lsns –t <type>: 查看当前系统的 namespace
ls -la /proc/<pid>/ns/: 查看某进程的 namespace
我很重要!!!
clone和unshare区别:
Linux 中有以下 6 种不同的 namespace,每种 namespace 对于容器技术的实现具有不同的作用和意义:
ipc namespace 的一种用例是将两个进程之间的共享内存 (SHM) 分开,以避免误用。相反每个进程将能够对共享内存段使用相同的标识符并生成两个不同的区域。当一个IPC命名空间被销毁时,该命名空间中的所有IPC对象也会自动被销毁
常见操作
ipcmk: 创建队列(-q),信号量(-s),共享内存(-m),权限(-p)
ipcms: 查看队列(-q),查看信号量(-s),查看共享内存(-m),
终端1
# 创建队列
[root@linjian ~]# ipcmk -Q
Message queue id: 0
# 发送消息
[root@linjian ~]# ipcmk -Q
Message queue id: 0
# 查看队列
[root@linjian ~]# ipcs -q
------ Message Queues --------
key msqid owner perms used-bytes messages
0x9c65478f 0 root 644 0 0
# 查询ipc 信息
[root@linjian ~]# readlink /proc/$$/ns/ipc
ipc:[4026531839]
终端2
# 查看队列
[root@linjian ~]# ipcs -q
------ Message Queues --------
key msqid owner perms used-bytes messages
0x9c65478f 0 root 644 0 0
# 查询ipc 信息
[root@linjian ~]# readlink /proc/$$/ns/ipc
ipc:[4026531839]
# 启动新的ipc namespace (-i)
[root@linjian ~]# sudo unshare -i /bin/bash
# 看不到刚才那个队列消息
[root@linjian ~]# ipcs -q
------ Message Queues --------
key msqid owner perms used-bytes messages
mnt 是第一个实现的命名空间。之前大部分人认为不需要多个命名空间,因此他们决定命名空间 取名为 CLONE_NEWNS。通过 mnt 命名空间,Linux 能够通过一组进程隔离一组挂载点,对构建用户或者容器自己的文件系统目录非常有用。
当前进程所在mount ns 里的所有挂载信息可以在/proc/[pid]/mounts、/proc/[pid]/mountinfo和/proc/[pid]/mountstats里面找到。每个mount namespace都拥有一份自己的挂载点列表,当用clone或者unshare函数创建新的mount namespace时,新创建的namespace将拷贝一份老namespace里的挂载点列表,但从这之后,他们就没有关系了,通过mount和umount增加和删除各自namespace里面的挂载点都不会相互影响。
客户端1
[root@linjian testMnt]# readlink /proc/$$/ns/mnt
mnt:[4026531840]
# 创建好对应文件
[root@linjian testMnt]# tree
.
├── mnt01
│?? └── mnt011
├── mnt01.iso
├── mnt02
│?? └── mnt021
└── mnt02.iso
4 directories, 2 files
# 挂载
[root@linjian testMnt]# mount ./mnt01.iso ./mnt01/mnt011
mount: /dev/loop0 is write-protected, mounting read-only
[root@linjian testMnt]# mount |grep mnt01.iso
/home/testMnt/mnt01.iso on /home/testMnt/mnt01/mnt011 type iso9660 (ro,relatime)
# mnt namespace
[root@linjian testMnt]# unshare --mount --uts /bin/bash
[root@linjian testMnt]# exec bash
[root@linjian testMnt]# readlink /proc/$$/ns/mnt
mnt:[4026532715]
[root@linjian testMnt]# mount |grep mnt01.iso
/home/testMnt/mnt01.iso on /home/testMnt/mnt01/mnt011 type iso9660 (ro,relatime)
[root@linjian testMnt]# mount ./mnt02.iso ./mnt02/mnt021
mount: /dev/loop1 is write-protected, mounting read-only
[root@linjian testMnt]# mount |grep mnt
/home/testMnt/mnt01.iso on /home/testMnt/mnt01/mnt011 type iso9660 (ro,relatime)
/home/testMnt/mnt02.iso on /home/testMnt/mnt02/mnt021 type iso9660 (ro,relatime)
[root@linjian testMnt]# umount ./mnt01.iso
[root@linjian testMnt]# mount |grep mnt
/home/testMnt/mnt02.iso on /home/testMnt/mnt02/mnt021 type iso9660 (ro,relatime)
客户端2
[root@linjian ~]# readlink /proc/$$/ns/mnt
mnt:[4026531840]
[root@linjian ~]# mount |grep mnt
/home/testMnt/mnt01.iso on /home/testMnt/mnt01/mnt011 type iso9660 (ro,relatime)
network namespace用来隔离网络设备, IP地址, 端口等. 每个namespace将会有自己独立的网络栈,路由表,防火墙规则,socket等。
每个新的network namespace默认有一个本地环回接口,除了lo接口外,所有的其他网络设备(物理/虚拟网络接口,网桥等)只能属于一个network namespace。每个socket也只能属于一个network namespace。
当新的network namespace被创建时,lo接口默认是关闭的,需要自己手动启动起
标记为"local devices"的设备不能从一个namespace移动到另一个namespace,比如loopback, bridge, ppp等,我们可以通过ethtool -k命令来查看设备的netns-local属性。
[root@linjian ~]# ethtool -k lo|grep netns-local
netns-local: on [fixed]
客户端1
[root@linjian ~]# readlink /proc/$$/ns/net
net:[4026531956]
# 新建network namespace
[root@linjian ~]# unshare --uts --net /bin/bash
[root@linjian ~]# readlink /proc/$$/ns/net
net:[4026532719]
[root@linjian ~]# ifconfig
[root@linjian ~]# ip link set lo up
[root@linjian ~]# ifconfig
lo: flags=73<UP,LOOPBACK,RUNNING> mtu 65536
inet 127.0.0.1 netmask 255.0.0.0
inet6 ::1 prefixlen 128 scopeid 0x10<host>
loop txqueuelen 1000 (Local Loopback)
RX packets 0 bytes 0 (0.0 B)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 0 bytes 0 (0.0 B)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
[root@linjian ~]# ping 127.0.0.1
PING 127.0.0.1 (127.0.0.1) 56(84) bytes of data.
64 bytes from 127.0.0.1: icmp_seq=1 ttl=64 time=0.047 ms
64 bytes from 127.0.0.1: icmp_seq=2 ttl=64 time=0.046 ms
[root@linjian ~]# echo $$
13619
客户端2
[root@linjian ~]# readlink /proc/$$/ns/net
net:[4026531956]
[root@linjian ~]# echo $$
13586
[root@linjian ~]# ip link add veth0 type veth peer name veth1
[root@linjian ~]# ip link set veth1 netns 13619(这个是客户端1的进程id)
[root@linjian ~]# ip address add dev veth0 192.168.1.1/24
[root@linjian ~]# ip link set veth0 up
[root@linjian ~]# ifconfig veth0
veth0: flags=4099<UP,BROADCAST,MULTICAST> mtu 1500
inet 192.168.1.1 netmask 255.255.255.0 broadcast 0.0.0.0
ether d2:58:8f:c4:9a:40 txqueuelen 1000 (Ethernet)
RX packets 0 bytes 0 (0.0 B)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 0 bytes 0 (0.0 B)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
[root@linjian ~]# echo $$
13586
客户端1
[root@linjian ~]# ip address add dev veth1 192.168.1.2/24
[root@linjian ~]# ip link set veth1 up
[root@linjian ~]# ifconfig veth1
veth1: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500
inet 192.168.1.2 netmask 255.255.255.0 broadcast 0.0.0.0
inet6 fe80::3090:b2ff:fe5a:89b8 prefixlen 64 scopeid 0x20<link>
ether 32:90:b2:5a:89:b8 txqueuelen 1000 (Ethernet)
RX packets 7 bytes 586 (586.0 B)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 7 bytes 586 (586.0 B)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
[root@linjian ~]# ping 192.168.1.1
PING 192.168.1.1 (192.168.1.1) 56(84) bytes of data.
64 bytes from 192.168.1.1: icmp_seq=1 ttl=64 time=0.062 ms
64 bytes from 192.168.1.1: icmp_seq=2 ttl=64 time=0.063 ms
64 bytes from 192.168.1.1: icmp_seq=3 ttl=64 time=0.058 ms
^C
--- 192.168.1.1 ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 2000ms
rtt min/avg/max/mdev = 0.058/0.061/0.063/0.002 ms
PID namespaces用来隔离进程的ID空间,使得不同pid namespace里的进程ID可以重复且相互之间不影响。PID namespace可以嵌套,父namespace里面可以看到所有子孙后代namespace里的进程信息,而子namespace里看不到祖先或者兄弟namespace里的进程信息。
在 PID 命名空间中创建的第一个进程的 id 为 1,并获得外面 init 进程相同的特殊待遇。例如,命名空间内的所有进程将重新设置为命名空间的 PID 1,而不是主机 PID 1。此外,终止此进程将立即终止其 PID 命名空间中的所有进程及其任何后代。
# namespace的ID
[root@linjian ~]# readlink /proc/self/ns/pid
pid:[4026531836]
# 创建新的pod namespace
[root@linjian ~]# unshare --uts --pid --mount --fork /bin/bash
# 切换到一个新的 bash shell 环境
[root@linjian ~]# exec bash
# 找到你执行unshare进程
[root@linjian ~]# pstree -pl
├─sshd(1173)─┬─sshd(2526)───bash(2534)
│ └─sshd(2914)───bash(2916)───unshare(2952)───bash(2953)───pstree(2982)
# 其实你可以看到当前已经是 进程 id 1了
[root@linjian ~]# echo $$
1
# 和刚刚开始ns id一样
[root@linjian ~]# readlink /proc/2952/ns/pid
pid:[4026531836]
# 新的ns id
[root@linjian ~]# readlink /proc/2953/ns/pid
pid:[4026532718]
# 这里一样是需要把信息文件挂载过去
[root@linjian ~]# readlink /proc/$$/ns/pid
pid:[4026531836]
[root@linjian ~]# mount -t proc proc /proc
[root@linjian ~]# readlink /proc/$$/ns/pid
pid:[4026532718]
[root@linjian ~]#
[root@linjian ~]# ps -ef
UID PID PPID C STIME TTY TIME CMD
root 1 0 0 13:35 pts/1 00:00:00 bash
root 32 1 0 13:39 pts/1 00:00:00 ps -ef
[root@linjian ~]# exit
exit
[root@linjian ~]# readlink /proc/self/ns/pid
pid:[4026531836]
用户命名空间使得进程的用户和组 ID 在命名空间内部和外部可以不同。进程可以在用户命名空间之外拥有非root 用户 ID,同时在内部拥有root 权限。换句话说,该进程没有权限进行用户命名空间之外的操作,但在命名空间内具有 root 权限
[root@linjian ~]# readlink /proc/$$/ns/user
user:[4026531837]
[root@linjian ~]# id
uid=0(root) gid=0(root) groups=0(root)
[root@linjian ~]# unshare --user /bin/bash
/usr/bin/id: cannot find name for group ID 65534
/usr/bin/id: cannot find name for user ID 65534
[I have no name!@linjian ~]$ id
uid=65534 gid=65534 groups=65534
[I have no name!@linjian ~]$ readlink /proc/$$/ns/user
user:[4026532718]
这里我执行unshare --user /bin/bash 报错了,/proc/sys/user/max_user_namespaces是0(可修改user ns 数量),将max_user_namespaces 修改非0 就行了
UTS Namespace 用于隔离主机名和域名等系统识别信息。为不同的容器分配不同的容器名,从而避免名称冲突,提高了容器之间的独立性。
以hostname 为例
客户端1:
[root@linjian ~]# hostname
linjian
客户端2:l
[root@linjian ~]# hostname
linjian
[root@linjian ~]# unshare -u
[root@linjian ~]# hostname testUts
[root@linjian ~]# hostname
testUts