一 介紹
如果把linux操作系統比作一個大房子,那命名空間指的就是這個房子中的一個個房間,住在每個房間里的人都自以為獨享了整個房子的資源,但其實大家僅僅只是在共享的基礎之上互相隔離,共享指的是共享全局的資源,而隔離指的是局部上彼此保持隔離,因而命名空間的本質就是指:一種在空間上隔離的概念,當下盛行的許多容器虛擬化技術(典型代表如LXC、Docker)就是基於linux命名空間的概念而來的。
一方面:如果我們要深入研究docker技術,linux namespace是必須掌握的基礎知識。
另一方面:Neutron也使用Linux命名空間(Network Namespace),這是理解openstack網絡機制的根本。

Linux Namespace是Linux提供的一種內核級別環境隔離的方法,關於隔離的概念其實大家早已接觸過:比如在光盤修復模式下,可以用chroot切換到其他的文件系統,chroot提供了一種簡單的隔離模式:chroot內部的文件系統無法訪問外部的內容。Linux Namespace在此基礎上又提供了很多其他隔離機制。
當前,Linux 支持6種不同類型的命名空間。它們的出現,使用戶創建的進程能夠與系統分離得更加徹底,從而不需要使用更多的底層虛擬化技術。詳細請點擊

二 Linux Namespaces深入分析
主要是三個系統調用
- clone() – 實現線程的系統調用,用來創建一個新的進程,並可以通過設計上述參數達到隔離。
- unshare() – 使某進程脫離某個namespace
- setns() – 把某進程加入到某個namespace
首先,我們來看一下一個最簡單的clone()系統調用的示例,(后面,我們的程序都會基於這個程序做修改):
文件名:clone.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 - 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基本上是一樣的玩法。但是,對於上面的程序,父子進程的進程空間是沒有什么差別的,父進程能訪問到的子進程也能。
下面, 讓我們來看幾個例子看看,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
