天天看點

為什麼建構容器需要 Namespace

Namespace 概念

Namespace 是 Linux 核心的一個特性,該特性可以實作在同一主機系統中,對程序 ID、主機名、使用者 ID、檔案名、網絡和程序間通信等資源的隔離。Docker 利用 Linux 核心的 Namespace 特性,實作了每個容器的資源互相隔離,進而保證容器内部隻能通路到自己 Namespace 的資源。

目前 Linux 核心中提供了 8 種類型的 Namespace:

為什麼建構容器需要 Namespace

各種 Namespace 的作用

Mount Namespace

Mount Namespace 是 Linux 核心實作的第一個 Namespace,從核心 2.4.19 版本開始加入。它可以用來隔離不同的程序或者程序組看到的挂載點。通俗地說,就是可以實作在不同的程序中看到不同的挂載目錄。使用 Mount Namespace 可以實作容器内隻能看到自己的挂載資訊,在容器内的挂載操作不會影響主機的挂載目錄。

下面我們通過一個執行個體來示範下 Mount Namespace。在示範之前,我們先來認識一個指令行工具 unshare。unshare 是 util-linux 工具包中的一個工具,CentOS 7 系統預設已經內建了該工具,使用 unshare 指令可以實作建立并通路不同類型的 Namespace。

首先我們使用以下指令建立一個 bash 程序并且建立一個 Mount Namespace:

#建立一個 Mount Namespace
root@docker-demo ~]# unshare --mount --fork /bin/bash      

執行完上述指令後,這時我們已經在主機上建立了一個新的 Mount Namespace,并且目前指令行視窗加入了新建立的 Mount Namespace。下面我通過一個例子來驗證下,在獨立的 Mount Namespace 内建立挂載目錄是不影響主機的挂載目錄的。

然後在 /tmp 目錄下建立一個目錄,建立好目錄後使用 mount 指令挂載一個 tmpfs 類型的目錄:

[root@docker-demo ~]# mkdir /tmp/tmpfs
[root@docker-demo ~]# mount -t tmpfs -o size=20m tmpfs /tmp/tmpfs      

然後使用 df 指令檢視一下已挂載的目錄資訊:

[root@docker-demo ~]# df -h
檔案系統                 容量  已用  可用 已用% 挂載點
/dev/mapper/centos-root   50G   15G   36G   29% /
devtmpfs                 3.9G     0  3.9G    0% /dev
tmpfs                    3.9G     0  3.9G    0% /dev/shm
tmpfs                    3.9G     0  3.9G    0% /sys/fs/cgroup
tmpfs                    3.9G  8.9M  3.9G    1% /run
tmpfs                    797M  8.0K  797M    1% /run/user/42
tmpfs                    797M     0  797M    0% /run/user/0
/dev/sda1               1014M  222M  793M   22% /boot
/dev/mapper/centos-home  142G   40M  142G    1% /home
tmpfs                     20M     0   20M    0% /tmp/tmpfs  #剛剛挂載的目錄      

可以看到 /tmp/tmpfs 目錄已經被正确挂載。為了驗證主機上并沒有挂載此目錄,我們新打開一個指令行視窗,同樣執行 df 指令檢視主機的挂載資訊:

root@docker-demo ~]# df -hT
檔案系統                類型      容量  已用  可用 已用% 挂載點
devtmpfs                devtmpfs  3.9G     0  3.9G    0% /dev
tmpfs                   tmpfs     3.9G     0  3.9G    0% /dev/shm
tmpfs                   tmpfs     3.9G  9.0M  3.9G    1% /run
tmpfs                   tmpfs     3.9G     0  3.9G    0% /sys/fs/cgroup
/dev/mapper/centos-root xfs        50G   15G   36G   29% /
/dev/sda1               xfs      1014M  222M  793M   22% /boot
/dev/mapper/centos-home xfs       142G   40M  142G    1% /home
tmpfs                   tmpfs     797M  8.0K  797M    1% /run/user/42
tmpfs                   tmpfs     797M     0  797M    0% /run/user/0      

通過上面輸出可以看到主機上并沒有挂載 /tmp/tmpfs,可見我們獨立的 Mount Namespace 中執行 mount 操作并不會影響主機。

