namespace

 

 

新瓶装旧酒

docker 的实现,主要依赖 linux 的 namespace、cgroup 和 unionFS 三种技术实现,达到容器的环境隔离、资源控制和镜像打包。

namespace: 环境隔离

cgroup :资源控制

unionFS :镜像打包 。 小,overfloy。overlayfs(UnionFS)   aufs(UnionFS)

 

======================================================================================

简介

Linux Namespace是Linux提供的一种内核级别的环境隔离方法。在Unix时代,有一个叫做chroot的系统调用,它提供了一种简单的隔离模式:chroot内部的文件系统无法访问外部的内容。目前Linux 内核提供了以下几种Namespace:

Namespace在此基础上,提供了对UTS、IPC、mount、PID、network、User等的隔离机制。

名称宏定义隔离资源内核版本
mnt CLONE_NEWNS Mount point 2.4.19
ipc CLONE_NEWIPC System V IPC, POSIX message queue 2.6.19
net CLONE_NEWNET network device interface, IPv4 and IPv6 protocol stack, IP routing table, firewall rule, the /proc/net and /sys/class/net directory tree, socket, etc 2.6.24
pid CLONE_NEWPID Process ID 2.6.24
user CLONE_NEWUSER User and group ID 3.8
UTS CLONE_NEWUTS Hostname and NIS domain name 2.6.19
cgroup CLONE_NEWCGROUP Control group root directory 4.6

这些namespace基本上覆盖了一个程序运行所需的环境,包括主机名、用户权限、文件系统、网络、进程号、进程间通信。所有的进程都会有namespace,可以理解为namespace是进程的一个属性。

每个进程都对应一个/proc/[pid]/ns目录,里面保存了该进程所在namespace的链接文件:

1
2
3
4
5
6
7
8
9
10
11
12
[root@control-plane ~]# ps
PID TTY TIME CMD
2718 pts/0 00:00:00 bash
18121 pts/0 00:00:00 ps
[root@control-plane ~]# ll /proc/2718/ns/
total 0
lrwxrwxrwx. 1 root root 0 Jan 7 11:43 ipc -> ipc:[4026531839]
lrwxrwxrwx. 1 root root 0 Jan 7 11:43 mnt -> mnt:[4026531840]
lrwxrwxrwx. 1 root root 0 Jan 7 11:43 net -> net:[4026531956]
lrwxrwxrwx. 1 root root 0 Jan 7 11:43 pid -> pid:[4026531836]
lrwxrwxrwx. 1 root root 0 Jan 7 11:43 user -> user:[4026531837]
lrwxrwxrwx. 1 root root 0 Jan 7 11:43 uts -> uts:[4026531838]

上述每个文件都是对应namespace的文件描述符,方括号中的值是文件的inode,如果两个进程所在的namespace是相同的,那么inode值也是相同的。如果某个namespace中没有进程了,它会被自动删除,但当某个应用程序打开了namespace文件时它不会被删除,通过这个特性,完美可以持有一个空的namspace,以便向其中添加进程。

三个系统调用

Linux内核提供的功能都会提供系统调用接口供应用程序使用,namespace自然也不例外。和namespace相关的系统调用主要有三个:clonesetnsunshare

clone:创建新进程并设置它的namespace

clone类似于fork系统调用,可以创建一个新进程,不同的是我们可以指定紫禁城要执行的函数,以及通过参数控制子进程的运行环境。下面是clone的定义:

1
2
# include <sched.h>
int clone(int (*fn) (void *), void *child_stack, int flags, void *arg, ...);

它有四个重要的参数:

  • fn参数是一个函数指针,子进程启动的时候会调用这个函数来执行
  • child_stack参数指定了子进程stack开始的内存地址,因为stack都会从高位到地委增长,所以这个指针需要指向分配stack的最高位地址
  • flags用来控制子进程的特性,比如新创建的进程是否与父进程共享虚拟内存等。比如可以传入CLONE_NEWNS标志使得新创建的新城拥有独立的Mount Namespace,也可以传入多个flags如CLONE_NEWNS | CLONE_NEWUTS | CLONE_NEWIPC
  • arg作为fn函数的参数

setns:让进程加入已经存在的namespace

setns能够把某个进程加入到给定的namespace,它的定义是这样的:

1
int setns(int fd, int nstype);
  • fd参数是一个文件描述符,指向/proc/[pid]/ns/目录下的某个namespace,调用这个函数的进程就会被加入到fd指向文件所代表的namespace,fd可以通过打开namespace对应的文件获取
  • nstype限定进程可以加入的namespace,可能的取值有:(如果不知道 fd 指向的 namespace 类型,比如fd是其他进程打开的,然后在应用中希望明确指定特种类型的 namespacenstype 就非常有用)
    • 0:可以加入任意的namespace
    • CLONE_NEWIPC:fd 必须指向IPC namespace
    • CLONE_NEWNET:fd 必须指向network namespace
    • CLONE_NEWNS:fd 必须指向mount namespace
    • CLONE_NEWPID:fd 必须指向PID namespace
    • CLONE_NEWUSER: fd 必须指向user namespace
    • CLONE_NEWUTS: fd 必须指向UTS namespace

