[mydocker]---Linux Namespace

前言

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 主要用來隔離nodenamedomainname兩個系統(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)程的pid12855,但是在其自己的namespace中它的pid1.

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 IDGroup IDUser 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 IPCPOSIX 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)測試

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容