為了進一步驗證我們的想法,我們繼續在目前指令行視窗檢視一下目前程序的 Namespace 資訊,指令如下:

[root@docker-demo ~]# ls -l /proc/self/ns/
總用量 0
lrwxrwxrwx. 1 root root 0 3月  21 19:58 cgroup -> cgroup:[4026531835]
lrwxrwxrwx. 1 root root 0 3月  21 19:58 ipc -> ipc:[4026531839]
lrwxrwxrwx. 1 root root 0 3月  21 19:58 mnt -> mnt:[4026532670]
lrwxrwxrwx. 1 root root 0 3月  21 19:58 net -> net:[4026531992]
lrwxrwxrwx. 1 root root 0 3月  21 19:58 pid -> pid:[4026531836]
lrwxrwxrwx. 1 root root 0 3月  21 19:58 pid_for_children -> pid:[4026531836]
lrwxrwxrwx. 1 root root 0 3月  21 19:58 time -> time:[4026531834]
lrwxrwxrwx. 1 root root 0 3月  21 19:58 time_for_children -> time:[4026531834]
lrwxrwxrwx. 1 root root 0 3月  21 19:58 user -> user:[4026531837]
lrwxrwxrwx. 1 root root 0 3月  21 19:58 uts -> uts:[4026531838]      

然後新打開一個指令行視窗,使用相同的指令檢視一下主機上的 Namespace 資訊:

[root@docker-demo ~]#  ls -l /proc/self/ns/
總用量 0
lrwxrwxrwx. 1 root root 0 3月  21 19:58 cgroup -> cgroup:[4026531835]
lrwxrwxrwx. 1 root root 0 3月  21 19:58 ipc -> ipc:[4026531839]
lrwxrwxrwx. 1 root root 0 3月  21 19:58 mnt -> mnt:[4026531840]
lrwxrwxrwx. 1 root root 0 3月  21 19:58 net -> net:[4026531992]
lrwxrwxrwx. 1 root root 0 3月  21 19:58 pid -> pid:[4026531836]
lrwxrwxrwx. 1 root root 0 3月  21 19:58 pid_for_children -> pid:[4026531836]
lrwxrwxrwx. 1 root root 0 3月  21 19:58 time -> time:[4026531834]
lrwxrwxrwx. 1 root root 0 3月  21 19:58 time_for_children -> time:[4026531834]
lrwxrwxrwx. 1 root root 0 3月  21 19:58 user -> user:[4026531837]
lrwxrwxrwx. 1 root root 0 3月  21 19:58 uts -> uts:[4026531838]      

通過對比兩次指令的輸出結果,我們可以看到,除了 Mount Namespace 的 ID 值不一樣外,其他Namespace 的 ID 值均一緻。

通過以上結果我們可以得出結論,使用 unshare 指令可以建立 Mount Namespace,并且在建立的 Mount Namespace 内 mount 是和外部完全隔離的。

PID Namespace

PID Namespace 的作用是用來隔離程序。在不同的 PID Namespace 中,程序可以擁有相同的 PID 号,利用 PID Namespace 可以實作每個容器的主程序為 1 号程序,而容器内的程序在主機上卻擁有不同的PID。例如一個程序在主機上 PID 為 122,使用 PID Namespace 可以實作該程序在容器内看到的 PID 為 1。

下面我們通過一個執行個體來示範下 PID Namespace的作用。首先我們使用以下指令建立一個 bash 程序,并且建立一個 PID Namespace:

[root@docker-demo ~]# unshare --pid --fork --mount-proc /bin/bash
[root@docker-demo ~]#      

執行完上述指令後,我們在主機上建立了一個新的 PID Namespace,并且目前指令行視窗加入了新建立的 PID Namespace。在目前的指令行視窗使用 ps aux 指令檢視一下程序資訊:

[root@docker-demo ~]# ps aux
USER       PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND
root         1  1.7  0.0 116752  4884 pts/0    S    19:59   0:00 /bin/bash
root        31  7.0  0.0 151124  3872 pts/0    R+   19:59   0:00 ps aux      

通過上述指令輸出結果可以看到目前 Namespace 下 bash 為 1 号程序,而且我們也看不到主機上的其他程序資訊。

UTS Namespace

