Docker Namespce 隔离

如果我们想像 Docker 一样实现一个简陋的资源隔离的容器,我们需要隔离文件系统、需要隔离网络、需要隔离主机名、需要隔离进程间的通信,需要隔离PID,需要隔离用户和用户组。

Namespace 是 Linux 提供的资源隔离机制。只有在同一个 Namespace 下的进程可以相互联系,但无法感受到外部进程的存在,营造出处于一个独立的系统环境中的错觉,从而实现了隔离。

Linux内核中就提供了这六种 namespace 隔离的系统调用,如下表所示。

image.png

Linux提供的操作 namespace 相关 API 介绍如下:
API包括 clone()、setns( )以及 unshare(),还有 /proc 下的部分文件。为了确定隔离的到底是哪种 namespace,在使用这些 API 时,通常需要指定上面图中的参数的六个常数的一个或多个,通过 |(位或)操作来实现。

  1. clone()
    clone()是fork()的一种实现方式。调用fork()函数时系统会创建新进程,为其分配资源,并把原来进程中的值复制到新进程中,可通过 fpid 区分新进程和父进程
int clone(int (*child_func)(void *), void *child_stack, int flags, void *arg);

参数:
child_func 传入子进程运行的程序主函数。
child_stack 传入子进程使用的栈空间
flags 表示使用哪些 CLONE_* 标志位
args 则可用于传入用户参数

  1. setns()
    setns()用于加入已存在的namespace
int setns(int fd, int nstype);

参数:
fd:要加入namespace的文件描述符(指向/proc/[pid]/ns目录)。
nstype:要加入namespace的类型,用于检查,0表示不检查。

  1. unshare()
    unshare()在原进程上进行namespace隔离。
int unshare(int flags)

unshare()不启动新进程,但是跳出了原来的namespace。

UTS(UNIX Time-sharing System)namespace

分时系统(Time-sharing System)中一台主机连接了若干个终端,每个终端有一个用户在使用。

用户交互式地向系统提出命令请求,系统接受每个用户的命令,采用时间片轮转方式处理服务请求,并通过交互方式在终端上向用户显示结果。用户根据上步结果发出下道命令。

分时操作系统将CPU的时间划分成若干个片段,称为时间片。操作系统以时间片为单位,轮流为每个终端用户服务。每个用户轮流使用一个时间片而使每个用户并不感到有别的用户存在。

UTS(Unix Time-sharing System) namespace提供了主机名和域名的隔离,使每个Docker容器可以拥有独立的主机名和域名,在网络上可以视为独立的节点。
下面通过 go 来简单实现 UTS 隔离

package main

import (
    "log"
    "os"
    "os/exec"
    "syscall"
)

func main() {
    cmd := exec.Command("sh")
    cmd.SysProcAttr = &syscall.SysProcAttr{
        Cloneflags: syscall.CLONE_NEWUTS,
    }
    cmd.Stdin = os.Stdin
    cmd.Stdout = os.Stdout
    cmd.Stderr = os.Stderr
    if err := cmd.Run(); err != nil {
        log.Fatal(err)
    }
}

在 Linux 环境下执行

go run uts.go

此时在一个新的进程中执行了sh命令,由于指定了 flag syscall.CLONE_NEWUTS, 此时已经与之前的进程不在同一个 UTS namespace中了。在新 sh 和原 sh 中分别执行 ls -l /proc/$$/ns 进行验证,可以看到这里两个只有 uts 所指向的 ID 不同,说明已经隔离成功


image.png

IPC(Interprocess Communication)namespace

容器中进程间通信采用的方法包括常见的信号量、消息队列和共享内存。然而与虚拟机不同的是,容器内部进程间通信对宿主机来说,实际上是具有相同 PID namespace 中的进程间通信,因此需要一个唯一的标识符来进行区别。申请 IPC 资源就申请了这样一个全局唯一的 32 位 ID,所以 IPC namespace 中实际上包含了系统 IPC 标识符以及实现POSIX消息队列的文件系统。在同一个 IPC namespace下的进程彼此可见,而与其他的IPC namespace下的进程则互相不可见。
IPC namespace在代码上的变化与UTS namespace相似,只是标识位有所变化,需要加上 CLONE_NEWIPC 参数。
我们首先在 shell 中使用 ipcmk -Q 命令创建一个message queue。
使用 ipcs -q 查看已经有一个 id 为 0 message queue
我们可以运行 ipd.go 新建的子进程中执行 ipcs -q 查看 message queue。


image.png

上面的结果显示中可以发现,已经找不到原先声明的message queue,实现了IPC的隔离。

Process identifier(PID) namespace

PID是大多数操作系统的内核用于唯一标识进程的一个数值。
PID为1的进程是init,作为所有进程的父进程,不会处理来自其他进程的信号(信号屏蔽),并维护一张进程表,当有子进程变成孤儿时会回收其资源并结束进程。
PID namespace隔离对进程PID重新编号,两个不同namespace下的进程没有关系,因此PID也可以相同。内核为所有的PID namespace维护了一个树状结构。


image.png

其中:
1)每个PID namespace中的第一个进程拥有特权。
2)一个namespace中的进程不能影响父节点或兄弟节点namespace中的进程。
3)root namespace中可以看到所有的进程,包括所有后代节点中的namespace。
4)在外部可以通过监控Docker daemon所在的PID namespace中的所有进程和子进程来实现对Docker中运行的程序的监控。
同样修改 flag 运行代码看 pid 隔离的效果:


image.png

Mount namespace

mount namespace 是历史上第一个Linux namespace,通过隔离文件系统挂载点隔离文件系统,标识位为CLONE_NEWNS。隔离之后不同的mount namespace中的文件结构互不影响。

可以通过/proc/[pid]/mounts查看所有挂载在当前namespace中的文件系统。进程创建mount namespace时把当前文件结构复制给新的namespace。

挂载传播(mount propagation)定义了挂载对象之间的关系,解决了文件结构复制过程中子节点namespace影响父节点namespace文件系统的问题。

共享关系(share relationship):存在挂载关系的两个挂载对象中的事件会双向传播
从属关系(slave relationship):挂载对象中的事件只能按指向从属对象的方向传播(共享挂载—>从属挂载)

Network namespace

network namespace提供了关于网络资源的隔离,包括网络设备、IPv4和IPv6协议栈、IP路由表、防火墙、/proc/net目录、/sys/class/net目录、套接字(socket)等。

注意:
一个物理的网络设备最多存在于一个network namespace中
可以通过veth pair在不同的network namespace中创建通道进行通信。
一般情况下,物理网络设备都分配在最初的root namespace中。
User namespace
user namespace主要隔离了安全相关的标识符和属性(用户ID、用户组ID、root目录、key(密钥)、特殊权限)。

因此用 clone() 创建的新进程在新的 user namespace 中可以拥有不同的用户和用户组,在新进程创建的容器中,它是超级用户,但在容器之外只是普通用户。

Linux中,特权用户的 user ID 是 0,user ID 非 0 的进程启动 user namespace后 user ID 可以变为 0

参考:
http://www.sel.zju.edu.cn/?p=556
http://lionheartwang.github.io/blog/2018/03/18/dockerzi-yuan-ge-chi-he-xian-zhi-shi-xian-yuan-li/

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,126评论 6 481
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,254评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 152,445评论 0 341
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,185评论 1 278
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,178评论 5 371
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,970评论 1 284
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,276评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,927评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,400评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,883评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,997评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,646评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,213评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,204评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,423评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,423评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,722评论 2 345

推荐阅读更多精彩内容