日日操夜夜添-日日操影院-日日草夜夜操-日日干干-精品一区二区三区波多野结衣-精品一区二区三区高清免费不卡

公告:魔扣目錄網(wǎng)為廣大站長提供免費(fèi)收錄網(wǎng)站服務(wù),提交前請做好本站友鏈:【 網(wǎng)站目錄:http://www.ylptlb.cn 】, 免友鏈快審服務(wù)(50元/站),

點(diǎn)擊這里在線咨詢客服
新站提交
  • 網(wǎng)站:51998
  • 待審:31
  • 小程序:12
  • 文章:1030137
  • 會(huì)員:747

在 深入理解 Docker 核心原理:Namespace、Cgroups 和 Rootfs 一文中我們分析了 Docker 是由三大核心技術(shù)實(shí)現(xiàn)的。

探索 Linux Namespace:Docker 隔離的神奇背后

今天就一起分析 Docker 三大核心技術(shù)之一的 Linux Namespace。

后續(xù)文章會(huì)演示如何從零實(shí)現(xiàn)一個(gè)簡易的 Docker,這里先簡單了解下 Docker 的核心原理。


如果你對云原生技術(shù)充滿好奇,想要深入了解更多相關(guān)的文章和資訊,歡迎關(guān)注微信公眾號(hào)。

搜索公眾號(hào)【探索云原生】即可訂閱

探索 Linux Namespace:Docker 隔離的神奇背后


當(dāng)我們通過 docker run -it 啟動(dòng)并進(jìn)入一個(gè)容器之后,會(huì)發(fā)現(xiàn)不論是進(jìn)程、網(wǎng)絡(luò)還是文件系統(tǒng),好像都被隔離了,就像這樣:

[root@docker cpu]# docker run -it busybox
/ # 
/ # ps
PID   USER     TIME  COMMAND
    1 root      0:00 sh
    7 root      0:00 ps
/ # ip a 
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue 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
120: eth0@if121: <BROADCAST,MULTICAST,UP,LOWER_UP,M-DOWN> mtu 1500 qdisc noqueue 
    link/ether 02:42:ac:11:00:02 brd ff:ff:ff:ff:ff:ff
    inet 172.17.0.2/16 brd 172.17.255.255 scope global eth0
       valid_lft forever preferred_lft forever
/ # ls
bin    dev    etc    home   lib    lib64  proc   root   sys    tmp    usr    var
  • ps 命令看不到宿主機(jī)上的進(jìn)程
  • ip 命令也只能看到容器內(nèi)部的網(wǎng)卡
  • ls 命令看到的文件好像也和宿主機(jī)不一樣

這就是 Docker 核心之一,借助 Linux Namespace 技術(shù)實(shí)現(xiàn)了視圖隔離。

看起來容器和宿主機(jī)隔離開了

接下來就讓我們一起探索一下 Linux Namespace 以及 Docker 是如何借助該能力來實(shí)現(xiàn)隔離的。

1. 什么是 Linux 命名空間(Namespace)?

Namespace 是 Linux 提供的一種內(nèi)核級(jí)別環(huán)境隔離的方法。

可以使得處于不同 namespace 的進(jìn)程擁有獨(dú)立的全局系統(tǒng)資源,改變一個(gè) namespace 中的系統(tǒng)資源只會(huì)影響當(dāng)前 namespace 里的進(jìn)程,對其他 namespace 中的進(jìn)程沒有影響。

簡單來說:namespace 就是對資源的邏輯隔離

目前,Linux 內(nèi)核里面實(shí)現(xiàn)了 8 種不同類型的 namespace:

探索 Linux Namespace:Docker 隔離的神奇背后

分類 系統(tǒng)調(diào)用參數(shù) 隔離內(nèi)容 相關(guān)內(nèi)核版本
Mount namespaces CLONE_NEWNS Mount points Linux 2.4.19
UTS namespaces CLONE_NEWUTS Hostname and NIS domain name Linux 2.6.19
IPC namespaces CLONE_NEWIPC System V IPC, POSIX message queues Linux 2.6.19
PID namespaces CLONE_NEWPID Process IDs Linux 2.6.24
Network namespaces CLONE_NEWNET Network devices, stacks, ports, etc. 始于Linux 2.6.24 完成于 Linux 2.6.29
User namespaces CLONE_NEWUSER User and group IDs 始于 Linux 2.6.23 完成于 Linux 3.8)
Cgroup namespace CLONE_NEWCGROUP Cgroup root directory Linux 4.6
Time namespace CLONE_NEWTIME Boot and monotonic Linux 5.6

