Docker基础技术:Linux Namespace(上) | 酷 壳 - CoolShell
1. 利用namespace进行资源隔离
namespace相关的基本系统调用:setns()、clone()、unshare、使用主机/proc下的相关文件(主要是/proc/{pid}/ns/下的文件)。
setns(int fd, int nstype): 使当前进程加入指定的namespace
fd代表打开namespace文件的文件描述符, nstype检查fd对应的namespace类型与nstype是否对应,nstype为0表示不检查namespace类型
fd=open("/proc/1111/ns/uts") ///打开指定的namespace文件
setns(fd,0) ///加入/proc/1111/ns/uts namespace
execvp("hostname") ///在指定namespace下面打印主机名
int clone(int (fn)(void ), void child_stack, int flags, void arg);
强化版的fork,在当前进程中创建子进程。这里fn是要执行的函数指针。我们知道进程的4要素,这个就是指向程序的指针,就是所谓的“剧本", child_stack明显是为子进程分配系统堆栈空间(在[Linux](http://lib.csdn.net/base/linux)下系统堆栈空间是2页面,就是8K的内存,其中在这块内存中,低地址上放入了值,这个值就是进程控制块task_struct的值),flags就是标志用来描述你需要从父进程继承哪些资源, arg就是传给子进程的参数)。下面是flags可以取的值
标志含义
CLONE_PARENT 创建的子进程的父进程是调用者的父进程,新进程与创建它的进程成了“兄弟”而不是“父子”
CLONE_FS 子进程与父进程共享相同的文件系统,包括root、当前目录、umask
CLONE_FILES 子进程与父进程共享相同的文件描述符(file descriptor)表
CLONE_NEWNS 在新的namespace启动子进程,namespace描述了进程的文件hierarchy
CLONE_SIGHAND 子进程与父进程共享相同的信号处理(signal handler)表
CLONE_PTRACE 若父进程被trace,子进程也被trace
CLONE_VFORK 父进程被挂起,直至子进程释放虚拟内存资源
CLONE_VM 子进程与父进程运行于相同的内存空间
CLONE_PID 子进程在创建时PID与父进程一致
CLONE_THREAD Linux 2.4中增加以支持POSIX线程标准,子进程与父进程共享相同的线程群
CLONE_NEWUTS 在子进程中创建新的主机名和域名namespace,从而与父进程的namespace隔离 CLONE_NEWIPC 在子进程中创建新的 IPC namespace,从而与父进程的namespace隔离 CLONE_NEWNET 在子进程中创建新的network namespace, 从而与父进程的namespace隔离 CLONE_NEWNS 在子进程中创建新的mount namespace, 从而与父进程的namespace隔离 CLONE_NEWUSER 在子进程中创建新的 user and usergroup namespace, 从而与父进程的namespace隔离.UTS namespace
某个进程都可以有单独的uts(主机名和域名)命名空间,这样每个docker容器都可以作为网络的一个节点,并不仅仅是宿主机上的一个进程。
clone(sethostname("forTest"), CLONE_NEWUTS)
在“sethostname”这个进程结束后, 不会改变宿主机的主机名,因为带了CLONE_NEWUTS标志,sethostname这个进程运行在新建的自己的uts namespace内
如果不带CLONE_NEWUTS那么会改变主机的主机名
IPC namespace
涉及的IPC资源包括:信号量、消息队列和共享内存。 每一个IPC资源都有一个全局唯一的ID标识符。因此一个ipc namespace中实际上包含了标识符以及实现POSIX消息队列的文件系统。因此想要使用ipc相互通信,那么相互通信的进程需要处在相同的ipc namespace下。
PID namespace
可以对进程重新标号,并且在不同pid namespace下的进程可以使用相同的pid.PID namespace比较特殊,namespace之间呈现树状结构,根节点是 root namespace, 其他所有的pid namespace都是root namespace的孩子节点。父pid namespace可以看到子pid namespace中的所有进程,并且父pid namespace可以向子pid namespace中的进程发送信号。
每个namespace中的第一个进程“PID 1”,都像linux的init进程一样拥有特殊权限和作用。
如果在新的pid namespace中重新挂载proc文件系统,那么在/proc目录下面只会oi发现所属pid namespace的所有进程
在root pid namespace下可以看到所有的进程。
mount namespace
挂载点隔离,不同mount namepace中发生的挂载行为不会相互影响(默认私有共享的情况下)。mount挂载方式有几种:
- 共享挂载:共享挂载对象的任何挂载行为都会共享给与之共享的挂载对象
- 传播挂载:单向共享,如图
- 私有挂载:不共享
注意docker文件系统是分层的,一个image可以启动多个container,这时候会有一个问题,如果每个container对大家共有的部分都有可写的权限,就会出问题。所以docker启动的时候会加载镜像的文件系统那层是只读的,然后每个contianer 获取自己的可读写的层,如果container要修改只读层的文件,那么该文件就会从只读层提取到读写层。只读层的文件就被读写层的文件覆盖了,但只读层的那个文件依然存在 这个就实现了文件系统上的隔离。使用docker commit会提交自己对image文件系统的修改。
下面是一个创建network namespace的例子:
# 在network节点上添加一个名为nstest的namespace
[root@network ~]# ip netns add nstest
[root@network ~]#
# 查看namespace中的网络信息
[root@network ~]# ip netns exec nstest ip addr
1: lo: <LOOPBACK> mtu 65536 qdisc noop state DOWN
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
[root@network ~]#
# 创建一对虚拟网卡veth-a 和 veth-b,两者由一根虚拟网线连接
[root@network ~]# ip link add veth-a type veth peer name veth-b
[root@network ~]#
# 设置虚拟网卡地址
[root@network ~]# ip addr add 10.0.0.1/24 dev veth-a
[root@network ~]# ip link set dev veth-a up
[root@network ~]# ip link set veth-b netns nstest
[root@network ~]# ip netns exec nstest ip addr add 10.0.0.2/24 dev veth-b
[root@network ~]# ip netns exec nstest ip link set dev veth-b up
# 测试连通性
[root@network ~]# ping 10.0.0.2 -I veth-a
PING 10.0.0.2 (10.0.0.2) from 10.0.0.1 veth-a: 56(84) bytes of data.
64 bytes from 10.0.0.2: icmp_seq=1 ttl=64 time=0.048 ms
64 bytes from 10.0.0.2: icmp_seq=2 ttl=64 time=0.043 ms
64 bytes from 10.0.0.2: icmp_seq=3 ttl=64 time=0.043 ms
^C
--- 10.0.0.2 ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 1999ms
rtt min/avg/max/mdev = 0.043/0.044/0.048/0.008 ms
[root@network ~]#
[root@network ~]#
[root@network ~]# ip netns exec nstest ping 10.0.0.1
PING 10.0.0.1 (10.0.0.1) 56(84) bytes of data.
64 bytes from 10.0.0.1: icmp_seq=1 ttl=64 time=0.033 ms
64 bytes from 10.0.0.1: icmp_seq=2 ttl=64 time=0.042 ms
64 bytes from 10.0.0.1: icmp_seq=3 ttl=64 time=0.044 ms
network namespace
使用network namespace可以对网络进行隔离。同一个namespace下的设备可以像在一台主机上一样互相访问。不同namespace下的设备无法直接互相访问。