UTS Namespace 主要是用來隔離主機名的,它允許每個 UTS Namespace 擁有一個獨立的主機名。例如我們的主機名稱為 docker,使用 UTS Namespace 可以實作在容器内的主機名稱為 lagoudocker 或者其他任意自定義主機名。

同樣我們通過一個執行個體來驗證下 UTS Namespace 的作用,首先我們使用 unshare 指令來建立一個 UTS Namespace:

[root@docker-demo ~]# unshare --uts --fork /bin/bash
[root@docker-demo ~]#      

建立好 UTS Namespace 後,目前指令行視窗已經處于一個獨立的 UTS Namespace 中,下面我們使用 hostname 指令(hostname 可以用來檢視主機名稱)設定一下主機名:

[root@docker-demo ~]# hostname -b lagoudocker
#檢視更改後的主機名
[root@docker-demo ~]# hostname
lagoudocker      

通過上面指令的輸出,我們可以看到目前UTS Namespace 内的主機名已經被修改為 lagoudocker。然後我們新打開一個指令行視窗,使用相同的指令檢視一下主機的 hostname:

[root@docker-demo ~]# hostname
docker-demo      

可以看到主機的名稱仍然為 docker-demo,并沒有被修改。由此,可以驗證 UTS Namespace 可以用來隔離主機名。

IPC Namespace

IPC Namespace 主要是用來隔離程序間通信的。例如 PID Namespace 和 IPC Namespace 一起使用可以實作同一 IPC Namespace 内的程序彼此可以通信,不同 IPC Namespace 的程序卻不能通信。

同樣我們通過一個執行個體來驗證下IPC Namespace的作用,首先我們使用 unshare 指令來建立一個 IPC Namespace:

[root@docker-demo ~]# unshare --ipc --fork /bin/bash
[root@docker-demo ~]#      

下面我們需要借助兩個指令來實作對 IPC Namespace 的驗證。

  • ipcs -q 指令:用來檢視系統間通信隊列清單。
  • ipcmk -Q 指令:用來建立系統間通信隊列。

我們首先使用 ipcs -q 指令檢視一下目前 IPC Namespace 下的系統通信隊列清單:

[root@docker-demo ~]# ipcs -q
--------- 消息隊列 -----------
鍵        msqid      擁有者  權限     已用位元組數 消息      

由上可以看到目前無任何系統通信隊列,然後我們使用 ipcmk -Q 指令建立一個系統通信隊列:

[root@docker-demo ~]# ipcmk -Q
消息隊列 id:0      

再次使用 ipcs -q 指令檢視目前 IPC Namespace 下的系統通信隊列清單:

[root@docker-demo ~]# ipcs -q
--------- 消息隊列 -----------
鍵        msqid      擁有者  權限     已用位元組數 消息
0x91ce1a3d 0          root       644        0            0      

可以看到我們已經成功建立了一個系統通信隊列。然後我們新打開一個指令行視窗,使用ipcs -q 指令檢視一下主機的系統通信隊列:

[root@docker-demo ~]# ipcs -q
--------- 消息隊列 -----------
鍵        msqid      擁有者  權限     已用位元組數 消息      

通過上面的實驗,可以發現,在單獨的 IPC Namespace 内建立的系統通信隊列在主機上無法看到。即 IPC Namespace 實作了系統通信隊列的隔離。

User Namespace

User Namespace 主要是用來隔離使用者和使用者組的。一個比較典型的應用場景就是在主機上以非 root 使用者運作的程序可以在一個單獨的 User Namespace 中映射成 root 使用者。使用 User Namespace 可以實作程序在容器内擁有 root 權限,而在主機上卻隻是普通使用者。User Namesapce 的建立是可以不使用 root 權限的,下面我們以普通使用者的身份建立一個 User Namespace,指令如下:

# 建立并切換到普通使用者
[root@docker-demo ~]# su - chengzw
[chengzw@docker-demo ~]$ id
uid=1002(chengzw) gid=1004(chengzw) groups=1004(chengzw) context=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023
# 建立 User Namespace
[chengzw@docker-demo ~]$ unshare --user -r /bin/bash      

然後執行 id 指令檢視一下目前的使用者資訊:

[root@docker-demo ~]# id
uid=0(root) gid=0(root) 組=0(root) 環境=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023      