前面 6 種是比較常見的,后面兩種 Cgroup Namespace 以及 Time Namespace 則是比較少見。

相關(guān)API

和 namespace 相關(guān)的函數(shù)只有四個(gè),這里簡單的看一下:

探索 Linux Namespace:Docker 隔離的神奇背后

  • clone
  • setns
  • unshare
  • ioctl_ns

clone:創(chuàng)建一個(gè)新的進(jìn)程并把他放到新的 namespace 中。

int clone(int (*fn)(void *), void *stack, int flags, void *arg, ...
                 /* pid_t *parent_tid, void *tls, pid_t *child_tid */ );
 /*
 flags:
    指定一個(gè)或者多個(gè)上面的CLONE_NEW*(當(dāng)然也可以包含跟namespace無關(guān)的flags), 
    這樣就會(huì)創(chuàng)建一個(gè)或多個(gè)新的不同類型的namespace, 
    并把新創(chuàng)建的子進(jìn)程加入新創(chuàng)建的這些namespace中。
 */

setns:將當(dāng)前進(jìn)程加入到已有的 namespace 中。

int setns(int fd, int nstype);
/*
fd:
    指向/proc/[pid]/ns/目錄里相應(yīng)namespace對應(yīng)的文件,
    表示要加入哪個(gè)namespace
nstype:
    指定namespace的類型(上面的任意一個(gè)CLONE_NEW*):
    1. 如果當(dāng)前進(jìn)程不能根據(jù)fd得到它的類型,如fd由其他進(jìn)程創(chuàng)建,
    并通過UNIX domain socket傳給當(dāng)前進(jìn)程,
    那么就需要通過nstype來指定fd指向的namespace的類型
    2. 如果進(jìn)程能根據(jù)fd得到namespace類型,比如這個(gè)fd是由當(dāng)前進(jìn)程打開的,
    那么nstype設(shè)置為0即可
*/

unshare:使當(dāng)前進(jìn)程退出指定類型的 namespace,并加入到新創(chuàng)建的 namespace(相當(dāng)于創(chuàng)建并加入新的 namespace)。

int unshare(int flags);
/*
flags:
    指定一個(gè)或者多個(gè)上面的CLONE_NEW*,
    這樣當(dāng)前進(jìn)程就退出了當(dāng)前指定類型的namespace并加入到新創(chuàng)建的namespace
*/

ioctl_ns:查詢 namespace 信息。

new_fd = ioctl(fd, request);
/*
fd: 指向/proc/[pid]/ns/目錄里相應(yīng)namespace對應(yīng)的文件
request: 
  NS_GET_USERNS: 返回指向擁有用戶的文件描述符namespace fd引用的命名空間
    NS_GET_PARENT: 返回引用父級(jí)的文件描述符由fd引用的命名空間的命名空間。
*/

看完之后大致可以這樣分類:

  • clone、unshare:加入新 namespace
  • setsns:加入已有 namespace
  • ioctl_ns:主要用于查詢

clone 和 unshare 都是加入新 namespace,二者有什么區(qū)別呢?

二者的功能都是創(chuàng)建并加入新的 namespace, 區(qū)別在于:

  • unshare 是使 當(dāng)前進(jìn)程 加入新的 namespace
  • clone 是創(chuàng)建一個(gè)新的子進(jìn)程,然后讓 子進(jìn)程 加入新的 namespace,而當(dāng)前進(jìn)程保持不變

查看進(jìn)程所屬的 namespaces

系統(tǒng)中的每個(gè)進(jìn)程都有 /proc/[pid]/ns/ 這樣一個(gè)目錄,里面包含了這個(gè)進(jìn)程所屬 namespace 的信息,里面每個(gè)文件的描述符都可以用來作為 setns 函數(shù)(后面會(huì)介紹)的參數(shù)。

#查看當(dāng)前bash進(jìn)程所屬的namespace
 lixd  ~  ls -l /proc/$$/ns
