Docker namespace资源隔离Linux内核机制深入剖析-Docker商业环境实战

数据云技术社区 · · 842 次点击 · · 开始浏览    
这是一个创建于 的文章,其中的信息可能已经有所发展或是发生改变。

专注于大数据及容器云核心技术解密,可提供全栈的大数据+云原生平台咨询方案,请持续关注本套博客。如有任何学术交流,可随时联系。更多内容请关注《数据云技术社区》公众号。

1 Namespace资源隔离

  • Docker 和虚拟机技术一样,从操作系统级上实现了资源的隔离,它本质上是宿主机上的进程(容器进程),所以资源隔离主要就是指进程资源的隔离。实现资源隔离的核心技术就是 Linux namespace。
  • 隔离意味着可以抽象出多个轻量级的内核(容器进程),这些进程可以充分利用宿主机的资源,宿主机有的资源容器进程都可以享有,但彼此之间是隔离的,同样,不同容器进程之间使用资源也是隔离的,这样,彼此之间进行相同的操作,都不会互相干扰,安全性得到保障。
  • 为了支持这些特性,Linux namespace 实现了 6 项资源隔离,基本上涵盖了一个小型操作系统的运行要素,包括主机名、用户权限、文件系统、网络、进程号、进程间通信。

2 UTS namespace

  • UTS namespace 提供了主机名和域名的隔离,这样每个容器就拥有独立的主机名和域名了,在网络上就可以被视为一个独立的节点,在容器中对 hostname 的命名不会对宿主机造成任何影响。
这 6 项资源隔离分别对应 6 种系统调用,通过传入上表中的参数,调用 clone() 函数来完成。
int clone(int (*child_func)(void *), void *child_stack, int flags, void *arg);

#define _GNU_SOURCE
#include#include#include#include#include#include#define STACK_SIZE (1024 * 1024)

static char child_stack[STACK_SIZE];
char* const child_args[] = {
  "/bin/bash",
  NULL
};

int child_main(void* arg) {
  printf("在子进程中!\n");
  sethostname("Namespace", 12);
  execv(child_args[0], child_args);
  return 1;
}

int main() {
  printf("程序开始: \n");
  int child_pid = clone(child_main, child_stack+STACK_SIZE,
    CLONE_NEWUTS | SIGCHLD, NULL);
  waitpid(child_pid, NULL, 0);
  printf("已退出\n");
  return 0

该程序骨架调用 clone() 函数实现了子进程的创建工作,并定义子进程的执行函数,clone() 第二个参数指定了子进程运行的栈空间大小,第三个参数即为创建不同 namespace 隔离的关键。

root@local:~# gcc -Wall namespace.c -o main.o && ./main.o
程序开始:
在子进程中!
root@NewNamespace:~# exit
exit
已退出
root@local:~#  <- 回到原来的hostname="" <="" pre="">
复制代码

2 IPC namespace(Interprocess Communication)

  • 容器中进程间通信采用的方法包括常见的信号量、消息队列和共享内存。然而与虚拟机不同的是,容器内部进程间通信对宿主机来说,实际上是具有相同PID namespace中的进程间通信,因此需要一个唯一的标识符来进行区别。
  • 申请IPC资源就申请了这样一个全局唯一的32位ID,所以IPC namespace中实际上包含了系统IPC标识符以及实现POSIX消息队列的文件系统。在同一个IPC namespace下的进程彼此可见,而与其他的IPC namespace下的进程则互相不可见。
int child_pid = clone(child_main, child_stack+STACK_SIZE,
           CLONE_NEWIPC | CLONE_NEWUTS | SIGCHLD, NULL);
           
我们首先在shell中使用ipcmk -Q命令创建一个message queue。
root@local:~# ipcmk -Q
Message queue id: 32769

通过ipcs -q可以查看到已经开启的message queue,序号为32769
root@local:~# ipcs -q
------ Message Queues --------
key        msqid   owner   perms   used-bytes   messages
0x4cf5e29f 32769   root    644     0            0

编译运行加入了IPC namespace隔离的ipc.c,在新建的子进程中调用的shell中执行ipcs -q查看message queue。

root@local:~# gcc -Wall ipc.c -o ipc.o && ./ipc.o
程序开始:
在子进程中!
root@NewNamespace:~# ipcs -q
------ Message Queues --------
key   msqid   owner   perms   used-bytes   messages
root@NewNamespace:~# exit
exit
已退出
复制代码
  • 上面的结果显示中可以发现,已经找不到原先声明的message queue,实现了IPC的隔离。

3 PID namespace

  • PID namespace隔离非常实用,它对进程PID重新标号,即两个不同namespace下的进程可以有同一个PID。
  • 每个PID namespace都有自己的计数程序。内核为所有的PID namespace维护了一个树状结构,最顶层的是系统初始时创建的,我们称之为root namespace。
  • 不同的PID namespaces会形成一个等级体系。所属的父节点可以看到子节点中的进程,并可以通过信号等方式对子节点中的进程产生影响。反过来,子节点不能看到父节点PID namespace中的任何内容。
  • 在root namespace中可以看到所有的进程,并且递归包含所有子节点中的进程。
int child_pid = clone(child_main, child_stack+STACK_SIZE,
           CLONE_NEWPID | CLONE_NEWIPC | CLONE_NEWUTS 
           | SIGCHLD, NULL);

root@local:~# gcc -Wall pid.c -o pid.o && ./pid.o
程序开始:
在子进程中!
root@NewNamespace:~# echo $$
1                      <<--注意此处看到shell的pid变成了1

退出后如果再次执行可以看到效果如下
root@local:~# echo $$
17542
复制代码
  • 在子进程的shell中执行了ps aux/top之类的命令,发现还是可以看到所有父进程的PID,那是因为我们还没有对文件系统进行隔离,ps/top之类的命令调用的是真实系统下的/proc文件内容,看到的自然是所有的进程。
  • 为了实现一个稳定安全的容器,PID namespace还需要进行一些额外的工作才能确保其中的进程运行顺利。

4 user namespace

  • user namespace主要隔离安全相关的标识符和属性,包括用户ID、用户组ID、root目录、key以及特殊权限。简单来说,我们可以在Linux中用非root的用户来创建一个容器,它创建的容器进程却属于拥有超级权限的用户。

5 network namespace

  • network namespace主要提供了关于网络资源的隔离,包括网络设备、IPv4和IPv6协议栈、IP路由表、防火墙、套接字等。简单说,我们在每个容器中都可以启动一个Apache进程并占用“80端口”而不会出现端口冲突。
  • 假设计算机只有一个物理网络设备时,该设备只能位于一个network namespace下提供网络服务。解决的方法是通过创建veth pair在不同的network namespace间进行通信。

6 总结

专注于大数据及容器云核心技术解密,可提供全栈的大数据+云原生平台咨询方案,请持续关注本套博客。如有任何学术交流,可随时联系。更多内容请关注《数据云技术社区》公众号。


有疑问加站长微信联系(非本文作者)

本文来自:掘金

感谢作者:数据云技术社区

查看原文:Docker namespace资源隔离Linux内核机制深入剖析-Docker商业环境实战

入群交流(和以上内容无关):加入Go大咖交流群,或添加微信:liuxiaoyan-s 备注:入群;或加QQ群:692541889

842 次点击  
加入收藏 微博
暂无回复
添加一条新回复 (您需要 登录 后才能回复 没有账号 ?)
  • 请尽量让自己的回复能够对别人有帮助
  • 支持 Markdown 格式, **粗体**、~~删除线~~、`单行代码`
  • 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet
  • 图片支持拖拽、截图粘贴等方式上传