前言
namespace是實現(xiàn)容器隔離的基礎(chǔ).namespace的本質(zhì)就是把原來所有進(jìn)程全局共享的資源拆分成了很多個一組一組進(jìn)程共享的資源.
root@nicktming:~# ls -l /proc/self/ns
total 0
lrwxrwxrwx 1 root root 0 Mar 27 21:57 ipc -> ipc:[4026531839]
lrwxrwxrwx 1 root root 0 Mar 27 21:57 mnt -> mnt:[4026531840]
lrwxrwxrwx 1 root root 0 Mar 27 21:57 net -> net:[4026531956]
lrwxrwxrwx 1 root root 0 Mar 27 21:57 pid -> pid:[4026531836]
lrwxrwxrwx 1 root root 0 Mar 27 21:57 user -> user:[4026531837]
lrwxrwxrwx 1 root root 0 Mar 27 21:57 uts -> uts:[4026531838]
接下來將用實踐的例子來簡單看一下這些
namespace的作用.
后面的例子需要用到unshare命令
root@nicktming:~# unshare
Usage:
unshare [options] <program> [args...]
Options:
-h, --help usage information (this)
-m, --mount unshare mounts namespace
-u, --uts unshare UTS namespace (hostname etc)
-i, --ipc unshare System V IPC namespace
-n, --net unshare network namespace
For more information see unshare(1).
UTS Namespace
UTS Namespace主要用來隔離nodename和domainname兩個系統(tǒng)標(biāo)識.
終端執(zhí)行例子
root@nicktming:~# echo $$
13563
root@nicktming:~# ls -l /proc/13563/ns
total 0
lrwxrwxrwx 1 root root 0 Mar 27 22:34 ipc -> ipc:[4026531839]
lrwxrwxrwx 1 root root 0 Mar 27 22:34 mnt -> mnt:[4026531840]
lrwxrwxrwx 1 root root 0 Mar 27 22:34 net -> net:[4026531956]
lrwxrwxrwx 1 root root 0 Mar 27 22:34 pid -> pid:[4026531836]
lrwxrwxrwx 1 root root 0 Mar 27 22:34 user -> user:[4026531837]
lrwxrwxrwx 1 root root 0 Mar 27 22:34 uts -> uts:[4026531838]
root@nicktming:~# unshare -u /bin/sh
# echo $$
14029
# ps -ef | grep 14029
root 14029 13563 0 22:35 pts/2 00:00:00 /bin/sh
root 14091 14029 0 22:36 pts/2 00:00:00 ps -ef
root 14092 14029 0 22:36 pts/2 00:00:00 grep 14029
# ls -l /proc/14029/ns
total 0
lrwxrwxrwx 1 root root 0 Mar 27 22:36 ipc -> ipc:[4026531839]
lrwxrwxrwx 1 root root 0 Mar 27 22:36 mnt -> mnt:[4026531840]
lrwxrwxrwx 1 root root 0 Mar 27 22:36 net -> net:[4026531956]
lrwxrwxrwx 1 root root 0 Mar 27 22:36 pid -> pid:[4026531836]
lrwxrwxrwx 1 root root 0 Mar 27 22:36 user -> user:[4026531837]
lrwxrwxrwx 1 root root 0 Mar 27 22:36 uts -> uts:[4026532166]
# hostname
nicktming
# hostname -b bird
# hostname
bird
# uname -a
Linux bird 3.13.0-128-generic #177-Ubuntu SMP Tue Aug 8 11:40:23 UTC 2017 x86_64 x86_64 x86_64 GNU/Linux
由上可以看到進(jìn)程
13563中啟動了一個子進(jìn)程14029并且有uts namespace, 所以可以看到這父子進(jìn)程共享了ipc mnt net pid user namespace,但是并不在同一個uts namspace中.
打開另外一個終端執(zhí)行.
root@nicktming:~# hostname
nicktming
root@nicktming:~# uname -a
Linux nicktming 3.13.0-128-generic #177-Ubuntu SMP Tue Aug 8 11:40:23 UTC 2017 x86_64 x86_64 x86_64 GNU/Linux
可以看到在
14029進(jìn)程新建的一個uts namespace里改變hostname并不會影響主機(jī)里面的hostname, 更不會影響到其他的namespace中的hostname, 進(jìn)而達(dá)到一種隔離的狀態(tài).
go實現(xiàn)
go實現(xiàn)只需要在啟子進(jìn)程的時候在SysProcAttr定義中加入需要創(chuàng)建的namespace即可,uts namespace對應(yīng)的是syscall.CLONE_NEWUTS.
func main() {
cmd := exec.Command("/bin/sh")
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.Printf("Run error:%v\n", err)
log.Fatal(err)
}
}
執(zhí)行如下:
root@nicktming:~/go/src/github.com/nicktming/mydocker/test/namespace# hostname
nicktming
root@nicktming:~/go/src/github.com/nicktming/mydocker/test/namespace# go run utsNamespace.go
# hostname
nicktming
# hostname -b bird
# hostname
bird
打開另外一個終端執(zhí)行
hostname,可以看到效果與上面終端例子一樣.
root@nicktming:~# hostname
nicktming
MNT Namespace
Mount Namespace用來隔離各個進(jìn)程看到的掛載點視圖。在不同Namespace的進(jìn)程中,看到的文件系統(tǒng)層次是不一樣的。在Mount Namespace中調(diào)用mount()和umount()僅僅只會影響當(dāng)前Namespace內(nèi)的文件系統(tǒng),而對全局的文件系統(tǒng)是沒有影響的.
終端執(zhí)行例子
root@nicktming:~# echo $$
8217
root@nicktming:~# ls -l /proc/8217/ns
total 0
lrwxrwxrwx 1 root root 0 Mar 28 21:23 ipc -> ipc:[4026531839]
lrwxrwxrwx 1 root root 0 Mar 28 21:23 mnt -> mnt:[4026531840]
lrwxrwxrwx 1 root root 0 Mar 28 21:23 net -> net:[4026531956]
lrwxrwxrwx 1 root root 0 Mar 28 21:23 pid -> pid:[4026531836]
lrwxrwxrwx 1 root root 0 Mar 28 21:23 user -> user:[4026531837]
lrwxrwxrwx 1 root root 0 Mar 28 21:23 uts -> uts:[4026531838]
root@nicktming:~# mkdir -p /tmp/test_mnt_namespace
root@nicktming:~# unshare --mount /bin/sh
# echo $$
8493
# ls -l /proc/8493/ns
total 0
lrwxrwxrwx 1 root root 0 Mar 28 21:25 ipc -> ipc:[4026531839]
lrwxrwxrwx 1 root root 0 Mar 28 21:25 mnt -> mnt:[4026532166]
lrwxrwxrwx 1 root root 0 Mar 28 21:25 net -> net:[4026531956]
lrwxrwxrwx 1 root root 0 Mar 28 21:25 pid -> pid:[4026531836]
lrwxrwxrwx 1 root root 0 Mar 28 21:25 user -> user:[4026531837]
lrwxrwxrwx 1 root root 0 Mar 28 21:25 uts -> uts:[4026531838]
# mount -t tmpfs tmpfs /tmp/test_mnt_namespace
# cd /tmp/test_mnt_namespace
# echo "pid:8493 mnt namespace" > test01.txt
# ls
test01.txt
可以看到父子進(jìn)程不在同一個
mnt namespace.重新打開一個terminal.
root@nicktming:~# ls -l /tmp/test_mnt_namespace/
total 0
root@nicktming:~# unshare --mount /bin/sh
# ls /tmp/test_mnt_namespace
# mount -t tmpfs tmpfs /tmp/test_mnt_namespace
# cd /tmp/test_mnt_namespace
# echo $$
8996
# echo "pid:8996 mnt namespace" > test02.txt
# ls
test02.txt
go實現(xiàn)
利用
syscall.CLONE_NEWNS這個字段.
func main() {
cmd := exec.Command("/bin/sh")
cmd.SysProcAttr = &syscall.SysProcAttr{
Cloneflags: syscall.CLONE_NEWNS,
}
cmd.Stdin = os.Stdin
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
if err := cmd.Run(); err != nil {
log.Printf("Run error:%v\n", err)
log.Fatal(err)
}
}
PID Namespace
PID Namespace是用來隔離進(jìn)程ID的. 同樣一個進(jìn)程在不同的PID Namespace中有不同的pid.
go實現(xiàn)
root@nicktming:~/go/src/github.com/nicktming/mydocker/test/namespace# echo $$
8848
root@nicktming:~/go/src/github.com/nicktming/mydocker/test/namespace# go run pidNamespace.go
# echo $$
1
# ps -l
F S UID PID PPID C PRI NI ADDR SZ WCHAN TTY TIME CMD
4 S 0 8848 8796 0 80 0 - 5343 wait pts/2 00:00:00 bash
4 S 0 12834 8848 0 80 0 - 45700 futex_ pts/2 00:00:00 go
4 S 0 12852 12834 0 80 0 - 810 wait pts/2 00:00:00 pidNamespace
0 S 0 12855 12852 0 80 0 - 1111 wait pts/2 00:00:00 sh
0 R 0 12908 12855 0 80 0 - 2185 - pts/2 00:00:00 ps
# mount -t proc proc /proc
# ps -l
F S UID PID PPID C PRI NI ADDR SZ WCHAN TTY TIME CMD
0 S 0 1 0 0 80 0 - 1111 wait pts/2 00:00:00 sh
0 R 0 4 1 0 80 0 - 2185 - pts/2 00:00:00 ps
可以看到進(jìn)行該子進(jìn)程進(jìn)入了一個新的
mnt pid namespace, 在宿主機(jī)中該子進(jìn)程的pid是12855,但是在其自己的namespace中它的pid是1.
NET Namespace
Network Namespace是用來隔離網(wǎng)絡(luò)設(shè)備、IP地址端口等網(wǎng)絡(luò)械的Namespace.Network Namespace可以讓每個容器擁有自己獨立的(虛擬的)網(wǎng)絡(luò)設(shè)備,而且容器內(nèi)的應(yīng)用可以綁定到自己的端口,每個Namespace內(nèi)的端口都不會互相沖突。在宿主機(jī)上搭建網(wǎng)橋后,就能很方便地實現(xiàn)容器之間的通信,而且不同容器上的應(yīng)用可以使用相同的端口。
終端執(zhí)行例子
root@nicktming:~# echo $$
1222
root@nicktming:~# ls -l /proc/1222/ns
total 0
lrwxrwxrwx 1 root root 0 Mar 28 22:34 ipc -> ipc:[4026531839]
lrwxrwxrwx 1 root root 0 Mar 28 22:34 mnt -> mnt:[4026531840]
lrwxrwxrwx 1 root root 0 Mar 28 22:34 net -> net:[4026531956]
lrwxrwxrwx 1 root root 0 Mar 28 22:34 pid -> pid:[4026531836]
lrwxrwxrwx 1 root root 0 Mar 28 22:34 user -> user:[4026531837]
lrwxrwxrwx 1 root root 0 Mar 28 22:34 uts -> uts:[4026531838]
root@nicktming:~# unshare --net /bin/sh
# echo $$
1353
# ls -l /proc/1353/ns
total 0
lrwxrwxrwx 1 root root 0 Mar 28 22:35 ipc -> ipc:[4026531839]
lrwxrwxrwx 1 root root 0 Mar 28 22:35 mnt -> mnt:[4026531840]
lrwxrwxrwx 1 root root 0 Mar 28 22:35 net -> net:[4026532161]
lrwxrwxrwx 1 root root 0 Mar 28 22:35 pid -> pid:[4026531836]
lrwxrwxrwx 1 root root 0 Mar 28 22:35 user -> user:[4026531837]
lrwxrwxrwx 1 root root 0 Mar 28 22:35 uts -> uts:[4026531838]
# ifconfig
# ip addr
1: lo: <LOOPBACK> mtu 65536 qdisc noop state DOWN group default
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
# ping 127.0.0.1
connect: Network is unreachable
# ip link set lo up
# ip addr
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default
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
# ping 127.0.0.1
PING 127.0.0.1 (127.0.0.1) 56(84) bytes of data.
64 bytes from 127.0.0.1: icmp_seq=1 ttl=64 time=0.016 ms
可以看到父子進(jìn)程在不同的
network namespace中.
go實現(xiàn)
用
syscall.CLONE_NEWNET進(jìn)行標(biāo)識.
func main() {
cmd := exec.Command("/bin/sh")
cmd.SysProcAttr = &syscall.SysProcAttr{
Cloneflags: syscall.CLONE_NEWNET,
}
cmd.Stdin = os.Stdin
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
if err := cmd.Run(); err != nil {
log.Printf("Run error:%v\n", err)
log.Fatal(err)
}
}
執(zhí)行結(jié)果如下:
root@nicktming:~/go/src/github.com/nicktming/mydocker/test/namespace# go run netNamespace.go
# ifconfig
# ip addr
1: lo: <LOOPBACK> mtu 65536 qdisc noop state DOWN group default
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
USER Namespace
User Namespace主要是隔離用戶的用戶組ID. 也就是說, 一個進(jìn)程的User ID和Group ID在User Namespace內(nèi)外可以是不同的。比較常用的是,在宿主機(jī)上以一個非root用戶運行創(chuàng)建一個User Namespace, 然后在User Namespace 里面卻映射成root用戶。這意味著 這個進(jìn)程在User Namespace里面有root權(quán)限,但是在User Namespace外面卻沒有root的權(quán)限。從Linux Kernel 3.8開始,非root進(jìn)程也可以創(chuàng)建User Namespace, 并且此用戶在Namespace里面可以被映射成root,且在Namespace內(nèi)有root權(quán)限。
func main() {
cmd := exec.Command("/bin/sh")
cmd.SysProcAttr = &syscall.SysProcAttr{
Cloneflags: syscall.CLONE_NEWUSER,
}
cmd.Stdin = os.Stdin
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
if err := cmd.Run(); err != nil {
log.Printf("Run error:%v\n", err)
log.Fatal(err)
}
}
執(zhí)行結(jié)果如下:
root@nicktming:~/go/src/github.com/nicktming/mydocker/test/namespace# id
uid=0(root) gid=0(root) groups=0(root)
root@nicktming:~/go/src/github.com/nicktming/mydocker/test/namespace# go run userNamespace.go
$ id
uid=65534(nobody) gid=65534(nogroup) groups=65534(nogroup)
IPC Namespace
IPC Namespace用來隔離System V IPC和POSIX message queues.
終端執(zhí)行例子
root@nicktming:~# ipcs
------ Shared Memory Segments --------
key shmid owner perms bytes nattch status
------ Semaphore Arrays --------
key semid owner perms nsems
------ Message Queues --------
key msqid owner perms used-bytes messages
root@nicktming:~# echo $$
3792
root@nicktming:~# ls -l /proc/3792/ns
total 0
lrwxrwxrwx 1 root root 0 Mar 28 22:53 ipc -> ipc:[4026531839]
lrwxrwxrwx 1 root root 0 Mar 28 22:53 mnt -> mnt:[4026531840]
lrwxrwxrwx 1 root root 0 Mar 28 22:53 net -> net:[4026531956]
lrwxrwxrwx 1 root root 0 Mar 28 22:53 pid -> pid:[4026531836]
lrwxrwxrwx 1 root root 0 Mar 28 22:53 user -> user:[4026531837]
lrwxrwxrwx 1 root root 0 Mar 28 22:53 uts -> uts:[4026531838]
root@nicktming:~# unshare --ipc /bin/sh
# echo $$
3861
# ls -l /proc/3861/ns
total 0
lrwxrwxrwx 1 root root 0 Mar 28 22:54 ipc -> ipc:[4026532160]
lrwxrwxrwx 1 root root 0 Mar 28 22:54 mnt -> mnt:[4026531840]
lrwxrwxrwx 1 root root 0 Mar 28 22:54 net -> net:[4026531956]
lrwxrwxrwx 1 root root 0 Mar 28 22:54 pid -> pid:[4026531836]
lrwxrwxrwx 1 root root 0 Mar 28 22:54 user -> user:[4026531837]
lrwxrwxrwx 1 root root 0 Mar 28 22:54 uts -> uts:[4026531838]
# ipcs
------ Shared Memory Segments --------
key shmid owner perms bytes nattch status
------ Semaphore Arrays --------
key semid owner perms nsems
------ Message Queues --------
key msqid owner perms used-bytes messages
# ipcmk -Q
Message queue id: 0
# ipcs -q
------ Message Queues --------
key msqid owner perms used-bytes messages
0x695dae8c 0 root 644 0 0
# ipcs
------ Shared Memory Segments --------
key shmid owner perms bytes nattch status
------ Semaphore Arrays --------
key semid owner perms nsems
------ Message Queues --------
key msqid owner perms used-bytes messages
0x695dae8c 0 root 644 0 0
打開另外一個
terminal,發(fā)現(xiàn)宿主機(jī)上并沒有剛剛創(chuàng)建的queue,所以ipc namespace隔離已經(jīng)成功.
root@nicktming:~# ipcs
------ Shared Memory Segments --------
key shmid owner perms bytes nattch status
------ Semaphore Arrays --------
key semid owner perms nsems
------ Message Queues --------
key msqid owner perms used-bytes messages
go實現(xiàn)
func main() {
cmd := exec.Command("/bin/sh")
cmd.SysProcAttr = &syscall.SysProcAttr{
Cloneflags: syscall.CLONE_NEWIPC,
}
cmd.Stdin = os.Stdin
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
if err := cmd.Run(); err != nil {
log.Printf("Run error:%v\n", err)
log.Fatal(err)
}
}
參考
1. 自己動手寫docker.(基本參考此書,加入一些自己的理解,加深對
docker的理解)
全部內(nèi)容
mydocker.png
1. [mydocker]---環(huán)境說明
2. [mydocker]---urfave cli 理解
3. [mydocker]---Linux Namespace
4. [mydocker]---Linux Cgroup
5. [mydocker]---構(gòu)造容器01-實現(xiàn)run命令
6. [mydocker]---構(gòu)造容器02-實現(xiàn)資源限制01
7. [mydocker]---構(gòu)造容器02-實現(xiàn)資源限制02
8. [mydocker]---構(gòu)造容器03-實現(xiàn)增加管道
9. [mydocker]---通過例子理解存儲驅(qū)動AUFS
10. [mydocker]---通過例子理解chroot 和 pivot_root
11. [mydocker]---一步步實現(xiàn)使用busybox創(chuàng)建容器
12. [mydocker]---一步步實現(xiàn)使用AUFS包裝busybox
13. [mydocker]---一步步實現(xiàn)volume操作
14. [mydocker]---實現(xiàn)保存鏡像
15. [mydocker]---實現(xiàn)容器的后臺運行
16. [mydocker]---實現(xiàn)查看運行中容器
17. [mydocker]---實現(xiàn)查看容器日志
18. [mydocker]---實現(xiàn)進(jìn)入容器Namespace
19. [mydocker]---實現(xiàn)停止容器
20. [mydocker]---實現(xiàn)刪除容器
21. [mydocker]---實現(xiàn)容器層隔離
22. [mydocker]---實現(xiàn)通過容器制作鏡像
23. [mydocker]---實現(xiàn)cp操作
24. [mydocker]---實現(xiàn)容器指定環(huán)境變量
25. [mydocker]---網(wǎng)際協(xié)議IP
26. [mydocker]---網(wǎng)絡(luò)虛擬設(shè)備veth bridge iptables
27. [mydocker]---docker的四種網(wǎng)絡(luò)模型與原理實現(xiàn)(1)
28. [mydocker]---docker的四種網(wǎng)絡(luò)模型與原理實現(xiàn)(2)
29. [mydocker]---容器地址分配
30. [mydocker]---網(wǎng)絡(luò)net/netlink api 使用解析
31. [mydocker]---網(wǎng)絡(luò)實現(xiàn)
32. [mydocker]---網(wǎng)絡(luò)實現(xiàn)測試
