数据包在 K8s 中的一生

Borg、Omega、K8s:Google 十年三代容器管理系统的设计与思考

控制器模型

容器 Container

容器其实是一种沙盒技术。顾名思义,沙盒就是能够像一个集装箱一样,把你的应用“装”起来的技术。这样,应用与应用之间,就因为有了边界而不至于相互干扰;而被装进集装箱的应用,也可以被方便地搬来搬去,这不就是 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 隔离在了一个跟宿主机完全不同的世界当中。

Namespace 隔离机制

用 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 里的网络设备和配置。