Namespace 资源隔离
- 资源隔离 - linux有个chroot命令,可以实现资源隔离
- 主机隔离
- 网络隔离
- 进程间通信隔离
- 用户和用户组权限隔离
- 进程PID隔离
namespace 6项隔离
namespace | 系统调用参数 | 隔离内容 |
---|---|---|
UTS | CLONE_NEWUTS | 主机名与域名 |
IPC | CLONE_NEWIPC | 信号量、消息队列和共享内存 |
PID | CLONE_NEWPID | 进程编号 |
Network | CLONE_NEWNET | 网络设备、网络栈、端口等 |
Mount | CLONE_NEWNS | 挂载点(文件系统) |
User | CLONE_NEWUSER | 用户和用户组 |
同一namespace下的进程可以感知彼此的变化,而对外界的进程一无所知。此处的namespace是指Linux内核3.8及以后版本。
1. namespace api 4种操作方式
namespace的api包括clone()、setns()以及unshare(),还有/proc下的部分文件,
通过clone()在创建新进程的同时创建namespace
使用clone()来创建一个独立namespace的进程是常见方法,也是docker使用namespace最基本的方法:
int clone(int (*child_func)(void *), void *child_stack, int flags, void *arg);
查看/proc/[pid]/ns文件
用户就可以在/proc/[pid]/ns文件下看到指向不同namespace号的文件,形如[4034532445]者即为namespace号。
[root@localhost ~]# ls -l /proc/$$/ns
total 0
lrwxrwxrwx. 1 root root 0 Nov 25 21:21 ipc -> ipc:[4026531839]
lrwxrwxrwx. 1 root root 0 Nov 25 21:21 mnt -> mnt:[4026531840]
lrwxrwxrwx. 1 root root 0 Nov 25 21:21 net -> net:[4026531956]
lrwxrwxrwx. 1 root root 0 Nov 25 21:21 pid -> pid:[4026531836]
lrwxrwxrwx. 1 root root 0 Nov 25 21:21 user -> user:[4026531837]
lrwxrwxrwx. 1 root root 0 Nov 25 21:21 uts -> uts:[4026531838]
上面的link文件一旦被打开,只要打开的文件描述符(fd)存在,那么就算该namespace下的所有进程都已经结束,这个namespace也会一直存在,后续进程也可以再加入进来。docker通过文件描述符定位和加入一个存在的namespace是最基本的方式。
通过setns()加入一个已经存在的namespace
可以通过挂载的形式把namespace保留下来。在docker中,使用docker exec命令在已经运行着的容器中执行一个新的命令,就需要用到这个方法。通过setns()系统调用,进程从原先的namespace加入某个已经存在的namespace。
int setns(int fd, int nstype);
为了把新加入的namespace利用起来,需要引入execve()系列函数,该函数可以执行用户命令,最常用的就是调用/bin/bash并接受参数,运行起一个shell。
fd = open(argv[1], O_RDONLY);
setns(fd, 0);
execvp(argv[2], &argv[2]);
通过unshare()在原先进程上进行namespace隔离
调用unshare就是不启动一个新进程就可以起到隔离的效果,相当于跳出原先的namespace进行操作。linux自带的unshare就是通过unshare系统调用实现。docker目前并没有这个系统调用。
int unshare(int flags);
UTS namespace
UTS(UNIX Time-sharing System) namespace提供了主机名和域名的隔离,这样每个docker就可以拥有独立的主机名和域名了。
docker 每个镜像基本都以自身所提供的服务的名称来命名镜像的hostname,且不会对宿主机产生任何影响,其原理就是利用UTS namespace。