Docker容器隔离
对于Docker等大多数Linux容器来说,Namespace技术是用来修改进程视图的主要方法。
示例
通过创建一个容器来展示Linux中对PID资源的隔离
1 |
|
在Docker里最开始执行的/bin/sh
是这个容器内部的第1号进程(PID=1),而这个容器里共有两个进程在运行。说明/bin/sh
和ps
已经被Docker隔离在一个跟宿主机不同的世界中。
原本每当在宿主机上运行一个程序,操作系统都会给它分配一个PID(进程号),比如PID=100。这个编号是进程的唯一标识,当Docker在一个容器中运行程序时,会对被隔离应用的进程空间做处理,使得这些进程只能看到重新计算过的PID。在实际的宿主机操作系统里,它还是原先的进程。
Linux Namespace机制
Linux中的Namespace不止有PID,还提供了Mount、UTS、IPC、Network、User等,用来对进程上下进行处理。比如Mount Namespace用于让被隔离进程只看到当前Namespace里的挂载点信息,Network Namespace用于让被隔离进程只看到当前Namespace里的网络设备和配置。
Linux中Namespace的使用方式其实只是Linux创建新进程的一个可选参数。以上面PID Namespace为例,Linux中创建线程的系统调用是clone()
,如int pid = clone(main_function, stack_size, SIGCHLD, NULL)
,这个系统调用会为我们创建一个新的进程,并返回它的PID。而当用clone()
系统调用创建一个新进程时,就可以在参数中指定CLONE_NEWPID参数,比如int pid = clone(main_function, stack_size, CLONE_NEWPID | SIGCHLD, NULL)
,新创建的这个进程就会看到全新的一个进程空间。多次执行上面的clone()
调用,就会创建多个PID Namespace
,每个Namespace里的应用进程都会认为自己是容器里的第1号进程,既看不到宿主机里真正的进程空间,也看不到其他PID Namespace
里的具体情况。
缺点
基于Linux Namespace的隔离机制相比于虚拟化技术也有很多不足之处,主要问题是隔离得不彻底。既然容器只是在宿主机上运行的一种特殊进程,多个容器之间使用的就还是同一个宿主机的操作系统内核。尽管可以在容器里通过Mount Namespace单独挂载其他版本的操作系统文件,比如CentOS或者Ubuntu,但这并不能改变共享宿主机内核的事实。意味着如果要在Windows宿主机上运行Linux容器,或者在低版本的Linux宿主机上运行高版本的Linux容器,都是不行的。
其次在Linux内核中有很多资源和对象是不能被Namespace化的,比如时间。这就意味着,如果你的容器中的程序使用settimeofday(2)
系统调用修改了时间,那么整个宿主机的时间都会被随之修改。