Borg、Omega、K8s:Google 十年三代容器管理系统的设计与思考
容器其实是一种沙盒技术。顾名思义,沙盒就是能够像一个集装箱一样,把你的应用“装”起来的技术。这样,应用与应用之间,就因为有了边界而不至于相互干扰;而被装进集装箱的应用,也可以被方便地搬来搬去,这不就是 PaaS 最理想的状态嘛。
边界
对于进程来说,它的静态表现就是程序,平时存储在磁盘;而一旦运行起来,它就变成了计算机里的数据和状态的总和,这就是它的动态表现。
而容器技术的核心功能,就是通过约束和修改进程的动态表现,从而为其创造出一个“边界”。
对于 Docker 等大多数 Linux 容器来说,Cgroups 技术是用来制造约束的主要手段,而 Namespace技术则是用来修改进程视图的主要方法。
/bin/sh: 打开 bash 的 POSIX 标准模式 相当于 /bin/bash --posix
$ docker run -it busybox /bin/sh
上面这条指令翻译成人类的语言就是:请帮我启动一个容器,在容器里执行/bin/sh,并且给我分配一个命令行终端跟这个容器交互。这样,Ubuntu 机器就变成了一个宿主机,而一个运行着 /bin/sh 的容器,就跑在了这个宿主机里面。如果在容器里执行一下 ps 指令
PID USER TIME COMMAND
1 root 0:00 /bin/sh
10 root 0:00 ps
最开始执行的 /bin/sh,就是这个容器内部的第1号进程(PID=1),而这个容器里一共只有两个进程在运行。这就意味着,前面执行的/bin/sh,以及刚刚执行的 ps,已经被 Docker 隔离在了一个跟宿主机完全不同的世界当中。
用 clone() 系统调用创建一个新进程时,就可以在参数中指定 CLONE_NEWPID 参数
int pid = clone(main_function, stack_size, CLONE_NEWPID | SIGCHLD, NULL);
这时,新创建的这个进程将会“看到”一个全新的进程空间,在这个进程空间里,它的PID是1。之所以说“看到”,是因为这只是一个“障眼法”,在宿主机真实的进程空间里,这个进程的PID还是真实的数值,比如100。
除了我们刚刚用到的 PID Namespace,Linux 操作系统还提供了Mount、UTS、IPC、Network和 User 这些 Namespace,用来对各种不同的进程上下文进行“障眼法”操作。比如,Mount Namespace,用于让被隔离进程只看到当前 Namespace 里的挂载点信息;Network Namespace,用于让被隔离进程看到当前 Namespace 里的网络设备和配置。