unshare:让进程脱离到新的namespace

unshare()系统调用用于将当前进程和所在的namespace分离,并加入到一个新的namespace中,只需要指定想要分离的namespace即可,它的定义如下:

1
int unshare(int flags);
  • flags,它的含义和 clone 的 flags 相同。
    • CLONE_FILES: 子进程一般会共享父进程的文件描述符,如果子进程不想共享父进程的文件描述符了,可以通过这个flag来取消共享。
    • CLONE_FS: 使当前进程不再与其他进程共享文件系统信息。
    • CLONE_SYSVSEM: 取消与其他进程共享SYS V信号量。
    • CLONE_NEWIPC: 创建新的ipc namespace,并将该进程加入进来。

注意:unshare()setns()系统调用对PID Namespace的处理不太相同。

发生系统调用时,调用进程会为它的子进程分配一个新的pid namespace,且调用进程第一个创建的子进程会成为新namespace中的pid=1的进程,但是调用进程本身不会被移到新的pid namespace中。

为什么创建其他的namespace时unshare()setns()会直接进入新的namespace,而唯独PID Namespace不是如此呢?

因为调用getpid()函数得到的pid是根据调用者所在的PID Namespace而决定返回哪个pid,进入新的pid namespace会导致pid变化。而对用户态的程序和库函数来说,他们都认为进程的pid是一个常量,pid的变化会引起这些进程奔溃。

换句话说,一旦程序进程创建以后,那么它的pid namespace的关系就确定下来了。

 

docker 中的程序  可以理解成,是一组拥有相同namespace的子进程。 net memory,

namespace 概念有点像  进程 环境变量 。docker 中的 环境变量不一样。

 

======================================================================================================================================================================================================

Linux network namespace:ip命令

和Linux network namespace相关的操作子命令为ip netnsip link

创建net namespace

1
2
3
[root@control-plane ~]# ip netns add net1
[root@control-plane ~]# ip netns ls
net1

进入net namespace

1
2
3
4
5
6
7
8
9
[root@control-plane ~]# ip netns exec net1 ip addr
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
[root@control-plane ~]# ip netns exec net1 bash
[root@control-plane ~]# ip addr
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
[root@control-plane ~]# exit
exit

两个net namespace间通信

两个network namespace之间通信,可以借助虚拟网络设备veth pair。拓扑图如下:

cross-netns-two

创建veth pair

1
2
3
4
5
6
[root@control-plane ~]# ip link add type veth
[root@control-plane ~]# ip link
14: veth0@veth1: <BROADCAST,MULTICAST,M-DOWN> mtu 1500 qdisc noop state DOWN mode DEFAULT group default qlen 1000
link/ether 5a:b3:38:ff:19:68 brd ff:ff:ff:ff:ff:ff
15: veth1@veth0: <BROADCAST,MULTICAST,M-DOWN> mtu 1500 qdisc noop state DOWN mode DEFAULT group default qlen 1000
link/ether 76:fe:86:96:87:e4 brd ff:ff:ff:ff:ff:ff

可以看到,veth pair总是成对出现的,使用命令 ip link add xxx type veth peer name yyy 指定 veth pair 的两个网络接口名。

将veth pair加入net namespace

1
2
3
4
5
6
7
[root@control-plane ~]# ip link set veth0 netns net0
[root@control-plane ~]# ip link set veth1 netns net1
[root@control-plane ~]# ip netns exec net0 ip addr
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
14: veth0@if15: <BROADCAST,MULTICAST> mtu 1500 qdisc noop state DOWN group default qlen 1000
link/ether 5a:b3:38:ff:19:68 brd ff:ff:ff:ff:ff:ff link-netnsid 1

可以看到,net interface接入namespace后,初始状态为DOWN。

给veth pair配上IP地址

1
2
3
4
5
6
7
8
[root@control-plane ~]# ip netns exec net0 ip link set veth0 up 
[root@control-plane ~]# ip netns exec net0 ip addr add 10.1.1.1/24 dev veth0
[root@control-plane ~]# ip netns exec net0 ip route
10.1.1.0/24 dev veth0 proto kernel scope link src 10.1.1.1
[root@control-plane ~]# ip netns exec net1 ip link set veth1 up
[root@control-plane ~]# ip netns exec net1 ip addr add 10.1.1.2/24 dev veth1
[root@control-plane ~]# ip netns exec net1 ip route
10.1.1.0/24 dev veth1 proto kernel scope link src 10.1.1.2

