溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點(diǎn)擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

Namespace的作用是什么

發(fā)布時(shí)間:2021-12-16 10:19:36 來源:億速云 閱讀:571 作者:iii 欄目:云計(jì)算

這篇文章主要介紹“Namespace的作用是什么”,在日常操作中,相信很多人在Namespace的作用是什么問題上存在疑惑,小編查閱了各式資料,整理出簡單好用的操作方法,希望對大家解答”Namespace的作用是什么”的疑惑有所幫助!接下來,請跟著小編一起來學(xué)習(xí)吧!

Namespace 概念

Namespace 是 Linux 內(nèi)核的一個(gè)特性,該特性可以實(shí)現(xiàn)在同一主機(jī)系統(tǒng)中,對進(jìn)程 ID、主機(jī)名、用戶 ID、文件名、網(wǎng)絡(luò)和進(jìn)程間通信等資源的隔離。Docker 利用 Linux 內(nèi)核的 Namespace 特性,實(shí)現(xiàn)了每個(gè)容器的資源相互隔離,從而保證容器內(nèi)部只能訪問到自己 Namespace 的資源。

目前 Linux 內(nèi)核中提供了 8 種類型的 Namespace: Namespace的作用是什么

各種 Namespace 的作用

Mount Namespace

Mount Namespace 是 Linux 內(nèi)核實(shí)現(xiàn)的第一個(gè) Namespace,從內(nèi)核 2.4.19 版本開始加入。它可以用來隔離不同的進(jìn)程或者進(jìn)程組看到的掛載點(diǎn)。通俗地說,就是可以實(shí)現(xiàn)在不同的進(jìn)程中看到不同的掛載目錄。使用 Mount Namespace 可以實(shí)現(xiàn)容器內(nèi)只能看到自己的掛載信息,在容器內(nèi)的掛載操作不會(huì)影響主機(jī)的掛載目錄。

下面我們通過一個(gè)實(shí)例來演示下 Mount Namespace。在演示之前,我們先來認(rèn)識一個(gè)命令行工具 unshare。unshare 是 util-linux 工具包中的一個(gè)工具,CentOS 7 系統(tǒng)默認(rèn)已經(jīng)集成了該工具,使用 unshare 命令可以實(shí)現(xiàn)創(chuàng)建并訪問不同類型的 Namespace。

首先我們使用以下命令創(chuàng)建一個(gè) bash 進(jìn)程并且新建一個(gè) Mount Namespace:

#新建一個(gè) Mount Namespace
root@docker-demo ~]# unshare --mount --fork /bin/bash

執(zhí)行完上述命令后,這時(shí)我們已經(jīng)在主機(jī)上創(chuàng)建了一個(gè)新的 Mount Namespace,并且當(dāng)前命令行窗口加入了新創(chuàng)建的 Mount Namespace。下面我通過一個(gè)例子來驗(yàn)證下,在獨(dú)立的 Mount Namespace 內(nèi)創(chuàng)建掛載目錄是不影響主機(jī)的掛載目錄的。

然后在 /tmp 目錄下創(chuàng)建一個(gè)目錄,創(chuàng)建好目錄后使用 mount 命令掛載一個(gè) 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
文件系統(tǒng)                 容量  已用  可用 已用% 掛載點(diǎn)
/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 目錄已經(jīng)被正確掛載。為了驗(yàn)證主機(jī)上并沒有掛載此目錄,我們新打開一個(gè)命令行窗口,同樣執(zhí)行 df 命令查看主機(jī)的掛載信息:

root@docker-demo ~]# df -hT
文件系統(tǒng)                類型      容量  已用  可用 已用% 掛載點(diǎn)
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

通過上面輸出可以看到主機(jī)上并沒有掛載 /tmp/tmpfs,可見我們獨(dú)立的 Mount Namespace 中執(zhí)行 mount 操作并不會(huì)影響主機(jī)。

為了進(jìn)一步驗(yàn)證我們的想法,我們繼續(xù)在當(dāng)前命令行窗口查看一下當(dāng)前進(jìn)程的 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]

然后新打開一個(gè)命令行窗口,使用相同的命令查看一下主機(jī)上的 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]

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