通過上面的輸出可以看到我們在新的 User Namespace 内已經是 root 使用者了。下面我們使用隻有主機 root 使用者才可以執行的 reboot 指令來驗證一下,在目前指令行視窗執行 reboot 指令:

[root@docker-demo ~]# reboot
Failed to open /dev/initctl: Permission denied
Failed to talk to init daemon.      

檢視目前 User Namespace 所在的程序:

[root@docker-demo ~]# echo $$
2157      

在主控端上檢視該程序對應的使用者,可以看到實際上在主控端上是 chengzw 使用者:

[root@docker-demo ~]# ps -ef  | grep 2157
chengzw   2157  2092  0 20:41 pts/0    00:00:00 /bin/bash      

可以看到,我們在新建立的 User Namespace 内雖然是 root 使用者,但是并沒有權限執行 reboot 指令。這說明在隔離的 User Namespace 中,并不能擷取到主機的 root 權限,也就是說 User Namespace 實作了使用者和使用者組的隔離。

Net Namespace

Net Namespace 是用來隔離網絡裝置、IP 位址和端口等資訊的。Net Namespace 可以讓每個程序擁有自己獨立的 IP 位址,端口和網卡資訊。例如主機 IP 位址為 192.168.1.41 ,容器内可以設定獨立的 IP 位址為 172.16.1.2。

同樣用執行個體驗證,我們首先使用 ip addr 指令檢視一下主機上的網絡資訊:

[root@docker-demo ~]$ ip addr
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
    inet6 ::1/128 scope host
       valid_lft forever preferred_lft forever
2: ens192: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc mq state UP qlen 1000
    link/ether 00:50:56:9b:2d:90 brd ff:ff:ff:ff:ff:ff
    inet 192.168.1.41/24 brd 192.168.1.255 scope global ens192
       valid_lft forever preferred_lft forever
    inet6 fe80::2556:f369:b4e7:fb64/64 scope link tentative dadfailed
       valid_lft forever preferred_lft forever
    inet6 fe80::6b8d:29f7:a5fe:dbee/64 scope link tentative dadfailed
       valid_lft forever preferred_lft forever
    inet6 fe80::f387:57a3:4975:d8f2/64 scope link tentative dadfailed
       valid_lft forever preferred_lft forever      

然後我們使用以下指令建立一個 Net Namespace:

[root@docker-demo ~]# unshare --net --fork /bin/bash      

同樣的我們使用 ip addr 指令檢視一下網絡資訊:

[root@docker-demo ~]# ip addr
1: lo: <LOOPBACK> mtu 65536 qdisc noop state DOWN qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00      

可以看到,主控端上有 lo、ens192 等網絡裝置,而我們建立的 Net Namespace 内則與主機上的網絡裝置不同。我們可以在 Net Namespace 中新增網卡并配置 IP:

[root@docker-demo ~]# ip link add eth0 type dummy
[root@docker-demo ~]# ip addr add 172.16.1.2/24 dev 
[root@docker-demo ~]# ip addr
1: lo: <LOOPBACK> mtu 65536 qdisc noop state DOWN qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
2: eth0: <BROADCAST,NOARP,UP,LOWER_UP> mtu 1500 qdisc noqueue state UNKNOWN qlen 1000
    link/ether e6:b7:92:d4:cc:ae brd ff:ff:ff:ff:ff:ff
    inet 172.16.1.2/24 scope global eth0
       valid_lft forever preferred_lft forever
    inet6 fe80::e4b7:92ff:fed4:ccae/64 scope link
       valid_lft forever preferred_lft forever      

Time Namespace

首先檢視主控端的時間,目前是3月21号:

[root@docker-demo ~]# date
2021年 03月 21日 星期日 21:02:38 CST      

啟動一個 centos 容器,檢視時間和主控端一緻(UTC + 8 = CST):

[root@docker-demo ~]# docker exec -it centos bash
[root@a695de10917d /]# date
Sun Mar 21 13:02:46 UTC 2021      

嘗試在容器中修改時間,提示權限不夠:

[root@a695de10917d /]# date -s 05/28
date: cannot set date: Operation not permitted      

在啟動容器的時候,加上

--cap-add SYS_TIME

參數,在容器内将時間修改成3月20号:

