网络虚拟化基础一:linux名称空间Namespaces

一 介绍

    若是把linux操做系统比做一个房子,那命名空间指的就是这个房子中的一个个房间,住在每一个房间里的人都自觉得独享了整个房子的资源,但其实你们仅仅只是在共享的基础之上互相隔离,共享指的是共享全局的资源,而隔离指的是局部上彼此保持隔离,于是命名空间的本质就是指:一种在空间上隔离的概念,当下盛行的许多容器虚拟化技术(典型表明如LXC、Docker)就是基于linux命名空间的概念而来的。php

    一方面:若是咱们要深刻研究docker技术,linux namespace是必须掌握的基础知识。node

    另外一方面:Neutron也使用Linux命名空间(Network Namespace),这是理解openstack网络机制的根本。linux

 

    Linux Namespace是Linux提供的一种内核级别环境隔离的方法,关于隔离的概念其实你们早已接触过:好比在光盘修复模式下,能够用chroot切换到其余的文件系统,chroot提供了一种简单的隔离模式:chroot内部的文件系统没法访问外部的内容。Linux Namespace在此基础上又提供了不少其余隔离机制。docker

    当前,Linux 支持6种不一样类型的命名空间。它们的出现,使用户建立的进程可以与系统分离得更加完全,从而不须要使用更多的底层虚拟化技术。详细请点击shell

二 Linux Namespaces深刻分析

主要是三个系统调用centos

  • clone() – 实现线程的系统调用,用来建立一个新的进程,并能够经过设计上述参数达到隔离。
  • unshare() – 使某进程脱离某个namespace
  • setns() – 把某进程加入到某个namespace

首先,咱们来看一下一个最简单的clone()系统调用的示例,(后面,咱们的程序都会基于这个程序作修改):安全

文件名:clone.cbash

#define _GNU_SOURCE 
#include <sys/types.h>
#include <sys/wait.h>
#include <stdio.h>
#include <sched.h>
#include <signal.h>
#include <unistd.h>

/* 定义一个给 clone 用的栈,栈大小1M */
#define STACK_SIZE (1024 * 1024) 
static char container_stack[STACK_SIZE];

char* const container_args[] = {
    "/bin/bash",
    NULL
};

int container_main(void* arg)
{
    printf("Container - inside the container!\n");
    /* 直接执行一个shell,以便咱们观察这个进程空间里的资源是否被隔离了 */
    execv(container_args[0], container_args);
    printf("Something's wrong!\n");
    return 1;
}

int main()
{
    printf("Parent - start a container!\n");
    /* 调用clone函数,其中传出一个函数,还有一个栈空间的(为何传尾指针,由于栈是反着的) */
    int container_pid = clone(container_main, container_stack+STACK_SIZE, SIGCHLD, NULL);
    /* 等待子进程结束 */
    waitpid(container_pid, NULL, 0);
    printf("Parent - container stopped!\n");
    return 0;
}

 测试开辟一个新的名称空间:网络

[root@www ~]# gcc -o clone clone.c #编译clone.c
[root@www ~]# ./clone #执行编译的结果
Parent - start a container!
Container - inside the container!
[root@www ~]#         #进入了一隔离的空间
[root@www ~]# exit    #退出该空间
exit
Parent - container stopped!
[root@www ~]#         #又回到最初的空间

从上面的程序,咱们能够看到,这和pthread基本上是同样的玩法。可是,对于上面的程序,父子进程的进程空间是没有什么差异的,父进程能访问到的子进程也能。app

下面, 让咱们来看几个例子看看,Linux的Namespace是什么样的。

由于下述测试涉及到用户权限问题,所以咱们新建用户egon(本人的英文名,哈哈),而且赋予该用户sudo权限

执行visudo而后新增以下内容: 
egon    ALL=(ALL)     NOPASSWD:ALL

2.1 UTS命名空间(系统调用CLONE_NEWUTS)