通過以上結(jié)果我們可以得出結(jié)論,使用 unshare 命令可以新建 Mount Namespace,并且在新建的 Mount Namespace 內(nèi) mount 是和外部完全隔離的。

PID Namespace

PID Namespace 的作用是用來隔離進(jìn)程。在不同的 PID Namespace 中,進(jìn)程可以擁有相同的 PID 號,利用 PID Namespace 可以實(shí)現(xiàn)每個(gè)容器的主進(jìn)程為 1 號進(jìn)程,而容器內(nèi)的進(jìn)程在主機(jī)上卻擁有不同的PID。例如一個(gè)進(jìn)程在主機(jī)上 PID 為 122,使用 PID Namespace 可以實(shí)現(xiàn)該進(jìn)程在容器內(nèi)看到的 PID 為 1。

下面我們通過一個(gè)實(shí)例來演示下 PID Namespace的作用。首先我們使用以下命令創(chuàng)建一個(gè) bash 進(jìn)程,并且新建一個(gè) PID Namespace:

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

執(zhí)行完上述命令后,我們在主機(jī)上創(chuàng)建了一個(gè)新的 PID Namespace,并且當(dāng)前命令行窗口加入了新創(chuàng)建的 PID Namespace。在當(dāng)前的命令行窗口使用 ps aux 命令查看一下進(jìn)程信息:

[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

通過上述命令輸出結(jié)果可以看到當(dāng)前 Namespace 下 bash 為 1 號進(jìn)程,而且我們也看不到主機(jī)上的其他進(jìn)程信息。

UTS Namespace

UTS Namespace 主要是用來隔離主機(jī)名的,它允許每個(gè) UTS Namespace 擁有一個(gè)獨(dú)立的主機(jī)名。例如我們的主機(jī)名稱為 docker,使用 UTS Namespace 可以實(shí)現(xiàn)在容器內(nèi)的主機(jī)名稱為 lagoudocker 或者其他任意自定義主機(jī)名。

同樣我們通過一個(gè)實(shí)例來驗(yàn)證下 UTS Namespace 的作用,首先我們使用 unshare 命令來創(chuàng)建一個(gè) UTS Namespace:

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

創(chuàng)建好 UTS Namespace 后,當(dāng)前命令行窗口已經(jīng)處于一個(gè)獨(dú)立的 UTS Namespace 中,下面我們使用 hostname 命令(hostname 可以用來查看主機(jī)名稱)設(shè)置一下主機(jī)名:

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

#查看更改后的主機(jī)名
[root@docker-demo ~]# hostname
lagoudocker

通過上面命令的輸出,我們可以看到當(dāng)前UTS Namespace 內(nèi)的主機(jī)名已經(jīng)被修改為 lagoudocker。然后我們新打開一個(gè)命令行窗口,使用相同的命令查看一下主機(jī)的 hostname:

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

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

IPC Namespace

IPC Namespace 主要是用來隔離進(jìn)程間通信的。例如 PID Namespace 和 IPC Namespace 一起使用可以實(shí)現(xiàn)同一 IPC Namespace 內(nèi)的進(jìn)程彼此可以通信,不同 IPC Namespace 的進(jìn)程卻不能通信。

同樣我們通過一個(gè)實(shí)例來驗(yàn)證下IPC Namespace的作用,首先我們使用 unshare 命令來創(chuàng)建一個(gè) IPC Namespace:

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

下面我們需要借助兩個(gè)命令來實(shí)現(xiàn)對 IPC Namespace 的驗(yàn)證。

  • ipcs -q 命令:用來查看系統(tǒng)間通信隊(duì)列列表。

  • ipcmk -Q 命令:用來創(chuàng)建系統(tǒng)間通信隊(duì)列。

我們首先使用 ipcs -q 命令查看一下當(dāng)前 IPC Namespace 下的系統(tǒng)通信隊(duì)列列表:

[root@docker-demo ~]# ipcs -q

--------- 消息隊(duì)列 -----------
鍵        msqid      擁有者  權(quán)限     已用字節(jié)數(shù) 消息

由上可以看到當(dāng)前無任何系統(tǒng)通信隊(duì)列,然后我們使用 ipcmk -Q 命令創(chuàng)建一個(gè)系統(tǒng)通信隊(duì)列:

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

再次使用 ipcs -q 命令查看當(dāng)前 IPC Namespace 下的系統(tǒng)通信隊(duì)列列表:

[root@docker-demo ~]# ipcs -q

--------- 消息隊(duì)列 -----------
鍵        msqid      擁有者  權(quán)限     已用字節(jié)數(shù) 消息
0x91ce1a3d 0          root       644        0            0

可以看到我們已經(jīng)成功創(chuàng)建了一個(gè)系統(tǒng)通信隊(duì)列。然后我們新打開一個(gè)命令行窗口,使用ipcs -q 命令查看一下主機(jī)的系統(tǒng)通信隊(duì)列:

[root@docker-demo ~]# ipcs -q

--------- 消息隊(duì)列 -----------
鍵        msqid      擁有者  權(quán)限     已用字節(jié)數(shù) 消息

通過上面的實(shí)驗(yàn),可以發(fā)現(xiàn),在單獨(dú)的 IPC Namespace 內(nèi)創(chuàng)建的系統(tǒng)通信隊(duì)列在主機(jī)上無法看到。即 IPC Namespace 實(shí)現(xiàn)了系統(tǒng)通信隊(duì)列的隔離。

User Namespace

User Namespace 主要是用來隔離用戶和用戶組的。一個(gè)比較典型的應(yīng)用場景就是在主機(jī)上以非 root 用戶運(yùn)行的進(jìn)程可以在一個(gè)單獨(dú)的 User Namespace 中映射成 root 用戶。使用 User Namespace 可以實(shí)現(xiàn)進(jìn)程在容器內(nèi)擁有 root 權(quán)限,而在主機(jī)上卻只是普通用戶。User Namesapce 的創(chuàng)建是可以不使用 root 權(quán)限的,下面我們以普通用戶的身份創(chuàng)建一個(gè) User Namespace,命令如下:

# 創(chuàng)建并切換到普通用戶
[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
# 創(chuàng)建 User Namespace
[chengzw@docker-demo ~]$ unshare --user -r /bin/bash

然后執(zhí)行 id 命令查看一下當(dāng)前的用戶信息:

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

通過上面的輸出可以看到我們在新的 User Namespace 內(nèi)已經(jīng)是 root 用戶了。下面我們使用只有主機(jī) root 用戶才可以執(zhí)行的 reboot 命令來驗(yàn)證一下,在當(dāng)前命令行窗口執(zhí)行 reboot 命令:

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

查看當(dāng)前 User Namespace 所在的進(jìn)程:

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

在宿主機(jī)上查看該進(jìn)程對應(yīng)的用戶,可以看到實(shí)際上在宿主機(jī)上是 chengzw 用戶:

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

可以看到,我們在新創(chuàng)建的 User Namespace 內(nèi)雖然是 root 用戶,但是并沒有權(quán)限執(zhí)行 reboot 命令。這說明在隔離的 User Namespace 中,并不能獲取到主機(jī)的 root 權(quán)限,也就是說 User Namespace 實(shí)現(xiàn)了用戶和用戶組的隔離。

Net Namespace

Net Namespace 是用來隔離網(wǎng)絡(luò)設(shè)備、IP 地址和端口等信息的。Net Namespace 可以讓每個(gè)進(jìn)程擁有自己獨(dú)立的 IP 地址,端口和網(wǎng)卡信息。例如主機(jī) IP 地址為 192.168.1.41 ,容器內(nèi)可以設(shè)置獨(dú)立的 IP 地址為 172.16.1.2。

同樣用實(shí)例驗(yàn)證,我們首先使用 ip addr 命令查看一下主機(jī)上的網(wǎng)絡(luò)信息:

[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

然后我們使用以下命令創(chuàng)建一個(gè) Net Namespace:

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

同樣的我們使用 ip addr 命令查看一下網(wǎng)絡(luò)信息:

[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

可以看到,宿主機(jī)上有 lo、ens192 等網(wǎng)絡(luò)設(shè)備,而我們新建的 Net Namespace 內(nèi)則與主機(jī)上的網(wǎng)絡(luò)設(shè)備不同。我們可以在 Net Namespace 中新增網(wǎng)卡并配置 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

首先查看宿主機(jī)的時(shí)間,目前是3月21號:

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

啟動(dòng)一個(gè) centos 容器,查看時(shí)間和宿主機(jī)一致(UTC + 8 = CST):

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

嘗試在容器中修改時(shí)間,提示權(quán)限不夠:

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

在啟動(dòng)容器的時(shí)候,加上--cap-add SYS_TIME參數(shù),在容器內(nèi)將時(shí)間修改成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

在宿主機(jī)上查看時(shí)間發(fā)現(xiàn)也改成3月20號了:

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

--cap-add SYS_TIME是為了將宿主機(jī)的內(nèi)核時(shí)間掛載進(jìn)來與容器共享,因此容器時(shí)間更改了,宿主機(jī)時(shí)間也會(huì)跟著更改。

為什么 Docker 需要 Namespace?

Linux 內(nèi)核從 2002 年 2.4.19 版本開始加入了 Mount Namespace,而直到內(nèi)核 3.8 版本加入了 User Namespace 才為容器提供了足夠的支持功能。

當(dāng) Docker 新建一個(gè)容器時(shí), 它會(huì)創(chuàng)建這六種 Namespace,然后將容器中的進(jìn)程加入這些 Namespace 之中,使得 Docker 容器中的進(jìn)程只能看到當(dāng)前 Namespace 中的系統(tǒng)資源。

正是由于 Docker 使用了 Linux 的這些 Namespace 技術(shù),才實(shí)現(xiàn)了 Docker 容器的隔離,可以說沒有 Namespace,就沒有 Docker 容器。

Docker exec 原理

在了解了 Linux Namespace 的隔離機(jī)制后,你應(yīng)該會(huì)很自然地想到一個(gè)問題:docker exec 是怎么做到進(jìn)入容器里的呢?

實(shí)際上,Linux Namespace 創(chuàng)建的隔離空間雖然看不見摸不著,但一個(gè)進(jìn)程的 Namespace 信息在宿主機(jī)上是確確實(shí)實(shí)存在的,并且是以一個(gè)文件的方式存在。

首先創(chuàng)建一個(gè)容器:

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

通過如下指令,你可以看到當(dāng)前正在運(yùn)行的 Docker 容器的進(jìn)程號(PID)是 25686:

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

這時(shí),你可以通過查看宿主機(jī)的 proc 文件,看到這個(gè) 25686 進(jìn)程的所有 Namespace 對應(yīng)的文件:

$ 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]

可以看到,一個(gè)進(jìn)程的每種 Linux Namespace,都在它對應(yīng)的 /proc/[進(jìn)程號]/ns 下有一個(gè)對應(yīng)的虛擬文件,并且鏈接到一個(gè)真實(shí)的 Namespace 文件上。

有了這樣一個(gè)可以“hold 住”所有 Linux Namespace 的文件,我們就可以對 Namespace 做一些很有意義事情了,比如:加入到一個(gè)已經(jīng)存在的 Namespace 當(dāng)中。

這也就意味著:一個(gè)進(jìn)程,可以選擇加入到某個(gè)進(jìn)程已有的 Namespace 當(dāng)中,從而達(dá)到“進(jìn)入”這個(gè)進(jìn)程所在容器的目的,這正是 docker exec 的實(shí)現(xiàn)原理。

而這個(gè)操作所依賴的,乃是一個(gè)名叫 setns() 的 Linux 系統(tǒng)調(diào)用。它的調(diào)用方法,我可以用如下一段小程序?yàn)槟阏f明:

#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");
}

這段代碼功能非常簡單:它一共接收兩個(gè)參數(shù),第一個(gè)參數(shù)是 argv[1],即當(dāng)前進(jìn)程要加入的 Namespace 文件的路徑,比如 /proc/25686/ns/net;而第二個(gè)參數(shù),則是你要在這個(gè) Namespace 里運(yùn)行的進(jìn)程,比如 /bin/bash。

這段代碼的核心操作,則是通過 open() 系統(tǒng)調(diào)用打開了指定的 Namespace 文件,并把這個(gè)文件的描述符 fd 交給 setns() 使用。在 setns() 執(zhí)行后,當(dāng)前進(jìn)程就加入了這個(gè)文件對應(yīng)的 Linux Namespace 當(dāng)中了。

現(xiàn)在,你可以編譯執(zhí)行一下這個(gè)程序,加入到容器進(jìn)程(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)

正如上所示,當(dāng)我們執(zhí)行 ifconfig 命令查看網(wǎng)絡(luò)設(shè)備時(shí),我會(huì)發(fā)現(xiàn)能看到的網(wǎng)卡“變少”了:只有兩個(gè)。而我的宿主機(jī)則至少有四個(gè)網(wǎng)卡。這是怎么回事呢?

實(shí)際上,在 setns() 之后我看到的這兩個(gè)網(wǎng)卡,正是我在前面啟動(dòng)的 Docker 容器里的網(wǎng)卡。也就是說,我新創(chuàng)建的這個(gè) /bin/bash 進(jìn)程,由于加入了該容器進(jìn)程(PID=25686)的 Network Namepace,它看到的網(wǎng)絡(luò)設(shè)備與這個(gè)容器里是一樣的,即:/bin/bash 進(jìn)程的網(wǎng)絡(luò)設(shè)備視圖,也被修改了。

而一旦一個(gè)進(jìn)程加入到了另一個(gè) Namespace 當(dāng)中,在宿主機(jī)的 Namespace 文件上,也會(huì)有所體現(xiàn)。

在宿主機(jī)上,你可以用 ps 指令找到這個(gè) set_ns 程序執(zhí)行的 /bin/bash 進(jìn)程,其真實(shí)的 PID 是 28499:

# 在宿主機(jī)上
ps aux | grep /bin/bash
root     28499  0.0  0.0 19944  3612 pts/0    S    14:15   0:00 /bin/bash

這時(shí),如果查看一下這個(gè) PID=28499 的進(jìn)程的 Namespace,你就會(huì)發(fā)現(xiàn)這樣一個(gè)事實(shí):

$ 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 目錄下,這個(gè) PID=28499 進(jìn)程,與我們前面的 Docker 容器進(jìn)程(PID=25686)指向的 Network Namespace 文件完全一樣。這說明這兩個(gè)進(jìn)程,共享了這個(gè)名叫 net:[4026532281]的 Network Namespace。

此外,Docker 還專門提供了一個(gè)參數(shù),可以讓你啟動(dòng)一個(gè)容器并“加入”到另一個(gè)容器的 Network Namespace 里,這個(gè)參數(shù)就是 -net,比如:

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

這樣,我們新啟動(dòng)的這個(gè)容器,就會(huì)直接加入到 ID=4ddf4638572d 的容器,也就是我們前面的創(chuàng)建的應(yīng)用容器(PID=25686)的 Network Namespace 中。所以,這里 ifconfig 返回的網(wǎng)卡信息,跟我前面那個(gè)小程序返回的結(jié)果一模一樣,你也可以嘗試一下。

而如果我指定–net=host,就意味著這個(gè)容器不會(huì)為進(jìn)程啟用 Network Namespace。這就意味著,這個(gè)容器拆除了 Network Namespace 的“隔離墻”,所以,它會(huì)和宿主機(jī)上的其他普通進(jìn)程一樣,直接共享宿主機(jī)的網(wǎng)絡(luò)棧。這就為容器直接操作和使用宿主機(jī)網(wǎng)絡(luò)提供了一個(gè)渠道。

到此,關(guān)于“Namespace的作用是什么”的學(xué)習(xí)就結(jié)束了,希望能夠解決大家的疑惑。理論與實(shí)踐的搭配能更好的幫助大家學(xué)習(xí),快去試試吧!若想繼續(xù)學(xué)習(xí)更多相關(guān)知識,請繼續(xù)關(guān)注億速云網(wǎng)站,小編會(huì)繼續(xù)努力為大家?guī)砀鄬?shí)用的文章!

向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI