網絡虛擬化基礎一: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

相關文章
相關標籤/搜索