network namespace用來隔離網絡設備, IP地址, 端口等. 每一個namespace將會有本身獨立的網絡棧,路由表,防火牆規則,socket等。node
每一個新的network namespace默認有一個本地環回接口,除了lo接口外,全部的其餘網絡設備(物理/虛擬網絡接口,網橋等)只能屬於一個network namespace。每一個socket也只能屬於一個network namespace。 git
當新的network namespace被建立時,lo接口默認是關閉的,須要本身手動啓動起github
標記爲"local devices"的設備不能從一個namespace移動到另外一個namespace,好比loopback, bridge, ppp等,咱們能夠經過ethtool -k命令來查看設備的netns-local屬性。docker
#這裏「on」表示該設備不能被移動到其餘network namespace dev@ubuntu:~$ ethtool -k lo|grep netns-local netns-local: on [fixed]
本篇全部例子都在ubuntu-server-x86_64 16.04下執行經過shell
本示例將演示如何建立新的network namespace並同外面的namespace進行通訊。ubuntu
#--------------------------第一個shell窗口---------------------- #記錄默認network namespace ID dev@ubuntu:~$ readlink /proc/$$/ns/net net:[4026531957] #建立新的network namespace dev@ubuntu:~$ sudo unshare --uts --net /bin/bash root@ubuntu:~# hostname container001 root@ubuntu:~# exec bash root@container001:~# readlink /proc/$$/ns/net net:[4026532478] #運行ifconfig啥都沒有 root@container001:~# ifconfig root@container001:~# #啓動lo (這裏不詳細介紹ip這個tool的用法,請參考man ip) root@container001:~# ip link set lo up root@container001:~# ifconfig 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:1 RX bytes:0 (0.0 B) TX bytes:0 (0.0 B) root@container001:~# 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.070 ms 64 bytes from 127.0.0.1: icmp_seq=2 ttl=64 time=0.015 ms #獲取當前bash進程的PID root@container001:~# echo $$ 15812 #--------------------------第二個shell窗口---------------------- #建立新的虛擬以太網設備,讓兩個namespace能通信 dev@ubuntu:~$ sudo ip link add veth0 type veth peer name veth1 #將veth1移動到上面第一個窗口中的namespace #這裏15812是上面bash的PID dev@ubuntu:~$ sudo ip link set veth1 netns 15812 #爲veth0分配IP並啓動veth0 dev@ubuntu:~$ sudo ip address add dev veth0 192.168.8.1/24 dev@ubuntu:~$ sudo ip link set veth0 up dev@ubuntu:~$ ifconfig veth0 veth0 Link encap:Ethernet HWaddr 9a:4d:d5:96:b5:36 inet addr:192.168.8.1 Bcast:0.0.0.0 Mask:255.255.255.0 inet6 addr: fe80::984d:d5ff:fe96:b536/64 Scope:Link UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1 RX packets:8 errors:0 dropped:0 overruns:0 frame:0 TX packets:8 errors:0 dropped:0 overruns:0 carrier:0 collisions:0 txqueuelen:1000 RX bytes:648 (648.0 B) TX bytes:648 (648.0 B) #--------------------------第一個shell窗口---------------------- #爲veth1分配IP地址並啓動它 root@container001:~# ip address add dev veth1 192.168.8.2/24 root@container001:~# ip link set veth1 up root@container001:~# ifconfig veth1 veth1 Link encap:Ethernet HWaddr 6a:dc:59:79:3c:8b inet addr:192.168.8.2 Bcast:0.0.0.0 Mask:255.255.255.0 inet6 addr: fe80::68dc:59ff:fe79:3c8b/64 Scope:Link UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1 RX packets:8 errors:0 dropped:0 overruns:0 frame:0 TX packets:8 errors:0 dropped:0 overruns:0 carrier:0 collisions:0 txqueuelen:1000 RX bytes:648 (648.0 B) TX bytes:648 (648.0 B) #鏈接成功 root@container001:~# ping 192.168.8.1 PING 192.168.8.1 (192.168.8.1) 56(84) bytes of data. 64 bytes from 192.168.8.1: icmp_seq=1 ttl=64 time=0.098 ms 64 bytes from 192.168.8.1: icmp_seq=2 ttl=64 time=0.023 ms
到目前爲止,兩個namespace之間能夠網絡通訊了,但在container001裏仍是不能訪問外網。下面將經過NAT的方式讓container001可以上外網。這部份內容徹底是網絡相關的知識,跟namespace已經沒什麼關係了。安全
#--------------------------第二個shell窗口---------------------- #回到上面示例中的第二個窗口 #確認IP forward是否已經開通,這裏1表示開通了 #若是你的機器上是0,請運行這個命令將它改成1: sudo sysctl -w net.ipv4.ip_forward=1 dev@ubuntu:~$ cat /proc/sys/net/ipv4/ip_forward 1 #添加NAT規則,這裏ens32是機器上鍊接外網的網卡 #關於iptables和nat都比較複雜,這裏不作解釋 dev@ubuntu:~$ sudo iptables -t nat -A POSTROUTING -o ens32 -j MASQUERADE #--------------------------第一個shell窗口---------------------- #回到第一個窗口,添加默認網關 root@container001:~# ip route add default via 192.168.8.1 root@container001:~# route -n Kernel IP routing table Destination Gateway Genmask Flags Metric Ref Use Iface 0.0.0.0 192.168.8.1 0.0.0.0 UG 0 0 0 veth1 192.168.8.0 0.0.0.0 255.255.255.0 U 0 0 0 veth1 #這樣就能夠訪問外網了 #因爲測試環境的限制,因此採用下面的方式檢測網絡是否暢通 #若是網絡沒有什麼限制的話,隨便ping一個外部的IP測試就能夠了 root@container001:~# curl -I www.google.com HTTP/1.1 200 OK Date: Fri, 15 Jul 2016 08:12:03 GMT
network namespace的概念比較簡單,但如何作好網絡的隔離和連通卻比較難,包括性能和安全相關的考慮,須要很好的Linux網絡知識。後續在介紹docker網絡管理的時候會對Linux網絡作一個更詳細的介紹。bash
在單獨操做network namespace時,ip netns是一個很方便的工具,而且它能夠給namespace取一個名字,而後根據名字來操做namespace。那麼給namespace取名字而且根據名字來管理namespace裏面的進程是怎麼實現的呢?請看下面的腳本(也能夠直接看它的源代碼):網絡
#開始以前,獲取一下默認network namespace的ID dev@ubuntu:~$ readlink /proc/$$/ns/net net:[4026531957] #建立一個用於綁定network namespace的文件, #ip netns將全部的文件放到了目錄/var/run/netns下, #因此咱們這裏重用這個目錄,而且建立一個咱們本身的文件netnamespace1 dev@ubuntu:~$ sudo mkdir -p /var/run/netns dev@ubuntu:~$ sudo touch /var/run/netns/netnamespace1 #建立新的network namespace,並在新的namespace中啓動新的bash dev@ubuntu:~$ sudo unshare --net bash #查看新的namespace ID root@ubuntu:~# readlink /proc/$$/ns/net net:[4026532448] #bind當前bash的namespace文件到上面建立的文件上 root@ubuntu:~# mount --bind /proc/$$/ns/net /var/run/netns/netnamespace1 #經過ls -i命令能夠看到文件netnamespace1的inode號和namespace的編號相同,說明綁定成功 root@ubuntu:~# ls -i /var/run/netns/netnamespace1 4026532448 /var/run/netns/netnamespace1 #退出新建立的bash root@ubuntu:~# exit exit #能夠看出netnamespace1的inode沒變,說明咱們使用了bind mount後 #雖然新的namespace中已經沒有進程了,但這個新的namespace還存在 dev@ubuntu:~$ ls -i /var/run/netns/netnamespace1 4026532448 /var/run/netns/netnamespace1 #上面的這一系列操做等同於執行了命令: ip netns add netnamespace1 #下面的nsenter命令等同於執行了命令: ip netns exec netnamespace1 bash #咱們能夠經過nsenter命令再建立一個新的bash,並將它加入netnamespace1所關聯的namespace(net:[4026532448]) dev@ubuntu:~$ sudo nsenter --net=/var/run/netns/netnamespace1 bash root@ubuntu:~# readlink /proc/$$/ns/net net:[4026532448]
從上面能夠看出,給namespace取名字其實就是建立一個文件,而後經過mount --bind將新建立的namespace文件和該文件綁定,就算該namespace裏的全部進程都退出了,內核仍是會保留該namespace,之後咱們還能夠經過這個綁定的文件來加入該namespace。curl
經過這種辦法,咱們也能夠給其餘類型的namespace取名字(有些類型的 namespace可能有些特殊,本人沒有一個一個的試過)。