Docker 入门笔记 7 - Namespace 简介(上)

Namespace 简介

众所周知,Docker 并不像KVM, Virtualbox,VMware那样虚拟出机器的各个硬件,Docker仅仅在同一操作系统中对应用实现资源分配并且实现应用之间的隔离, 让每个应用都觉得自己在一个独占的系统中,彼此之间隔离。 这一切都是依赖Linux 内核提供的 CGroups 和 Namespaces实现的。

CGroups 帮助限制、记录、调整进程组所使用的物力资源,比如CPU和内存。

Namespaces 是Linux提供的一种内核级别环境隔离的方法。Unix很早就有一个叫chroot的系统调用(通过修改根目录把用户jail到一个特定目录下),chroot提供了一种简单的隔离模式:chroot内部的文件系统无法访问外部的内容。Linux Namespace在此基础上,提供了对UTS、IPC、mount、PID、network、User等的隔离机制。

接下来的笔记将重点介绍Linux Namespaces的使用与原理,以加深对docker 的理解。内容参考自“Namespace in Operation”

Namesapce overview

目前,Linux内核里面实现了7种不同类型的namespace。

Namespace隔离内容
CgroupCLONE_NEWCGROUPCgroup root directory
IPCCLONE_NEWIPCSystem V IPC, POSIX message queues
NetworkCLONE_NEWNETNetwork devices, stacks, ports, etc.
MountCLONE_NEWNSMount points
PIDCLONE_NEWPIDProcess IDs
UserCLONE_NEWUSERUser and group IDs
UTSCLONE_NEWUTSHostname and NIS domain name

跟namespace相关的API

Namespace API由三个系统调用(clone(),unshare()和setns())和一些 /proc文件组成。

Clone

clone 将创建一个新的进程并把他放到新的namespace中

从本质上讲,clone 是传统UNIX fork系统调用的更一般的版本,其功能可以通过flags参数进行控制。 总的来说,有超过20个不同的 "CLONE_* 标志来控制clone()的各个操作,包括父进程和子进程是否共享虚拟内存,打开文件描述符和信号处置等资源。如果在调用中指定了一个CLONE_NEW *位,则会创建相应类型的新名称空间,并将新进程作为该名称空间的成员; 可以在标志中指定多个CLONE_NEW *位。

int clone(int (*child_func)(void *), void *child_stack, int flags, void *arg);

正是由于clone可以通过指定flag参数,实现进程间资源的隔离和共享,Linux中我们熟悉的pthread_create最终调用clone来实现线程创建,clone创建的子进程与父进程共享所有资源,子进程就相当与父进程内存空间下的一个线程了。

/proc/PID/ns 文件

每个进程都有一个 /proc/PID/ns 目录,每个类型的命名空间都包含一个文件。这些文件中的每一个都是一个特殊的符号链接,它提供了一种在进程的相关名称空间上执行某些操作的句柄。

$ ls -l /proc/$$/ns         # $$ is replaced by shell's PID
total 0
lrwxrwxrwx. 1 mtk mtk 0 Jan  8 04:12 ipc -> ipc:[4026531839]
lrwxrwxrwx. 1 mtk mtk 0 Jan  8 04:12 mnt -> mnt:[4026531840]
lrwxrwxrwx. 1 mtk mtk 0 Jan  8 04:12 net -> net:[4026531956]
lrwxrwxrwx. 1 mtk mtk 0 Jan  8 04:12 pid -> pid:[4026531836]
lrwxrwxrwx. 1 mtk mtk 0 Jan  8 04:12 user -> user:[4026531837]
lrwxrwxrwx. 1 mtk mtk 0 Jan  8 04:12 uts -> uts:[4026531838]

我们可以用这些链接查看两个进程是否在相同的名称空间中。

这些符号链接的一个用途是发现两个进程是否在相同的名称空间中。 内核确保如果两个进程在同一个名字空间中,则/proc/PID/ns 中对应的符号链接所报告的索引节点号将是相同的。

内核还会构造每个/proc/PID/ns 符号链接,以便指向由标识名称空间类型的字符串组成的名称,后跟inode编号。 我们可以使用ls -l或者readlink命令来检查这个名字。

另外,这些文件的一个小作用是可以帮助我们保留namesapce。 通常一个namesapce的所有进程都退出后,内核会回收这个namesapce。但如果这些文件中的某个正被使用,则namesapce会一直存在直到文件句柄被释放。

setns

这个函数将当前进程加入到已有的namespace中, 更准确地说,setns() 将调用进程与特定名称空间类型的一个实例分离,并将进程与同一名称空间类型的另一个实例重新关联。声明如下:

int setns(int fd, int nstype);

fd: 
    指向/proc/[pid]/ns/目录里相应namespace对应的文件,
    表示要加入哪个namespace

nstype:
    指定namespace的类型(上面的任意一个CLONE_NEW*):
    1. 如果当前进程不能根据fd得到它的类型,如fd由其他进程创建,
    并通过UNIX domain socket传给当前进程,那么就需要通过nstype来指定fd指向的namespace的类型
    2. 如果进程能根据fd得到namespace类型,比如这个fd是由当前进程打开的,那么nstype设置为0即可
    