total 0
lrwxrwxrwx 1 lixd lixd 0 Jan  6 19:00 cgroup -> 'cgroup:[4026531835]'
lrwxrwxrwx 1 lixd lixd 0 Jan  6 19:00 ipc -> 'ipc:[4026532227]'
lrwxrwxrwx 1 lixd lixd 0 Jan  6 19:00 mnt -> 'mnt:[4026532241]'
lrwxrwxrwx 1 lixd lixd 0 Jan  6 19:00 net -> 'net:[4026531992]'
lrwxrwxrwx 1 lixd lixd 0 Jan  6 19:00 pid -> 'pid:[4026532243]'
lrwxrwxrwx 1 lixd lixd 0 Jan  6 19:00 pid_for_children -> 'pid:[4026532243]'
lrwxrwxrwx 1 lixd lixd 0 Jan  6 19:00 user -> 'user:[4026531837]'
lrwxrwxrwx 1 lixd lixd 0 Jan  6 19:00 uts -> 'uts:[4026532242]

ipc:[4026532227]為例,其中 ipc 是 namespace 的類型,4026532227 是 inode number。

如果兩個(gè)進(jìn)程的 ipc namespace 的 inode number一樣,說明他們屬于同一個(gè) namespace。

這條規(guī)則對其他類型的 namespace 也同樣適用。

namespace 數(shù)量限制與回收策略

linux 也限制了 namespace 的數(shù)量,不能無限制的創(chuàng)建 namespace,具體限制一般在 /proc/sys/user 目錄中。具體如下:

$ tree /proc/sys/user/
/proc/sys/user/
├── max_cgroup_namespaces
├── max_inotify_instances
├── max_inotify_watches
├── max_ipc_namespaces
├── max_mnt_namespaces
├── max_net_namespaces
├── max_pid_namespaces
├── max_user_namespaces
└── max_uts_namespaces

$ cat /proc/sys/user/max_pid_namespaces
6784

以看到,當(dāng)前系統(tǒng)中 PID namespace 最多可以創(chuàng)建 6784 個(gè)。

既然數(shù)量有限制,那已經(jīng)創(chuàng)建的 namespace 什么時(shí)候會(huì)被銷毀回收呢?

規(guī)則還是比較好理解的:當(dāng)一個(gè) namespace 中的所有進(jìn)程都結(jié)束或者移出該 namespace 時(shí),該 namespace 將會(huì)被銷毀。

這也解釋了為什么沒有創(chuàng)建 namespace 的 API,因?yàn)閯倓?chuàng)建的 namespace 沒有任何進(jìn)程,立馬就會(huì)被回收。

不過也有一些特殊情況,可以再?zèng)]有進(jìn)程的時(shí)候保留 namespace:

  • 存在打開的 FD,或者對 /proc/[pid]/ns/* 執(zhí)行了 bind mount
  • 存在子 namespace
  • 它是一個(gè)擁有一個(gè)或多個(gè)非用戶 namespace 的 namespace。
  • 它是一個(gè) PID namespace,并且有一個(gè)進(jìn)程通過 /proc/[pid]/ns/pid_for_children 符號(hào)鏈接引用了這個(gè) namespace。
  • 它是一個(gè) Time namespace,并且有一個(gè)進(jìn)程通過 /proc/[pid]/ns/time_for_children 符號(hào)鏈接引用了這個(gè) namespace。
  • 它是一個(gè) IPC namespace,并且有一個(gè) mqueue 文件系統(tǒng)的 mount 引用了該 namespace
  • 它是一個(gè) PIDnamespace,并且有一個(gè) proc 文件系統(tǒng)的 mount 引用了該 namespace

一句話描述:當(dāng) namespace 有被使用時(shí)就不會(huì)被回收,反之則會(huì)被回收。

2. 使用 Go 語言操作 Linux 命名空間示例

UTS Namespace

UTS Namespace主要用來隔離nodename和domainname兩個(gè)系統(tǒng)標(biāo)識(shí)

在UTS Namespace里面,每個(gè)Namespace允許有自己的hostname.

以下程序展示了如何在 Go 中切換 UTS Namespace。

// 注: 運(yùn)行時(shí)需要 root 權(quán)限。
func main() {
	cmd := exec.Command("bash")
	cmd.SysProcAttr = &syscall.SysProcAttr{
		Cloneflags: syscall.CLONE_NEWUTS,
	}

	cmd.Stdin = os.Stdin
	cmd.Stdout = os.Stdout
	cmd.Stderr = os.Stderr

	if err := cmd.Run(); err != nil {
		log.Fatalln(err)
	}
}

運(yùn)行并測試

DESKTOP-9K4GB6E# go run main.go
root@DESKTOP-9K4GB6E:/home/lixd/projects/docker/mydocker#

運(yùn)行后會(huì)進(jìn)入了一個(gè)新的 shell 環(huán)境。

查看以下是否真的進(jìn)入了新的 UTS Namespace。

首先使用 pstree查看進(jìn)程關(guān)系:

root@DESKTOP-9K4GB6E:/home/lixd/projects/docker/mydocker# pstree -pl
init(1)─┬─init(1272)───init(1273)───server(1274)─┬─{server}(1282)
        │                                        ├─{server}(1283)
        │                                        ├─{server}(1284)
        │                                        ├─{server}(1285)
        │                                        ├─{server}(1286)
        │                                        ├─{server}(1287)
        │                                        ├─{server}(1288)
        │                                        └─{server}(1289)
        ├─init(3701)───init(3702)───zsh(3703)───su(7520)───bash(7521)───zsh(7575)───go(8104)─┬─main(8182)─┬─bash(8187)───pstree(8194)
        │                                                                                    │            ├─{main}(8183)
        │                                                                                    │            ├─{main}(8184)
        │                                                                                    │            ├─{main}(8185)
        │                                                                                    │            └─{main}(8186)
        │                                                                                    ├─{go}(8105)
        │                                                                                    ├─{go}(8106)
        │                                                                                    ├─{go}(8107)
        │                                                                                    ├─{go}(8108)
        │                                                                                    ├─{go}(8109)
        │                                                                                    ├─{go}(8110)
        │                                                                                    ├─{go}(8111)
        │                                                                                    ├─{go}(8112)
        │                                                                                    ├─{go}(8117)
        │                                                                                    └─{go}(8143)
        ├─init(3763)───init(3764)───zsh(3765)
        ├─init(5171)───init(5172)───fsnotifier-wsl(5173)
        ├─init(7459)───init(7460)───bash(7461)───su(7476)───bash(7477)
        ├─{init}(5)
        └─{init}(6)

主要關(guān)注這條:

├─init(3701)───init(3702)───zsh(3703)───su(7520)───bash(7521)───zsh(7575)───go(8104)─┬─main(8182)─┬─bash(8187)

main 程序 pid 為 8182,后續(xù)新創(chuàng)建的 bash pid 為 8187,現(xiàn)在查看二者 uts 是否相同即可:

root@DESKTOP-9K4GB6E:/home/lixd/projects/docker/mydocker# readlink /proc/8182/ns/uts
uts:[4026532242]
root@DESKTOP-9K4GB6E:/home/lixd/projects/docker/mydocker# readlink /proc/8187/ns/uts
uts:[4026532386]

可以發(fā)現(xiàn)二者確實(shí)不在一個(gè) UTS Namespace 中。由于 UTS Namespace hostname 做了隔離 所以在這個(gè)環(huán)境內(nèi)修改 hostname 應(yīng)該不影響外部主機(jī), 下面來做 下實(shí)驗(yàn)。

在這個(gè)新的 bash 環(huán)境中修改 hostname

root@DESKTOP-9K4GB6E:/home/lixd/projects/docker/mydocker# hostname bash
root@DESKTOP-9K4GB6E:/home/lixd/projects/docker/mydocker# hostname
bash

新開一個(gè)在宿主機(jī)上查看 hostname:

 lixd  ~ $ hostname
DESKTOP-9K4GB6E

可以看到外部的 hostname 并沒有被修改影響,由此可了解 UTS Namespace 的作用。

IPC Namespace

IPC Namespace 用來隔離 sys V IPC和 POSIX message queues

每個(gè) IPC Namespace 都有自己的 Sys V IPC 和 POSIX message queues。

微調(diào)一下程序,只是修改了 Cloneflags,新增了 CLONE_NEWIPC,表示同時(shí)創(chuàng)建 IPC Namespace。

// 注: 運(yùn)行時(shí)需要 root 權(quán)限。
func main() {
	cmd := exec.Command("bash")
	cmd.SysProcAttr = &syscall.SysProcAttr{
		// Cloneflags: syscall.CLONE_NEWUTS,
		Cloneflags: syscall.CLONE_NEWUTS | syscall.CLONE_NEWIPC,
	}

	cmd.Stdin = os.Stdin
	cmd.Stdout = os.Stdout
	cmd.Stderr = os.Stderr

	if err := cmd.Run(); err != nil {
		log.Fatalln(err)
	}
}

運(yùn)行并測試:

# 先查看宿主機(jī)上的 ipc message queue
DESKTOP-9K4GB6E# ipcs -q
------ Message Queues --------
key        msqid      owner      perms      used-bytes   messages

# 然后創(chuàng)建一個(gè) 
DESKTOP-9K4GB6E# ipcmk -Q
Message queue id: 0
# 再次查看,發(fā)現(xiàn)有了
DESKTOP-9K4GB6E# ipcs -q
------ Message Queues --------
key        msqid      owner      perms      used-bytes   messages
0x70ffd07c 0          root       644        0            0

運(yùn)行程序進(jìn)入新的 shell

DESKTOP-9K4GB6E# go run main.go
root@DESKTOP-9K4GB6E:/home/lixd/projects/docker/mydocker# ipcs -q
------ Message Queues --------
key        msqid      owner      perms      used-bytes   messages

可以發(fā)現(xiàn),在新的 Namespace 中已經(jīng)看不到宿主機(jī)上的 message queue 了。說明 IPC Namespace 創(chuàng)建成功,IPC 已經(jīng)被隔離。

PID Namespace

PID Namespace是用來隔離進(jìn)程ID的

同樣一個(gè)進(jìn)程在不同的PID Namespace里可以擁有不同的PID。

這樣就可以理解,在docker container 里面,使用ps -ef經(jīng)常會(huì)發(fā)現(xiàn),在容器內(nèi),前臺(tái)運(yùn)行的那個(gè)進(jìn)程PID是1,但是在容器外,使用ps -ef會(huì)發(fā)現(xiàn)同樣的進(jìn)程卻有不同的PID,這就是PID Namespace做的事情。

再次調(diào)整程序,增加 PID flags:

// 注: 運(yùn)行時(shí)需要 root 權(quán)限。
func main() {
	cmd := exec.Command("bash")
	cmd.SysProcAttr = &syscall.SysProcAttr{
		// Cloneflags: syscall.CLONE_NEWUTS,
		// Cloneflags: syscall.CLONE_NEWUTS | syscall.CLONE_NEWIPC,
		Cloneflags: syscall.CLONE_NEWUTS | syscall.CLONE_NEWIPC | syscall.CLONE_NEWPID,
	}

	cmd.Stdin = os.Stdin
	cmd.Stdout = os.Stdout
	cmd.Stderr = os.Stderr

	if err := cmd.Run(); err != nil {
		log.Fatalln(err)
	}
}

運(yùn)行并測試:

DESKTOP-9K4GB6E# go run main.go
root@DESKTOP-9K4GB6E:/home/lixd/projects/docker/mydocker# pstree -pl
init(1)─┬─init(1272)───init(1273)───server(1274)─┬─{server}(1282)
        │                                        ├─{server}(1283)
        │                                        ├─{server}(1284)
        │                                        ├─{server}(1285)
        │                                        ├─{server}(1286)
        │                                        ├─{server}(1287)
        │                                        ├─{server}(1288)
        │                                        └─{server}(1289)
        ├─init(3701)───init(3702)───zsh(3703)───su(7520)───bash(7521)───zsh(7575)───go(9103)─┬─main(9184)─┬─bash(9189)───pstree(9196)
        │                                                                                    │            ├─{main}(9185)
        │                                                                                    │            ├─{main}(9186)
        │                                                                                    │            ├─{main}(9187)
        │                                                                                    │            └─{main}(9188)
        │                                                                                    ├─{go}(9104)
        │                                                                                    ├─{go}(9105)
        │                                                                                    ├─{go}(9106)
        │                                                                                    ├─{go}(9107)
        │                                                                                    ├─{go}(9108)
        │                                                                                    ├─{go}(9109)
        │                                                                                    ├─{go}(9110)
        │                                                                                    ├─{go}(9111)
        │                                                                                    ├─{go}(9112)
        │                                                                                    └─{go}(9120)
        ├─init(3763)───init(3764)───zsh(3765)
        ├─init(5171)───init(5172)───fsnotifier-wsl(5173)
        ├─init(7459)───init(7460)───bash(7461)───su(7476)───bash(7477)
        ├─init(8201)───init(8202)───zsh(8203)
        ├─{init}(5)
        └─{init}(6)

可以看到 main 函數(shù) pid 為 9184,而新開的 bash pid 為 9189。

然后再新開的 bash 中查看自己的 pid:

這里只能使用 echo $$ 命令查看,ps、top 等命令會(huì)查看到其他 Namespace 中的信息。

root@DESKTOP-9K4GB6E:/home/lixd/projects/docker/mydocker# echo $$
1

發(fā)現(xiàn) pid 是1,說明再新開的 PID Namespace 中只有一個(gè) bash 這個(gè)進(jìn)程,而且被偽裝成了 1 號(hào)進(jìn)程。

Mount Namespace

Mount Namespace用來隔離各個(gè)進(jìn)程看到的掛載點(diǎn)視圖

在不同Namespace的進(jìn)程中,看到的文件系統(tǒng)層次是不一樣的。 在Mount Namespace中調(diào)用mount()和umount()僅僅只會(huì)影響當(dāng)前Namespace內(nèi)的文件系統(tǒng),而對全局的文件系統(tǒng)是沒有影響的。

看到這里,也許就會(huì)想到chroot(),它也是將某一個(gè)子目錄變成根節(jié)點(diǎn)。但是,Mount Namespace不僅能實(shí)現(xiàn)這個(gè)功能,而且能以更加靈活和安全的方式實(shí)現(xiàn)。

需要注意的是,Mount Namespace 的 flag 是CLONE_NEWNS,直接是 NEWNS 而不是 NEWMOUNT,因?yàn)?Mount Namespace 是 Linux 中實(shí)現(xiàn)的第一個(gè) Namespace,當(dāng)時(shí)也沒想到后續(xù)會(huì)有很多類型的 Namespace 加入。

再次修改代碼,增加 Mount Namespace 的 flag

	cmd.SysProcAttr = &syscall.SysProcAttr{
		// Cloneflags: syscall.CLONE_NEWUTS,
		// Cloneflags: syscall.CLONE_NEWUTS | syscall.CLONE_NEWIPC,
		// Cloneflags: syscall.CLONE_NEWUTS | syscall.CLONE_NEWIPC | syscall.CLONE_NEWPID,
		Cloneflags: syscall.CLONE_NEWUTS | syscall.CLONE_NEWIPC | syscall.CLONE_NEWPID | syscall.CLONE_NEWNS,
	}

運(yùn)行并測試:

首先運(yùn)行程序并在新的 bash 環(huán)境中查看 /proc

DESKTOP-9K4GB6E# go run main.go
root@DESKTOP-9K4GB6E:/home/lixd/projects/docker/mydocker# ls /proc
1     3764  7476  9476       cmdline    driver       kallsyms     loadavg  net           swaps        vmallocinfo
1272  3765  7477  9557       config.gz  execdomains  kcore        locks    pagetypeinfo  sys          vmstat
1273  5171  7520  9562       consoles   filesystems  key-users    mdstat   partitions    sysvipc      zoneinfo
1274  5172  7521  9569       cpuinfo    fs           keys         meminfo  sched_debug   thread-self
3701  5173  7575  acpi       crypto     interrupts   kmsg         misc     schedstat     timer_list
3702  7459  8201  buddyinfo  devices    iomem        kpagecgroup  modules  self          tty
3703  7460  8202  bus        diskstats  ioports      kpagecount   mounts   softirqs      uptime
3763  7461  8203  cgroups    dma        irq          kpageflags   mtrr     stat          version

可以看到,有一大堆文件,這是因?yàn)楝F(xiàn)在查看到的其實(shí)是宿主機(jī)上的 /proc 目錄。

現(xiàn)在把 proc 目錄掛載到當(dāng)前 Namespace 中來:

root@DESKTOP-9K4GB6E:/home/lixd/projects/docker/mydocker# mount -t proc proc /proc

root@DESKTOP-9K4GB6E:/home/lixd/projects/docker/mydocker# ls /proc
1          cmdline    diskstats    interrupts  key-users    loadavg  mounts        schedstat  sysvipc      vmallocinfo
10         config.gz  dma          iomem       keys         locks    mtrr          self       thread-self  vmstat
acpi       consoles   driver       ioports     kmsg         mdstat   net           softirqs   timer_list   zoneinfo
buddyinfo  cpuinfo    execdomains  irq         kpagecgroup  meminfo  pagetypeinfo  stat       tty
bus        crypto     filesystems  kallsyms    kpagecount   misc     partitions    swaps      uptime
cgroups    devices    fs           kcore       kpageflags   modules  sched_debug   sys        version

可以看到,少了一些文件,少的主要是數(shù)字命名的目錄,因?yàn)楫?dāng)前 Namespace 下沒有這些進(jìn)程,自然就看不到對應(yīng)的信息了。

此時(shí)就可以通過 ps 命令來查看了:

root@DESKTOP-9K4GB6E:/home/lixd/projects/docker/mydocker# ps -ef
UID        PID  PPID  C STIME TTY          TIME CMD
root         1     0  0 13:13 pts/2    00:00:00 bash
root        11     1  0 13:13 pts/2    00:00:00 ps -ef

可以看到,在當(dāng)前 Namespace 中 bash 為 1 號(hào)進(jìn)程。

這就說明,當(dāng)前 Mount Namespace 中的 mount 和外部是隔離的,mount 操作并沒有影響到外部,Docker volume 也是利用了這個(gè)特性。

User Namespace

User Narespace 主要是隔離用戶的用戶組ID

也就是說,一個(gè)進(jìn)程的 UserID 和GroupID 在不同的 User Namespace 中可以是不同的。

比較常用的是,在宿主機(jī)上以一個(gè)非 root 用戶運(yùn)行創(chuàng)建一個(gè) User Namespace, 然后在 User Namespace 里面卻映射成 root 用戶。這意味著,這個(gè)進(jìn)程在 User Namespace 里面有 root 權(quán)限,但是在 User Namespace 外面卻沒有 root 的權(quán)限。

再次修改代碼,增加 User Namespace 的 flag:

	cmd.SysProcAttr = &syscall.SysProcAttr{
		Cloneflags: syscall.CLONE_NEWUTS | syscall.CLONE_NEWIPC | syscall.CLONE_NEWPID | syscall.CLONE_NEWNS | syscall.CLONE_NEWUSER,
	}

運(yùn)行并測試:

首先在宿主機(jī)上查看一個(gè) user 和 group:

DESKTOP-9K4GB6E# id
uid=0(root) gid=0(root) groups=0(root)

可以看到,此時(shí)是 root 用戶。

運(yùn)行程序,進(jìn)入新的 bash 環(huán)境:

DESKTOP-9K4GB6E# go run main.go
nobody@DESKTOP-9K4GB6E:/home/lixd/projects/docker/mydocker$ id
uid=65534(nobody) gid=65534(nogroup) groups=65534(nogroup)

可以看到,UID 是不同的,說明 User Namespace 生效了。

Network Namespace

Network Namespace 是用來隔離網(wǎng)絡(luò)設(shè)備、IP 地址端口等網(wǎng)絡(luò)棧的 Namespace

Network Namespace 可以讓每個(gè)容器擁有自己獨(dú)立的(虛擬的)網(wǎng)絡(luò)設(shè)備,而且容器內(nèi)的應(yīng)用可以綁定到自己的端口,每個(gè) Namespace 內(nèi)的端口都不會(huì)互相沖突。

在宿主機(jī)上搭建網(wǎng)橋后,就能很方便地實(shí)現(xiàn)容器之間的通信,而且不同容器上的應(yīng)用可以使用相同的端口。

再次修改代碼,增加 Network Namespace 的 flag:

	cmd.SysProcAttr = &syscall.SysProcAttr{
		Cloneflags: syscall.CLONE_NEWUTS | syscall.CLONE_NEWIPC | syscall.CLONE_NEWPID | syscall.CLONE_NEWNS | 
			syscall.CLONE_NEWUSER | syscall.CLONE_NEWNET,
	}

運(yùn)行并測試:

首先看一下宿主機(jī)上的網(wǎng)絡(luò)設(shè)備:

DESKTOP-9K4GB6E# ip addr
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default 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: bond0: <BROADCAST,MULTICAST,MASTER> mtu 1500 qdisc noop state DOWN group default qlen 1000
    link/ether 22:2f:1f:8e:f7:72 brd ff:ff:ff:ff:ff:ff
3: dummy0: <BROADCAST,NOARP> mtu 1500 qdisc noop state DOWN group default qlen 1000
    link/ether 7e:46:8b:04:23:81 brd ff:ff:ff:ff:ff:ff
4: tunl0@NONE: <NOARP> mtu 1480 qdisc noop state DOWN group default qlen 1000
    link/ipip 0.0.0.0 brd 0.0.0.0
5: sit0@NONE: <NOARP> mtu 1480 qdisc noop state DOWN group default qlen 1000
    link/sit 0.0.0.0 brd 0.0.0.0
6: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc mq state UP group default qlen 1000
    link/ether 00:15:5d:6e:f2:65 brd ff:ff:ff:ff:ff:ff
    inet 172.18.167.21/20 brd 172.18.175.255 scope global eth0
       valid_lft forever preferred_lft forever
    inet6 fe80::215:5dff:fe6e:f265/64 scope link
       valid_lft forever preferred_lft forever

有 lo、eth0 等6 個(gè)設(shè)備。

然后運(yùn)行程序:

DESKTOP-9K4GB6E# go run main.go
nobody@DESKTOP-9K4GB6E:/home/lixd/projects/docker/mydocker$ ip addr
1: lo: <LOOPBACK> mtu 65536 qdisc noop state DOWN group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
2: tunl0@NONE: <NOARP> mtu 1480 qdisc noop state DOWN group default qlen 1000
    link/ipip 0.0.0.0 brd 0.0.0.0
3: sit0@NONE: <NOARP> mtu 1480 qdisc noop state DOWN group default qlen 1000
    link/sit 0.0.0.0 brd 0.0.0.0

可以發(fā)現(xiàn),新的 Namespace 中只有3個(gè)設(shè)備了,說明 Network Namespace 生效了。

3. 小結(jié)

  • 0)Docker 使用 namespace 進(jìn)行隔離。
  • 1)Namespace 的本質(zhì):Linux Namespace 是 Linux 提供的一種內(nèi)核級(jí)別環(huán)境隔離的方法,本質(zhì)就是對全局系統(tǒng)資源的一種封裝隔離
  • 2)Namespace 的使用:Namespace API 一共 4個(gè),最常用的就是 clone,而 Go 已經(jīng)把 clone 調(diào)用給封裝好了,使用時(shí)只需要傳入不同參數(shù)即可控制創(chuàng)建不同 Namespace。

探索 Linux Namespace:Docker 隔離的神奇背后

探索 Linux Namespace:Docker 隔離的神奇背后


如果你對云原生技術(shù)充滿好奇,想要深入了解更多相關(guān)的文章和資訊,歡迎關(guān)注微信公眾號(hào)。

搜索公眾號(hào)【探索云原生】即可訂閱

分享到:
標(biāo)簽:Docker 探索 服務(wù)器 神奇 隔離
用戶無頭像

網(wǎng)友整理

注冊時(shí)間:

網(wǎng)站:5 個(gè)   小程序:0 個(gè)  文章:12 篇

  • 51998

    網(wǎng)站

  • 12

    小程序

  • 1030137

    文章

  • 747

    會(huì)員

趕快注冊賬號(hào),推廣您的網(wǎng)站吧!
最新入駐小程序

數(shù)獨(dú)大挑戰(zhàn)2018-06-03

數(shù)獨(dú)一種數(shù)學(xué)游戲,玩家需要根據(jù)9

答題星2018-06-03

您可以通過答題星輕松地創(chuàng)建試卷

全階人生考試2018-06-03

各種考試題,題庫,初中,高中,大學(xué)四六

運(yùn)動(dòng)步數(shù)有氧達(dá)人2018-06-03

記錄運(yùn)動(dòng)步數(shù),積累氧氣值。還可偷

每日養(yǎng)生app2018-06-03

每日養(yǎng)生,天天健康

體育訓(xùn)練成績評(píng)定2018-06-03

通用課目體育訓(xùn)練成績評(píng)定