主要目的是独立出主机名和网络信息服务(NIS)。

文件名:uts.c

#define _GNU_SOURCE 
#include <sys/types.h>
#include <sys/wait.h>
#include <stdio.h>
#include <sched.h>
#include <signal.h>
#include <unistd.h>

/* 定义一个给 clone 用的栈,栈大小1M */
#define STACK_SIZE (1024 * 1024) 
static char container_stack[STACK_SIZE];

char* const container_args[] = {
    "/bin/bash",
    NULL
};


/* 与uts有关的代码:此处只演示主机名的隔离 */
int container_main(void* arg) 
{ 
    printf("Container - inside the container!\n"); 
    sethostname("container",10); /* 设置hostname */ 
    execv(container_args[0], container_args); 
    printf("Something's wrong!\n"); 
    return 1; 
} 
 
int main() 
{ 
    printf("Parent - start a container!\n"); 
    int container_pid = clone(container_main, container_stack+STACK_SIZE,  
            CLONE_NEWUTS | SIGCHLD, NULL); /*启用CLONE_NEWUTS Namespace隔离 */ 
    waitpid(container_pid, NULL, 0); 
    printf("Parent - container stopped!\n"); 
    return 0; 
} 

 测试开辟一个新的UTS名称空间/容器container,验证主机名的隔离性:

[egon@www ~]$ gcc -o uts uts.c #编译utc.c获得可执行文件uts
[egon@www ~]$ sudo ./uts #须要root权限才能开辟新的container
Parent - start a container!
Container - inside the container!
[root@container egon]#      #进入一个隔离的空间,即一个container
[root@container egon]# hostname #查看该空间下的主机名
container
[root@container egon]# exit #退出该container
exit
Parent - container stopped!
[egon@www ~]$ hostname  #查看最初的空间下的主机名
www.egon.org #发现确实与刚刚咱们开辟的container是不一样的主机名,验证了隔离性
[egon@www ~]$ 

2.2 IPC命名空间(系统调用CLONE_NEWIPC)

IPC全称 Inter-Process Communication,是Unix/Linux下进程间通讯的一种方式,IPC有共享内存、信号量、消息队列等方法。因此,为了隔离,咱们也须要把IPC给隔离开来,这样,只有在同一个Namespace下的进程才能相互通讯。若是你熟悉IPC的原理的话,你会知道,IPC须要有一个全局的ID,即然是全局的,那么就意味着咱们的Namespace须要对这个ID隔离,不能让别的Namespace的进程看到。

文件名:ipc.c

要启动IPC隔离,咱们只须要在调用clone时加上CLONE_NEWIPC参数就能够了(见下述代码标红的地方

#define _GNU_SOURCE 
#include <sys/types.h>
#include <sys/wait.h>
#include <stdio.h>
#include <sched.h>
#include <signal.h>
#include <unistd.h>

/* 定义一个给 clone 用的栈,栈大小1M */
#define STACK_SIZE (1024 * 1024) 
static char container_stack[STACK_SIZE];

char* const container_args[] = {
    "/bin/bash",
    NULL
};


/* 与uts有关的代码:此处只演示主机名的隔离 */
int container_main(void* arg) 
{ 
    printf("Container - inside the container!\n"); 
    sethostname("container",10); /* 设置hostname */ 
    execv(container_args[0], container_args); 
    printf("Something's wrong!\n"); 
    return 1; 
} 
 
int main() 
{ 
    printf("Parent - start a container!\n"); 
    int container_pid = clone(container_main, container_stack+STACK_SIZE,  
            CLONE_NEWUTS | CLONE_NEWIPC | SIGCHLD, NULL); /*新增CLONE_NEWIPC就能够了 */ 
    waitpid(container_pid, NULL, 0); 
    printf("Parent - container stopped!\n"); 
    return 0; 
} 

预备阶段(在全局新建IPC队列):

首先,咱们先建立一个IPC的Queue(以下所示,全局的Queue ID是0)

ipcmk建立队列

ipcrm删除队列

ipcs查看队列

[egon@www ~]$ ipcs -q #查看队列

------ Message Queues --------
key        msqid      owner      perms      used-bytes   messages    
[egon@www ~]$ ipcmk -Q #在全局建立一个ipc的队列,队列id为0
Message queue id: 0
[egon@www ~]$ ipcs -q #查看刚刚新建的全局的队列的信息

------ Message Queues --------
key        msqid      owner      perms      used-bytes   messages    
0x0c076dce 0          egon       644        0            0      

咱们暂且不运行编译的CLONE_NEWIPC的程序ipc,让咱们先运行以前编译的uts,发如今子进程中仍是能看到这个全局的IPC Queue。

[egon@www ~]$ ipcs -q #查看全局的队列

------ Message Queues --------
key        msqid      owner      perms      used-bytes   messages    
0x0c076dce 0          egon       644        0            0           

[egon@www ~]$ sudo ./uts #进入新的uts容器
Parent - start a container!
Container - inside the container!
[root@container egon]# ipcs -q #在uts容器下发现仍然能看到全局的IPC队列,证实此时没有实现IPC隔离

------ Message Queues --------
key        msqid      owner      perms      used-bytes   messages    
0x0c076dce 0          egon       644        0            0           

[root@container egon]# exit #退出uts容器
exit
Parent - container stopped!
[egon@www ~]$ 

测试开辟一个新的IPC名称空间/容器container,验证IPC的隔离性:

[egon@www ~]$ gcc -o ipc ipc.c #编译
[egon@www ~]$ ipcs -q #在全局查看ipc队列,确定能够看到

------ Message Queues --------
key        msqid      owner      perms      used-bytes   messages    
0x0c076dce 0          egon       644        0            0           

[egon@www ~]$ sudo ./ipc #进入ipc容器
Parent - start a container!
Container - inside the container!
[root@container egon]# ipcs -q #在容器内查看ipc队列,发现查看不到全局的ipc队列,本身这里的ipc队列为空,验证了ipc的隔离性
#同理若是在该容器内用ipcmk -Q建立的队列,在全局也没法看到,读者能够自行测试 ------ Message Queues -------- key msqid owner perms used-bytes messages [root@container egon]# exit exit Parent - container stopped! [egon@www ~]$

2.3 PID命名空间(系统调用CLONE_NEWPID)

空间内的PID 是独立分配的,意思就是命名空间内的虚拟 PID 可能会与命名空间外的 PID 相冲突,因而命名空间内的 PID 映射到命名空间外时会使用另一个 PID。好比说,命名空间内第一个 PID 为1,而在命名空间外就是该 PID 已被 init 进程所使用。

文件名:pid.c

基于ipc.c修改而来,见标红部分,其中只需新增CLONE_NEWPID就彻底可实现PID的隔离,而此处咱们即加了CLONE_NEWUTS又加了CLONE_NEWIPC,随后才添加了CLONE_NEWPID,表明的意思是:在UTS和IPC隔离的基础之上再进行PID的隔离,此时的容器已经愈来愈接近于在linux操做系统上新建一个隔离的操做系统了。

#define _GNU_SOURCE 
#include <sys/types.h>
#include <sys/wait.h>
#include <stdio.h>
#include <sched.h>
#include <signal.h>
#include <unistd.h>

/* 定义一个给 clone 用的栈,栈大小1M */
#define STACK_SIZE (1024 * 1024) 
static char container_stack[STACK_SIZE];

char* const container_args[] = {
    "/bin/bash",
    NULL
};


int container_main(void* arg) 
{ 
    printf("Container [%5d] - inside the container!\n",getpid()); /* 此处的getpid()是为了获取容器的初始进程(init)的pid */
    sethostname("container",10); /* 设置hostname */ 
    execv(container_args[0], container_args); 
    printf("Something's wrong!\n"); 
    return 1; 
} 
 
int main() 
{ 
    printf("Parent [%5d] - start a container!\n",getpid()); /* 此处的getpid()则是为了获取父进程的pid */ 
    int container_pid = clone(container_main, container_stack+STACK_SIZE,  
            CLONE_NEWUTS | CLONE_NEWIPC | CLONE_NEWPID | SIGCHLD, NULL); /*新增CLONE_NEWPID便可,此处表明在UTS和IPC隔离的基础之上再进行PID的隔离,其实咱们彻底能够只加CLONE_NEWPID本身:这样的话就只表明隔离PID了 */ 
    waitpid(container_pid, NULL, 0); 
    printf("Parent - container stopped!\n"); 
    return 0; 
}

 测试开辟一个新的PID名称空间/容器container,验证PID的隔离性:

[egon@www ~]$ gcc -o pid pid.c #编译
[egon@www ~]$ sudo ./pid #进入一个新的容器
Parent [ 4520] - start a container!
Container [    1] - inside the container!
[root@container egon]# echo $$ #查看该容器的初始程序(init)ID为1,而全局的init程序的ID也为1,证实了两者的隔离性
1
[root@container egon]# hostname #由于咱们在pid.c文件中加入了CLONE_NEWUTS,因此此时的主机名也是隔离的,看到的是本身的主机名
container
[root@container egon]# ipcs -q #由于咱们在pid.c文件中也加入了CLONE_NEWIPC,因此此时的IPC也是隔离的,看不到全局新建的那个IPC队列

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

 ps:centos7以后使用systemd代替init,此处咱们说的初始程序指的就是这两者,是一个意思

    说明:在传统的UNIX系统中,PID为1的进程是init,地位很是特殊。他做为全部进程的父进程,有不少特权(好比:屏蔽信号等),另外,其还会为检查全部进程的状态,咱们知道,若是某个子进程脱离了父进程(父进程没有wait它),那么init就会负责回收资源并结束这个子进程。因此,要作到进程空间的隔离,首先要建立出PID为1的进程,最好就像chroot那样,把子进程的PID在容器内变成1。

可是,咱们会发现,在子进程的shell里输入ps,top等命令,咱们仍是能够看获得全部进程。说明并无彻底隔离。这是由于,像ps, top这些命令会去读/proc文件系统,因此,由于/proc文件系统在父进程和子进程都是同样的,因此这些命令显示的东西都是同样的。

因此,咱们还须要对文件系统进行隔离,这就须要用到mount命名空间了

2.4 Mount命名空间(系统调用CLONE_NEWNS)

进程运行时能够将挂载点与系统分离,使用这个功能时,咱们能够达到 chroot 的功能,而在安全性方面比 chroot 更高。

文件名:fs.c

#define _GNU_SOURCE 
#include <sys/types.h>
#include <sys/wait.h>
#include <stdio.h>
#include <sched.h>
#include <signal.h>
#include <unistd.h>

/* 定义一个给 clone 用的栈,栈大小1M */
#define STACK_SIZE (1024 * 1024) 
static char container_stack[STACK_SIZE];

char* const container_args[] = {
    "/bin/bash",
    NULL
};

int container_main(void* arg) 
{ 
    printf("Container [%5d] - inside the container!\n", getpid()); 
    sethostname("container",10); 
    /* 从新mount proc文件系统到 /proc下 */ 
    system("mount -t proc proc /proc"); 
    execv(container_args[0], container_args); 
    printf("Something's wrong!\n"); 
    return 1; 
} 
 
int main() 
{ 
    printf("Parent [%5d] - start a container!\n", getpid()); 
    /* 启用Mount Namespace - 增长CLONE_NEWNS参数 */ 
    int container_pid = clone(container_main, container_stack+STACK_SIZE,  
            CLONE_NEWUTS | CLONE_NEWIPC | CLONE_NEWPID | CLONE_NEWNS | SIGCHLD, NULL); 
    waitpid(container_pid, NULL, 0); 
    printf("Parent - container stopped!\n"); 
    return 0; 
} 

咱们基于上次pid容器,在没有mount隔离状况下查看/proc、ps aux、top等信息

[egon@www ~]$ sudo ./pid
Parent [ 6231] - start a container!
Container [    1] - inside the container!
[root@container egon]# ls /proc/
1    116   132   148  165   18   197  213  230  248  265   282  36    5005  57    63   73   83   938        diskstats    locks         sysrq-trigger
10   117   133   149  166   180  198  214  231  249  266   283  37    51    58    64   731  84   94         dma          mdstat        sysvipc
100  118   134   15   167   181  199  215  232  25   267   284  38    514   59    640  74   841  95         driver       meminfo       timer_list
101  119   135   150  168   182  2    216  233  250  268   285  39    515   5939  641  745  85   957        execdomains  misc          timer_stats
102  12    136   151  169   183  20   217  234  251  2682  29   3944  517   60    642  75   86   96         fb           modules       tty
103  120   137   152  17    184  200  218  235  252  2684  293  3946  52    6047  643  76   863  960        filesystems  mounts        uptime
104  121   138   153  170   185  201  219  236  253  269   294  3982  520   6048  644  77   864  97         fs           mpt           version
105  122   139   154  171   186  202  22   237  254  27    295  40    53    6052  645  78   87   98         interrupts   mtrr          vmallocinfo
106  123   14    155  172   187  203  220  238  255  270   296  41    532   6053  646  780  871  99         iomem        net           vmstat
......省略n行  
[root@container egon]# ps aux
USER        PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND
root          1  0.0  0.6  44000  6548 ?        Ss   10:24   0:02 /usr/lib/systemd/systemd --switched-root --system --deserialize 21
root          2  0.0  0.0      0     0 ?        S    10:24   0:00 [kthreadd]
root          3  0.0  0.0      0     0 ?        S    10:24   0:00 [ksoftirqd/0]
root          5  0.0  0.0      0     0 ?        S<   10:24   0:00 [kworker/0:0H]
root          7  0.0  0.0      0     0 ?        S    10:24   0:00 [migration/0]
root          8  0.0  0.0      0     0 ?        S    10:24   0:00 [rcu_bh]
root          9  0.0  0.0      0     0 ?        S    10:24   0:00 [rcuob/0]
root         10  0.0  0.0      0     0 ?        S    10:24   0:00 [rcuob/1]
root         11  0.0  0.0      0     0 ?        S    10:24   0:00 [rcuob/2]
root         12  0.0  0.0      0     0 ?        S    10:24   0:00 [rcuob/3]
root         13  0.0  0.0      0     0 ?        S    10:24   0:00 [rcuob/4]
root         14  0.0  0.0      0     0 ?        S    10:24   0:00 [rcuob/5]
root         15  0.0  0.0      0     0 ?        S    10:24   0:00 [rcuob/6]
root         16  0.0  0.0      0     0 ?        S    10:24   0:00 [rcuob/7]
root         17  0.0  0.0      0     0 ?        S    10:24   0:00 [rcuob/8]
root         18  0.0  0.0      0     0 ?        S    10:24   0:00 [rcuob/9]
root         19  0.0  0.0      0     0 ?        S    10:24   0:00 [rcuob/10]
root         20  0.0  0.0      0     0 ?        S    10:24   0:00 [rcuob/11]
root         21  0.0  0.0      0     0 ?        S    10:24   0:00 [rcuob/12]
root         22  0.0  0.0      0     0 ?        S    10:24   0:00 [rcuob/13]
root         23  0.0  0.0      0     0 ?        S    10:24   0:00 [rcuob/14]
root         24  0.0  0.0      0     0 ?        S    10:24   0:00 [rcuob/15]
......省略n行

初次以外还有top命令运行的截图

测试开辟一个新的MOUNT名称空间/容器container,验证MOUNT的隔离性:

[egon@www ~]$ gcc -o fs fs.c #编译
[egon@www ~]$ sudo ./fs #进入mount容器
Parent [ 6554] - start a container!
Container [    1] - inside the container!
[root@container egon]#    #此处即是新的容器了
[root@container egon]# ls /proc/ #浏览/proc内容,发现少了好多
1          bus       crypto     execdomains  iomem     keys        loadavg  modules  pagetypeinfo  slabinfo  sysrq-trigger  uptime
13         cgroups   devices    fb           ioports   key-users   locks    mounts   partitions    softirqs  sysvipc        version
acpi       cmdline   diskstats  filesystems  irq       kmsg        mdstat   mpt      sched_debug   stat      timer_list     vmallocinfo
asound     consoles  dma        fs           kallsyms  kpagecount  meminfo  mtrr     scsi          swaps     timer_stats    vmstat
buddyinfo  cpuinfo   driver     interrupts   kcore     kpageflags  misc     net      self          sys       tty            zoneinfo
[root@container egon]# ps aux #查看进程信息发现只能两个进程:一个初始进程id为1,另一个就算ps命令自己
USER        PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND
root          1  0.0  0.2 115384  2092 pts/0    S    11:35   0:00 /bin/bash
root         14  0.0  0.1 139500  1632 pts/0    R+   11:35   0:00 ps aux

除此以外执行top命令,发现包括top命令自己,也是只要两个进程

 

须要强调的一点是:在经过CLONE_NEWNS建立mount namespace后,父进程会把本身的文件结构复制给子进程中。而子进程中新的namespace中的全部mount操做都只影响自身的文件系统,而不对外界产生任何影响。这样能够作到比较严格地隔离。

而且咱们彻底能够根据本身的须要来为容器定制mount选项。

Docker的 Mount Namespace

下面就让咱们来模拟制做一个镜像,模仿Docker的Mount Namespace

步骤一:

对于chroot来讲,chroot 目录,而后切入到目录对应的名称空间下,同理,咱们也须要为咱们的mount namespace提供一个目录(即镜像),因而咱们在/home/egon下新建目录rootfs

rootfs的目录结构参照linux根目录的结构

[root@www ~]# for i in `ls /`;do mkdir /home/egon/rootfs/$i -p;done
[root@www ~]# ls /home/egon/rootfs/
bin  boot  data  dev  etc  home  lib  lib64  media  mnt  opt  proc  root  run  sbin  srv  sys  tmp  usr  var

步骤二 :

把一些咱们须要在命名空间内使用的命令拷贝到/home/egon/rootfs/bin以及/home/egon/rootfs/usr/bin目录下,须要注意的是:/bin/sh命令必须被拷贝,且要被拷贝到/home/egon/rootfs/bin下,不然没法chroot

#新增目录
[root@www ~]# mkdir /home/egon/rootfs/usr/libexec
[root@www ~]# mkdir /home/egon/rootfs/usr/bin

#拷贝命令
[root@www ~]# cp -r /bin/*  /home/egon/rootfs/bin/
[root@www ~]# cp -r /usr/bin/*  /home/egon/rootfs/usr/bin/

#拷贝命令依赖的库,能够ldd /bin/ls来查看ls命令用来的库文件,而后定向拷贝,此处咱们就简单粗暴的使用*拷贝全部了
[root@www ~]# cp -r /lib/*  /home/egon/rootfs/lib/
[root@www ~]# cp -r /lib64/*  /home/egon/rootfs/lib64/
[root@www ~]# cp -r /usr/libexec/* /home/egon/rootfs/usr/libexec/

#拷贝命令依赖的一些配置文件
[root@www ~]# cp -r /etc/* /home/egon/rootfs/etc/

步骤三:

咱们还能够为命名空间定制一些配置文件

[root@www ~]# mkdir /home/egon/conf
[root@www ~]# echo 'egon_hostname' >> /home/egon/conf/hostname #定义hostname文件,用来挂载到命名空间中的/etc/hostname
[root@www ~]# echo '1.1.1.1 egon_hostname' >> /home/egon/conf/hosts #定义hosts文件,用来挂载到命名空间中的/etc/hosts
[root@www ~]# echo 'nameserver 202.110.110.213' >> /home/egon/conf/resolv.conf #定义resolv.conf文件,用来挂载到命名空间中的/etc/resolv.conf

同理,咱们也能够我新的命名空间定制一些目录

[root@www ~]# mkdir /tmp/t1 #本文最终会将该目录挂载到命名空间中的/mnt目录
[root@www ~]# touch /tmp/t1/egon_test.txt

步骤四:

文件名:newns.c

#define _GNU_SOURCE 
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/mount.h>
#include <stdio.h>
#include <sched.h>
#include <signal.h>
#include <unistd.h>

/* 定义一个给 clone 用的栈,栈大小1M */
#define STACK_SIZE (1024 * 1024) 
static char container_stack[STACK_SIZE];

char* const container_args[] = {
    "/bin/bash",
    "-l",
    NULL
};

int container_main(void* arg) 
{ 
    printf("Container [%5d] - inside the container!\n", getpid()); 
 
    sethostname("container",10); 
 
    /* remount "/proc" to make sure the "top" and "ps" show container's information */
    if (mount("proc", "rootfs/proc", "proc", 0, NULL) !=0 ) { 
        perror("proc"); 
    } 
    if (mount("sysfs", "rootfs/sys", "sysfs", 0, NULL)!=0) { 
        perror("sys"); 
    } 
    if (mount("none", "rootfs/tmp", "tmpfs", 0, NULL)!=0) { 
        perror("tmp"); 
    } 
    if (mount("udev", "rootfs/dev", "devtmpfs", 0, NULL)!=0) { 
        perror("dev"); 
    } 
    if (mount("devpts", "rootfs/dev/pts", "devpts", 0, NULL)!=0) { 
        perror("dev/pts"); 
    } 
    if (mount("shm", "rootfs/dev/shm", "tmpfs", 0, NULL)!=0) { 
        perror("dev/shm"); 
    } 
    if (mount("tmpfs", "rootfs/run", "tmpfs", 0, NULL)!=0) { 
        perror("run"); 
    } 
    /*  
     * 模仿Docker的从外向容器里mount相关的配置文件  
     * 你能够查看:/var/lib/docker/containers/<container_id>/目录, 
     * 你会看到docker的这些文件的。 
     */ 
    if (mount("conf/hosts", "rootfs/etc/hosts", "none", MS_BIND, NULL)!=0 || 
          mount("conf/hostname", "rootfs/etc/hostname", "none", MS_BIND, NULL)!=0 || 
          mount("conf/resolv.conf", "rootfs/etc/resolv.conf", "none", MS_BIND, NULL)!=0 ) { 
        perror("conf"); 
    } 
    /* 模仿docker run命令中的 -v, --volume=[] 参数干的事 */ 
    if (mount("/tmp/t1", "rootfs/mnt", "none", MS_BIND, NULL)!=0) { 
        perror("mnt"); 
    } 
 
    /* chroot 隔离目录 */
    if ( chdir("./rootfs") != 0 || chroot("./") != 0 ){ 
        perror("chdir/chroot"); 
    }
 
    execv(container_args[0], container_args); 
    perror("exec1111"); 
    printf("Something's wrong!\n"); 
    return 1; 
} 
 
int main() 
{ 
    printf("Parent [%5d] - start a container!\n", getpid()); 
    int container_pid = clone(container_main, container_stack+STACK_SIZE,  
            CLONE_NEWUTS | CLONE_NEWIPC | CLONE_NEWPID | CLONE_NEWNS | SIGCHLD, NULL); 
    waitpid(container_pid, NULL, 0); 
    printf("Parent - container stopped!\n"); 
    return 0; 
} 

步骤五:

[egon@www ~]$ gcc -o newns newns.c
[egon@www ~]$ sudo ./newns              #进行新的命名空间
Parent [ 2848] - start a container!
Container [    1] - inside the container!   #基于以前所作,咱们已然实现pid隔离
bash-4.2#                                             #chroot进了一个新的命名空间
bash-4.2# pwd                                      #chroot ./rootfs的效果
/
bash-4.2# hostname                             #查看主机名发现实现了主机名隔离
container
bash-4.2# ipcs -q                                  #ipc一样也是隔离的

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

bash-4.2# ps aux
USER        PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND
root          1  0.0  0.1  11768  1860 pts/0    S    20:55   0:00 /bin/bash -l
root         28  0.0  0.1  35884  1480 pts/0    R+   20:57   0:00 ps aux
bash-4.2# 
bash-4.2# 
bash-4.2# 
bash-4.2# 
bash-4.2# 
bash-4.2# 
bash-4.2# mount
proc on /proc type proc (rw,relatime)
sysfs on /sys type sysfs (rw,relatime,seclabel)
none on /tmp type tmpfs (rw,relatime,seclabel)
udev on /dev type devtmpfs (rw,relatime,seclabel,size=490432k,nr_inodes=122608,mode=755)
devpts on /dev/pts type devpts (rw,relatime,seclabel,mode=600,ptmxmode=000)
shm on /dev/shm type tmpfs (rw,relatime,seclabel)
tmpfs on /run type tmpfs (rw,relatime,seclabel)
/dev/mapper/centos-root on /etc/hosts type xfs (rw,relatime,seclabel,attr2,inode64,noquota)
/dev/mapper/centos-root on /etc/hostname type xfs (rw,relatime,seclabel,attr2,inode64,noquota)
/dev/mapper/centos-root on /etc/resolv.conf type xfs (rw,relatime,seclabel,attr2,inode64,noquota)
/dev/mapper/centos-root on /mnt type xfs (rw,relatime,seclabel,attr2,inode64,noquota)
proc on /proc type proc (rw,relatime)
none on /tmp type tmpfs (rw,relatime,seclabel)
shm on /dev/shm type tmpfs (rw,relatime,seclabel)
tmpfs on /run type tmpfs (rw,relatime,seclabel)
/dev/mapper/centos-root on /etc/hosts type xfs (rw,relatime,seclabel,attr2,inode64,noquota)
/dev/mapper/centos-root on /etc/hostname type xfs (rw,relatime,seclabel,attr2,inode64,noquota)
/dev/mapper/centos-root on /etc/resolv.conf type xfs (rw,relatime,seclabel,attr2,inode64,noquota)
/dev/mapper/centos-root on /mnt type xfs (rw,relatime,seclabel,attr2,inode64,noquota)
bash-4.2# cat /etc/hostname #验证步骤三所述
testhostname
bash-4.2# cat /etc/hosts    #同上
123
bash-4.2# cat /etc/resolv.conf #同上
123
bash-4.2# ls /mnt/             #同上
egon_test.txt

 

 

 

 

 

2.5 Network命名空间

用于隔离网络资源(/proc/net、IP 地址、网卡、路由等)。后台进程能够运行在不一样命名空间内的相同端口上,用户还能够虚拟出一块网卡。

每一个网络命名空间都有本身的路由表,它本身的iptables设置提供nat和过滤。Linux网络命名空间还提供了在网络命名空间内运行进程的功能。

2.6 User命名空间

同进程 ID 同样,用户 ID 和组 ID 在命名空间内外是不同的,而且在不一样命名空间内能够存在相同的 ID。

 

 

 

 

 

 

 

 

 

 

 

 

参考连接:

https://lwn.net/Articles/531114/

http://www.opencloudblog.com/?p=42

http://os.51cto.com/art/201609/517640.htm

http://os.51cto.com/art/201609/517641.htm

相关文章
相关标签/搜索