root@docker-demo ~]# docker run -it --cap-add SYS_TIME --name centos centos /bin/bash
[root@e7b61aba6222 /]# date -s 03/20
Sat Mar 20 00:00:00 UTC 2021
[root@e7b61aba6222 /]# date
Sat Mar 20 00:00:01 UTC 2021
# 退出容器
[root@e7b61aba6222 /]# exit
exit      

在主控端上檢視時間發現也改成3月20号了:

[root@docker-demo ~]# date
2021年 03月 20日 星期六 08:00:07 CST      

--cap-add SYS_TIME

是為了将主控端的核心時間挂載進來與容器共享,是以容器時間更改了,主控端時間也會跟着更改。

為什麼 Docker 需要 Namespace?

Linux 核心從 2002 年 2.4.19 版本開始加入了 Mount Namespace,而直到核心 3.8 版本加入了 User Namespace 才為容器提供了足夠的支援功能。

當 Docker 建立一個容器時, 它會建立這六種 Namespace,然後将容器中的程序加入這些 Namespace 之中,使得 Docker 容器中的程序隻能看到目前 Namespace 中的系統資源。

正是由于 Docker 使用了 Linux 的這些 Namespace 技術,才實作了 Docker 容器的隔離,可以說沒有 Namespace,就沒有 Docker 容器。

Docker exec 原理

在了解了 Linux Namespace 的隔離機制後,你應該會很自然地想到一個問題:docker exec 是怎麼做到進入容器裡的呢?

實際上,Linux Namespace 建立的隔離空間雖然看不見摸不着,但一個程序的 Namespace 資訊在主控端上是确确實實存在的,并且是以一個檔案的方式存在。

首先建立一個容器:

$ docker run -p 4000:80 helloworld python app.py      

通過如下指令,你可以看到目前正在運作的 Docker 容器的程序号(PID)是 25686:

$ docker inspect --format '{{ .State.Pid }}' 4ddf4638572d25686      

這時,你可以通過檢視主控端的 proc 檔案,看到這個 25686 程序的所有 Namespace 對應的檔案:

$ ls -l  /proc/25686/ns
total 0
lrwxrwxrwx 1 root root 0 Aug 13 14:05 cgroup -> cgroup:[4026531835]
lrwxrwxrwx 1 root root 0 Aug 13 14:05 ipc -> ipc:[4026532278]
lrwxrwxrwx 1 root root 0 Aug 13 14:05 mnt -> mnt:[4026532276]
lrwxrwxrwx 1 root root 0 Aug 13 14:05 net -> net:[4026532281]
lrwxrwxrwx 1 root root 0 Aug 13 14:05 pid -> pid:[4026532279]
lrwxrwxrwx 1 root root 0 Aug 13 14:05 pid_for_children -> pid:[4026532279]
lrwxrwxrwx 1 root root 0 Aug 13 14:05 user -> user:[4026531837]
lrwxrwxrwx 1 root root 0 Aug 13 14:05 uts -> uts:[4026532277]      

可以看到,一個程序的每種 Linux Namespace,都在它對應的 /proc/[程序号]/ns 下有一個對應的虛拟檔案,并且連結到一個真實的 Namespace 檔案上。

有了這樣一個可以“hold 住”所有 Linux Namespace 的檔案,我們就可以對 Namespace 做一些很有意義事情了,比如:加入到一個已經存在的 Namespace 當中。

這也就意味着:一個程序,可以選擇加入到某個程序已有的 Namespace 當中,進而達到“進入”這個程序所在容器的目的,這正是 docker exec 的實作原理。

而這個操作所依賴的,乃是一個名叫 setns() 的 Linux 系統調用。它的調用方法,我可以用如下一段小程式為你說明:

#define _GNU_SOURCE
#include <fcntl.h>
#include <sched.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#define errExit(msg) do { perror(msg); exit(EXIT_FAILURE);} while (0)
int main(int argc, char *argv[]) {
    int fd;
    
    fd = open(argv[1], O_RDONLY);
    if (setns(fd, 0) == -1) {
        errExit("setns");
    }
    execvp(argv[2], &argv[2]); 
    errExit("execvp");
}      

這段代碼功能非常簡單:它一共接收兩個參數,第一個參數是 argv[1],即目前程序要加入的 Namespace 檔案的路徑,比如 /proc/25686/ns/net;而第二個參數,則是你要在這個 Namespace 裡運作的程序,比如 /bin/bash。

