在 深入理解 Docker 核心原理:Namespace、Cgroups 和 Rootfs 一文中我們分析了 Docker 是由三大核心技術實現的。
今天就一起分析 Docker 三大核心技術之一的 Linux Namespace。
后續文章會演示如何從零實現一個簡易的 Docker,這里先簡單了解下 Docker 的核心原理。
如果你對云原生技術充滿好奇,想要深入了解更多相關的文章和資訊,歡迎關注微信公眾號。
搜索公眾號【探索云原生】即可訂閱
當我們通過 docker run -it
啟動并進入一個容器之后,會發現不論是進程、網絡還是文件系統,好像都被隔離了,就像這樣:
[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 命令看不到宿主機上的進程
- ip 命令也只能看到容器內部的網卡
- ls 命令看到的文件好像也和宿主機不一樣
這就是 Docker 核心之一,借助 Linux Namespace 技術實現了視圖隔離。
看起來容器和宿主機隔離開了
接下來就讓我們一起探索一下 Linux Namespace 以及 Docker 是如何借助該能力來實現隔離的。
1. 什么是 Linux 命名空間(Namespace)?
Namespace 是 Linux 提供的一種內核級別環境隔離的方法。
可以使得處于不同 namespace 的進程擁有獨立的全局系統資源,改變一個 namespace 中的系統資源只會影響當前 namespace 里的進程,對其他 namespace 中的進程沒有影響。
簡單來說:namespace 就是對資源的邏輯隔離
目前,Linux 內核里面實現了 8 種不同類型的 namespace:
分類 | 系統調用參數 | 隔離內容 | 相關內核版本 |
---|---|---|---|
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 則是比較少見。
相關API
和 namespace 相關的函數只有四個,這里簡單的看一下:
- clone
- setns
- unshare
- ioctl_ns
clone:創建一個新的進程并把他放到新的 namespace 中。
int clone(int (*fn)(void *), void *stack, int flags, void *arg, ...
/* pid_t *parent_tid, void *tls, pid_t *child_tid */ );
/*
flags:
指定一個或者多個上面的CLONE_NEW*(當然也可以包含跟namespace無關的flags),
這樣就會創建一個或多個新的不同類型的namespace,
并把新創建的子進程加入新創建的這些namespace中。
*/
setns:將當前進程加入到已有的 namespace 中。
int setns(int fd, int nstype);
/*
fd:
指向/proc/[pid]/ns/目錄里相應namespace對應的文件,
表示要加入哪個namespace
nstype:
指定namespace的類型(上面的任意一個CLONE_NEW*):
1. 如果當前進程不能根據fd得到它的類型,如fd由其他進程創建,
并通過UNIX domain socket傳給當前進程,
那么就需要通過nstype來指定fd指向的namespace的類型
2. 如果進程能根據fd得到namespace類型,比如這個fd是由當前進程打開的,
那么nstype設置為0即可
*/
unshare:使當前進程退出指定類型的 namespace,并加入到新創建的 namespace(相當于創建并加入新的 namespace)。
int unshare(int flags);
/*
flags:
指定一個或者多個上面的CLONE_NEW*,
這樣當前進程就退出了當前指定類型的namespace并加入到新創建的namespace
*/
ioctl_ns:查詢 namespace 信息。
new_fd = ioctl(fd, request);
/*
fd: 指向/proc/[pid]/ns/目錄里相應namespace對應的文件
request:
NS_GET_USERNS: 返回指向擁有用戶的文件描述符namespace fd引用的命名空間
NS_GET_PARENT: 返回引用父級的文件描述符由fd引用的命名空間的命名空間。
*/
看完之后大致可以這樣分類:
- clone、unshare:加入新 namespace
- setsns:加入已有 namespace
- ioctl_ns:主要用于查詢
clone 和 unshare 都是加入新 namespace,二者有什么區別呢?
二者的功能都是創建并加入新的 namespace, 區別在于:
- unshare 是使 當前進程 加入新的 namespace
- clone 是創建一個新的子進程,然后讓 子進程 加入新的 namespace,而當前進程保持不變
查看進程所屬的 namespaces
系統中的每個進程都有 /proc/[pid]/ns/
這樣一個目錄,里面包含了這個進程所屬 namespace 的信息,里面每個文件的描述符都可以用來作為 setns 函數(后面會介紹)的參數。
#查看當前bash進程所屬的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。
如果兩個進程的 ipc namespace 的 inode number一樣,說明他們屬于同一個 namespace。
這條規則對其他類型的 namespace 也同樣適用。
namespace 數量限制與回收策略
linux 也限制了 namespace 的數量,不能無限制的創建 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
以看到,當前系統中 PID namespace 最多可以創建 6784 個。
既然數量有限制,那已經創建的 namespace 什么時候會被銷毀回收呢?
規則還是比較好理解的:當一個 namespace 中的所有進程都結束或者移出該 namespace 時,該 namespace 將會被銷毀。
這也解釋了為什么沒有創建 namespace 的 API,因為剛創建的 namespace 沒有任何進程,立馬就會被回收。
不過也有一些特殊情況,可以再沒有進程的時候保留 namespace:
- 存在打開的 FD,或者對
/proc/[pid]/ns/*
執行了 bind mount - 存在子 namespace
- 它是一個擁有一個或多個非用戶 namespace 的 namespace。
- 它是一個 PID namespace,并且有一個進程通過 /proc/[pid]/ns/pid_for_children 符號鏈接引用了這個 namespace。
- 它是一個 Time namespace,并且有一個進程通過 /proc/[pid]/ns/time_for_children 符號鏈接引用了這個 namespace。
- 它是一個 IPC namespace,并且有一個 mqueue 文件系統的 mount 引用了該 namespace
- 它是一個 PIDnamespace,并且有一個 proc 文件系統的 mount 引用了該 namespace
一句話描述:當 namespace 有被使用時就不會被回收,反之則會被回收。
2. 使用 Go 語言操作 Linux 命名空間示例
UTS Namespace
UTS Namespace主要用來隔離nodename和domainname兩個系統標識。
在UTS Namespace里面,每個Namespace允許有自己的hostname.
以下程序展示了如何在 Go 中切換 UTS Namespace。
// 注: 運行時需要 root 權限。
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)
}
}
運行并測試
DESKTOP-9K4GB6E# go run main.go
root@DESKTOP-9K4GB6E:/home/lixd/projects/docker/mydocker#
運行后會進入了一個新的 shell 環境。
查看以下是否真的進入了新的 UTS Namespace。
首先使用 pstree
查看進程關系:
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)
主要關注這條:
├─init(3701)───init(3702)───zsh(3703)───su(7520)───bash(7521)───zsh(7575)───go(8104)─┬─main(8182)─┬─bash(8187)
main 程序 pid 為 8182,后續新創建的 bash pid 為 8187,現在查看二者 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]
可以發現二者確實不在一個 UTS Namespace 中。由于 UTS Namespace hostname 做了隔離 所以在這個環境內修改 hostname 應該不影響外部主機, 下面來做 下實驗。
在這個新的 bash 環境中修改 hostname
root@DESKTOP-9K4GB6E:/home/lixd/projects/docker/mydocker# hostname bash
root@DESKTOP-9K4GB6E:/home/lixd/projects/docker/mydocker# hostname
bash
新開一個在宿主機上查看 hostname:
lixd ~ $ hostname
DESKTOP-9K4GB6E
可以看到外部的 hostname 并沒有被修改影響,由此可了解 UTS Namespace 的作用。
IPC Namespace
IPC Namespace 用來隔離 sys V IPC和 POSIX message queues。
每個 IPC Namespace 都有自己的 Sys V IPC 和 POSIX message queues。
微調一下程序,只是修改了 Cloneflags,新增了 CLONE_NEWIPC,表示同時創建 IPC Namespace。
// 注: 運行時需要 root 權限。
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)
}
}
運行并測試:
# 先查看宿主機上的 ipc message queue
DESKTOP-9K4GB6E# ipcs -q
------ Message Queues --------
key msqid owner perms used-bytes messages
# 然后創建一個
DESKTOP-9K4GB6E# ipcmk -Q
Message queue id: 0
# 再次查看,發現有了
DESKTOP-9K4GB6E# ipcs -q
------ Message Queues --------
key msqid owner perms used-bytes messages
0x70ffd07c 0 root 644 0 0
運行程序進入新的 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
可以發現,在新的 Namespace 中已經看不到宿主機上的 message queue 了。說明 IPC Namespace 創建成功,IPC 已經被隔離。
PID Namespace
PID Namespace是用來隔離進程ID的。
同樣一個進程在不同的PID Namespace里可以擁有不同的PID。
這樣就可以理解,在docker container 里面,使用ps -ef經常會發現,在容器內,前臺運行的那個進程PID是1,但是在容器外,使用ps -ef會發現同樣的進程卻有不同的PID,這就是PID Namespace做的事情。
再次調整程序,增加 PID flags:
// 注: 運行時需要 root 權限。
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)
}
}
運行并測試:
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 函數 pid 為 9184,而新開的 bash pid 為 9189。
然后再新開的 bash 中查看自己的 pid:
這里只能使用
echo $$
命令查看,ps、top 等命令會查看到其他 Namespace 中的信息。
root@DESKTOP-9K4GB6E:/home/lixd/projects/docker/mydocker# echo $$
1
發現 pid 是1,說明再新開的 PID Namespace 中只有一個 bash 這個進程,而且被偽裝成了 1 號進程。
Mount Namespace
Mount Namespace用來隔離各個進程看到的掛載點視圖。
在不同Namespace的進程中,看到的文件系統層次是不一樣的。 在Mount Namespace中調用mount()和umount()僅僅只會影響當前Namespace內的文件系統,而對全局的文件系統是沒有影響的。
看到這里,也許就會想到chroot(),它也是將某一個子目錄變成根節點。但是,Mount Namespace不僅能實現這個功能,而且能以更加靈活和安全的方式實現。
需要注意的是,Mount Namespace 的 flag 是
CLONE_NEWNS
,直接是 NEWNS 而不是 NEWMOUNT,因為 Mount Namespace 是 Linux 中實現的第一個 Namespace,當時也沒想到后續會有很多類型的 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,
}
運行并測試:
首先運行程序并在新的 bash 環境中查看 /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
可以看到,有一大堆文件,這是因為現在查看到的其實是宿主機上的 /proc 目錄。
現在把 proc 目錄掛載到當前 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
可以看到,少了一些文件,少的主要是數字命名的目錄,因為當前 Namespace 下沒有這些進程,自然就看不到對應的信息了。
此時就可以通過 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
可以看到,在當前 Namespace 中 bash 為 1 號進程。
這就說明,當前 Mount Namespace 中的 mount 和外部是隔離的,mount 操作并沒有影響到外部,Docker volume 也是利用了這個特性。
User Namespace
User Narespace 主要是隔離用戶的用戶組ID。
也就是說,一個進程的 UserID 和GroupID 在不同的 User Namespace 中可以是不同的。
比較常用的是,在宿主機上以一個非 root 用戶運行創建一個 User Namespace, 然后在 User Namespace 里面卻映射成 root 用戶。這意味著,這個進程在 User Namespace 里面有 root 權限,但是在 User Namespace 外面卻沒有 root 的權限。
再次修改代碼,增加 User Namespace 的 flag:
cmd.SysProcAttr = &syscall.SysProcAttr{
Cloneflags: syscall.CLONE_NEWUTS | syscall.CLONE_NEWIPC | syscall.CLONE_NEWPID | syscall.CLONE_NEWNS | syscall.CLONE_NEWUSER,
}
運行并測試:
首先在宿主機上查看一個 user 和 group:
DESKTOP-9K4GB6E# id
uid=0(root) gid=0(root) groups=0(root)
可以看到,此時是 root 用戶。
運行程序,進入新的 bash 環境:
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 是用來隔離網絡設備、IP 地址端口等網絡棧的 Namespace。
Network Namespace 可以讓每個容器擁有自己獨立的(虛擬的)網絡設備,而且容器內的應用可以綁定到自己的端口,每個 Namespace 內的端口都不會互相沖突。
在宿主機上搭建網橋后,就能很方便地實現容器之間的通信,而且不同容器上的應用可以使用相同的端口。
再次修改代碼,增加 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,
}
運行并測試:
首先看一下宿主機上的網絡設備:
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 個設備。
然后運行程序:
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
可以發現,新的 Namespace 中只有3個設備了,說明 Network Namespace 生效了。
3. 小結
- 0)Docker 使用 namespace 進行隔離。
- 1)Namespace 的本質:Linux Namespace 是 Linux 提供的一種內核級別環境隔離的方法,本質就是對全局系統資源的一種封裝隔離。
- 2)Namespace 的使用:Namespace API 一共 4個,最常用的就是 clone,而 Go 已經把 clone 調用給封裝好了,使用時只需要傳入不同參數即可控制創建不同 Namespace。
如果你對云原生技術充滿好奇,想要深入了解更多相關的文章和資訊,歡迎關注微信公眾號。
搜索公眾號【探索云原生】即可訂閱