unshare

使当前进程退出指定类型的namespace,并加入到新创建的namespace(相当于创建并加入新的namespace), 声明如下:

int unshare(int flags);

flags:
    指定一个或者多个上面的CLONE_NEW*,
    这样当前进程就退出了当前指定类型的namespace并加入到新创建的namespace

IPC Namespace

由于容器的本质是资源隔离,容器内部的所有进程都只能访问主机分配给它的资源。因此,容器内部进程间的通信,实际上是同一主机上同一个
PID空间下的进程间通信。因此,在访问IPC资源时需要提供一个全局唯一的32bit 标识符。

而容器的资源隔离特征要求, 容器内部的所有进程的IPC通信不应跨越容器。这就意味着Namespace需要对这个ID隔离,不能让别的Namespace的进程看到。换句话说,每个IPC命名空间都有自己的一套System V IPC标识符和它自己的POSIX消息队列文件系统。因此,只有在同一个 IPC namespace 的进程之间才能互相通信.

检查当前IPC资源状态

通过ipcs命令,可以看到当前bash中用于进程通信的共享内存和信号量的信息都有内容,只有消息队列是空的

$ ipcs
------ Message Queues --------
key        msqid      owner      perms      used-bytes   messages

------ Shared Memory Segments --------
key        shmid      owner      perms      bytes      nattch     status      
0x00000000 5898263    chic       600        7057408    2          dest      
0x00000000 5603352    chic       600        245760     2          dest      
0x00000000 3506201    chic       600        28672      2          dest      
0x00000000 3538970    chic       600        28672      2          dest      
0x00000000 7045147    chic       600        524288     2          dest      
0x00000000 4161564    chic       600        524288     2          dest       
…         
------ Semaphore Arrays --------
key        semid      owner      perms      nsems     
0x002fa327 0          root       666        2         

查看当前bash的名空间

# ll /proc/$$/ns
总用量 0
dr-x--x--x 2 root root 0 Dec 29 16:04 ./
dr-xr-xr-x 9 root root 0 Dec 29 14:01 ../
lrwxrwxrwx 1 root root 0 Dec 29 16:04 cgroup -> cgroup:[4026531835]
lrwxrwxrwx 1 root root 0 Dec 29 16:04 ipc -> ipc:[4026531839]
lrwxrwxrwx 1 root root 0 Dec 29 16:04 mnt -> mnt:[4026531840]
lrwxrwxrwx 1 root root 0 Dec 29 16:04 net -> net:[4026531957]
lrwxrwxrwx 1 root root 0 Dec 29 16:04 pid -> pid:[4026531836]
lrwxrwxrwx 1 root root 0 Dec 29 16:04 user -> user:[4026531837]
lrwxrwxrwx 1 root root 0 Dec 29 16:04 uts -> uts:[4026531838]

利用unshare命令 运行一个新的bash并将它加入到新的ipc namespace中

# unshare --ipc /bin/bash
# ll /proc/$$/ns
总用量 0
dr-x--x--x 2 root root 0 Dec 29 16:09 ./
dr-xr-xr-x 9 root root 0 Dec 29 16:09 ../
lrwxrwxrwx 1 root root 0 Dec 29 16:09 cgroup -> cgroup:[4026531835]
lrwxrwxrwx 1 root root 0 Dec 29 16:09 ipc -> ipc:[4026532468]
lrwxrwxrwx 1 root root 0 Dec 29 16:09 mnt -> mnt:[4026531840]
lrwxrwxrwx 1 root root 0 Dec 29 16:09 net -> net:[4026531957]
lrwxrwxrwx 1 root root 0 Dec 29 16:09 pid -> pid:[4026531836]
lrwxrwxrwx 1 root root 0 Dec 29 16:09 user -> user:[4026531837]
lrwxrwxrwx 1 root root 0 Dec 29 16:09 uts -> uts:[4026531838]

比较两个bash 的名空间可以发现, 只有 ipc namespace发生了变化

查看新的bash的IPC资源状态,可以看见目前还没有任何IPC资源

# ipcs
------ Message Queues --------
key        msqid      owner      perms      used-bytes   messages

------ Shared Memory Segments --------
key        shmid      owner      perms      bytes      nattch     status      
       
------ Semaphore Arrays --------
key        semid      owner      perms      nsems     
  

尝试创建一个新的Message queue

# ipcmk --queue         (可使用 ipcrm msg 0  命令删除)
Message queue id: 0
# ipcs
------ Message Queues --------
key        msqid      owner      perms      used-bytes   messages
0x5337e437 0          root       644        0            0
------ Shared Memory Segments --------
key        shmid      owner      perms      bytes      nattch     status      
       
------ Semaphore Arrays --------
key        semid      owner      perms      nsems      

在一般开发时,要使用IPC namespace 也很简单,只需要把“CLONE_NEWPIC”标记添加到“clone”的调用中,而不需要其它额外的步骤。IPC namespace也能和其他namespace组合使用。

需要说明的是,IPC namesapce 只隔离信号量, 消息队列和共享内存这三种 System V IPC 资源。一些传统的Unix
IPC 手段不在隔离范围内, 比如:pipe、FIFO、signal等。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值