這段代碼的核心操作,則是通過 open() 系統調用打開了指定的 Namespace 檔案,并把這個檔案的描述符 fd 交給 setns() 使用。在 setns() 執行後,目前程序就加入了這個檔案對應的 Linux Namespace 當中了。

現在,你可以編譯執行一下這個程式,加入到容器程序(PID=25686)的 Network Namespace 中:

$ gcc -o set_ns set_ns.c 
$ ./set_ns /proc/25686/ns/net /bin/bash 
$ ifconfig
eth0      Link encap:Ethernet  HWaddr 02:42:ac:11:00:02  
          inet addr:172.17.0.2  Bcast:0.0.0.0  Mask:255.255.0.0
          inet6 addr: fe80::42:acff:fe11:2/64 Scope:Link
          UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
          RX packets:12 errors:0 dropped:0 overruns:0 frame:0
          TX packets:10 errors:0 dropped:0 overruns:0 carrier:0
     collisions:0 txqueuelen:0 
          RX bytes:976 (976.0 B)  TX bytes:796 (796.0 B)
lo        Link encap:Local Loopback  
          inet addr:127.0.0.1  Mask:255.0.0.0
          inet6 addr: ::1/128 Scope:Host
          UP LOOPBACK RUNNING  MTU:65536  Metric:1
          RX packets:0 errors:0 dropped:0 overruns:0 frame:0
          TX packets:0 errors:0 dropped:0 overruns:0 carrier:0
    collisions:0 txqueuelen:1000 
          RX bytes:0 (0.0 B)  TX bytes:0 (0.0 B)      

正如上所示,當我們執行 ifconfig 指令檢視網絡裝置時,我會發現能看到的網卡“變少”了:隻有兩個。而我的主控端則至少有四個網卡。這是怎麼回事呢?

實際上,在 setns() 之後我看到的這兩個網卡,正是我在前面啟動的 Docker 容器裡的網卡。也就是說,我新建立的這個 /bin/bash 程序,由于加入了該容器程序(PID=25686)的 Network Namepace,它看到的網絡裝置與這個容器裡是一樣的,即:/bin/bash 程序的網絡裝置視圖,也被修改了。

而一旦一個程序加入到了另一個 Namespace 當中,在主控端的 Namespace 檔案上,也會有所展現。

在主控端上,你可以用 ps 指令找到這個 set_ns 程式執行的 /bin/bash 程序,其真實的 PID 是 28499:

# 在主控端上
ps aux | grep /bin/bash
root     28499  0.0  0.0 19944  3612 pts/0    S    14:15   0:00 /bin/bash      

這時,如果檢視一下這個 PID=28499 的程序的 Namespace,你就會發現這樣一個事實:

$ ls -l /proc/28499/ns/net
lrwxrwxrwx 1 root root 0 Aug 13 14:18 /proc/28499/ns/net -> net:[4026532281]
$ ls -l  /proc/25686/ns/net
lrwxrwxrwx 1 root root 0 Aug 13 14:05 /proc/25686/ns/net -> net:[4026532281]      

在 /proc/[PID]/ns/net 目錄下,這個 PID=28499 程序,與我們前面的 Docker 容器程序(PID=25686)指向的 Network Namespace 檔案完全一樣。這說明這兩個程序,共享了這個名叫 net:[4026532281]的 Network Namespace。

此外,Docker 還專門提供了一個參數,可以讓你啟動一個容器并“加入”到另一個容器的 Network Namespace 裡,這個參數就是 -net,比如:

$ docker run -it --net container:4ddf4638572d busybox ifconfig      

這樣,我們新啟動的這個容器,就會直接加入到 ID=4ddf4638572d 的容器,也就是我們前面的建立的應用容器(PID=25686)的 Network Namespace 中。是以,這裡 ifconfig 傳回的網卡資訊,跟我前面那個小程式傳回的結果一模一樣,你也可以嘗試一下。

而如果我指定–net=host,就意味着這個容器不會為程序啟用 Network Namespace。這就意味着,這個容器拆除了 Network Namespace 的“隔離牆”,是以,它會和主控端上的其他普通程序一樣,直接共享主控端的網絡棧。這就為容器直接操作和使用主控端網絡提供了一個管道。