可以看到,配置完IP地址后,自动生成了路由信息。

测试连通性

1
2
3
4
[root@control-plane ~]# ip netns exec net0 ping 10.1.1.2
PING 10.1.1.2 (10.1.1.2) 56(84) bytes of data.
64 bytes from 10.1.1.2: icmp_seq=1 ttl=64 time=0.055 ms
64 bytes from 10.1.1.2: icmp_seq=2 ttl=64 time=0.042 ms

多个net namespace之间通信

两个namespace之间通信可以借助veth pair,多个net namespace之间的通信则更适合用bridge来转接。拓扑图如下:

cross-netns-multi

使用ip link和brctl创建bridge

1
2
3
4
5
[root@control-plane ~]# ip link add br0 type bridge 
[root@control-plane ~]# ip link set dev br0 up
[root@control-plane ~]# ip link
16: br0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UNKNOWN mode DEFAULT group default qlen 1000
link/ether 2e:ea:a9:1e:70:3b brd ff:ff:ff:ff:ff:ff

创建veth pair

1
2
3
[root@control-plane ~]# ip link add type veth
[root@control-plane ~]# ip link add type veth
[root@control-plane ~]# ip link add type veth

将veth pair的一端挂到namespace,另一端挂到bridge

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
[root@control-plane ~]# ip link set dev veth1 netns net0
[root@control-plane ~]# ip netns exec net0 ip link set dev veth1 name eth0
[root@control-plane ~]# ip netns exec net0 ip addr add 10.0.1.1/24 dev eth0
[root@control-plane ~]# ip netns exec net0 ip link set dev eth0 up

[root@control-plane ~]# ip link set dev veth0 master br0
[root@control-plane ~]# ip link set dev veth0 up


[root@control-plane ~]# ip link set dev veth3 netns net1
[root@control-plane ~]# ip netns exec net1 ip link set dev veth3 name eth0
[root@control-plane ~]# ip netns exec net1 ip addr add 10.0.1.2/24 dev eth0
[root@control-plane ~]# ip netns exec net1 ip link set dev eth0 up

[root@control-plane ~]# ip link set dev veth2 master br0
[root@control-plane ~]# ip link set dev veth2 up


[root@control-plane ~]# ip link set dev veth5 netns net2
[root@control-plane ~]# ip netns exec net2 ip link set dev veth5 name eth0
[root@control-plane ~]# ip netns exec net2 ip addr add 10.0.1.3/24 dev eth0
[root@control-plane ~]# ip netns exec net2 ip link set dev eth0 up

[root@control-plane ~]# ip link set dev veth4 master br0
[root@control-plane ~]# ip link set dev veth4 up

测试连通性

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
[root@control-plane ~]# ip netns exec net0 ping 10.0.1.2
PING 10.0.1.2 (10.0.1.2) 56(84) bytes of data.
64 bytes from 10.0.1.2: icmp_seq=1 ttl=64 time=0.075 ms
64 bytes from 10.0.1.2: icmp_seq=2 ttl=64 time=0.059 ms
^C
--- 10.0.1.2 ping statistics ---
2 packets transmitted, 2 received, 0% packet loss, time 999ms
rtt min/avg/max/mdev = 0.059/0.067/0.075/0.008 ms

[root@control-plane ~]# ip netns exec net0 ping 10.0.1.3
PING 10.0.1.3 (10.0.1.3) 56(84) bytes of data.
64 bytes from 10.0.1.3: icmp_seq=1 ttl=64 time=0.105 ms
64 bytes from 10.0.1.3: icmp_seq=2 ttl=64 time=0.049 ms
^C
--- 10.0.1.3 ping statistics ---
2 packets transmitted, 2 received, 0% packet loss, time 999ms
rtt min/avg/max/mdev = 0.049/0.077/0.105/0.028 ms

上述数据表明,三个network namespace之间可以互相访问。

 

 

============================================================================================

 

 

 

 

 

 

 

参考:
https://www.cnblogs.com/maoqide/p/11259092.html

https://popofkh.github.io/2021/01/08/Linux-Namespace%E8%AF%A6%E8%A7%A3-%E4%B8%80-%EF%BC%9A%E7%BB%BC%E8%BF%B0/

 

posted @   redrobot  阅读(96)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 分享 3 个 .NET 开源的文件压缩处理库,助力快速实现文件压缩解压功能!
· Ollama——大语言模型本地部署的极速利器
· DeepSeek如何颠覆传统软件测试?测试工程师会被淘汰吗?
点击右上角即可分享
微信分享提示