理解network namespace
network namespace用來隔離網絡環境,「在network namespace中,網絡設備、端口、套接字、網絡協議棧、路由表、防火墻規則等都是獨立的」。
因network namespace中具有獨立的網絡協議棧,因此每個network namespace中都有一個lo接口,但lo接口默認未啟動,需要手動啟動起來。
#?-n或--net選項用于創建network?namespace
$?sudo?unshare?-n?/bin/bash#?默認未啟動lo
root@longshuai-vm:/home/longshuai#?ip?a
1:?lo:?<LOOPBACK>?mtu?65536?qdisc?noop?state?DOWN?group?default?qlen?1000link/loopback?00:00:00:00:00:00?brd?00:00:00:00:00:00#?將之啟動
root@longshuai-vm:/home/longshuai#?ip?link?set?lo?up
root@longshuai-vm:/home/longshuai#?ip?a
1:?lo:?<LOOPBACK,UP,LOWER_UP>?mtu?65536?qdisc?noqueue?state?UNKNOWN?group?default?qlen?1000link/loopback?00:00:00:00:00:00?brd?00:00:00:00:00:00inet?127.0.0.1/8?scope?host?lovalid_lft?forever?preferred_lft?foreverinet6?::1/128?scope?host?valid_lft?forever?preferred_lft?forever
讓某個network namespace和root network namespace或其他network namespace之間保持通信是一個非常常見的需求,這一般通過veth虛擬設備實現。veth類型的虛擬設備由一對虛擬的eth網卡設備組成,像管道一樣,一端寫入的數據總會從另一端流出,從一端讀取的數據一定來自另一端。
用戶可以將veth的其中一端放在某個network namespace中,另一端保留在root network namespace中。這樣就可以讓用戶創建的network namespace和宿主機通信。
例如:
#####?在第一個shell窗口
#?創建network?namespace?ns1
$?sudo?unshare?-n?/bin/bash#?查看該network?namespace的進程號
root@longshuai-vm:/home/longshuai#?echo?$$
53091#####?在第二個shell窗口
#?創建veth設備
$?sudo?ip?link?add?veth0?type?veth?peer?name?veth1#?創建之后,就有了一對虛擬的eth設備
$?ip?a?|?grep?veth
3:?veth1@veth0:?<BROADCAST,MULTICAST,M-DOWN>?mtu?1500?qdisc?noop?state?DOWN?group?default?qlen?1000
4:?veth0@veth1:?<BROADCAST,MULTICAST,M-DOWN>?mtu?1500?qdisc?noop?state?DOWN?group?default?qlen?1000#?veth0準備留在root?network?namespace中
#?為veth0設置IP地址并啟動
$?sudo?ip?a?add?dev?veth0?192.168.10.10/24
$?sudo?ip?link?set?veth0?up
$?ip?a?s?veth0
4:?veth0@veth1:?<NO-CARRIER,BROADCAST,MULTICAST,UP,M-DOWN>?mtu?1500?qdisc?noqueue?state?LOWERLAYERDOWN?group?default?qlen?1000link/ether?36:30:54:ba:d3:aa?brd?ff:ff:ff:ff:ff:ffinet?192.168.10.10/24?scope?global?veth0valid_lft?forever?preferred_lft?forever#?最后,將veth1移動到剛才新建的network?namespace?ns1中
#?ip?link?set?xxx?netns?[?PID|NETNS_NAME?]
$?sudo?ip?link?set?veth1?netns?53091#?注:
#?并不是所有的網絡設備都能在network?namespace之間移動,
#?ethtool?-k?<interface>輸出的結果中,netns-local為on的表示不能移動
$?ethtool?-k?lo?|?grep?netns
netns-local:?on?[fixed]
$?ethtool?-k?ens32?|?grep?netns??????
netns-local:?off?[fixed]
$?ethtool?-k?veth1?|?grep?netns?????????????????????????
netns-local:?off?[fixed]#####?在ns1中啟動veth1并設置IP地址
root@longshuai-vm:/home/longshuai#?ip?link?set?dev?veth1?uproot@longshuai-vm:/home/longshuai#?ip?a?add?dev?veth1?192.168.10.20/24root@longshuai-vm:/home/longshuai#?ip?a?s?veth1
3:?veth1@if4:?<BROADCAST,MULTICAST,UP,LOWER_UP>?mtu?1500?qdisc?noqueue?state?UP?group?default?qlen?1000link/ether?22:a8:4b:5b:55:4d?brd?ff:ff:ff:ff:ff:ff?link-netnsid?0inet?192.168.10.20/24?scope?global?veth1valid_lft?forever?preferred_lft?foreverinet6?fe80::20a8:4bff:fe5b:554d/64?scope?link?valid_lft?forever?preferred_lft?forever?#?互ping,測試兩端是否能通信,例如在ns1測試
root@longshuai-vm:/home/longshuai#?ping?192.168.10.10
PING?192.168.10.10?(192.168.10.10)?56(84)?bytes?of?data.
64?bytes?from?192.168.10.10:?icmp_seq=1?ttl=64?time=0.076?ms
64?bytes?from?192.168.10.10:?icmp_seq=2?ttl=64?time=0.189?ms
但現在ns1還不能和公網通信。解決這個問題也很簡單,在root network namespace中開啟轉發并設置SNAT,在ns1中添加默認路由即可。
#####?在root?network?namespace中執行
$?sudo?sysctl?-w?net.ipv4.ip_forward=1
$?sudo?iptables?-t?nat?-A?POSTROUTING?-o?ens32?-j?MASQUERADE#####?在network?namespace?ns1中執行
$?route?add?default?gw?192.168.10.10
$?ping?www.baidu.com
PING?www.baidu.com?(36.152.44.95)?56(84)?bytes?of?data.
64?bytes?from?36.152.44.95:?icmp_seq=1?ttl=127?time=53.4?ms
64?bytes?from?36.152.44.95:?icmp_seq=2?ttl=127?time=51.0?ms
持久化的network namespace
正常情況下,當namespace中的所有進程都退出后,namespace也會隨之銷毀。但有時候需要讓namespace即使沒有進程在其中運行也依然有效,即namespace的持久化。例如,創建了network namespace后,想要讓network namespace中的網絡配置一直生效。
實際上,無論是network namespace還是其他類型的namespace,「當通過mount bind為某個namespace的namespace文件(/proc/$$/ns/xxx
)進行了bind掛載,這個namespace將成為持久化的namespace,即使namespace中的第一個進程或所有進程都退出了,namespace也不會立即銷毀,之后還可以通過nsenter重新進入該namespace」。
其實方式很簡單,直接在unshare創建namespace時的對應長選項上指定一個已存在的文件即可(注:不允許在短選項上指定文件名)。
unshare:?-m,?--mount[=<file>]???unshare?mounts?namespace-u,?--uts[=<file>]?????unshare?UTS?namespace?(hostname?etc)-i,?--ipc[=<file>]?????unshare?System?V?IPC?namespace-n,?--net[=<file>]?????unshare?network?namespace-p,?--pid[=<file>]?????unshare?pid?namespace-U,?--user[=<file>]????unshare?user?namespace-C,?--cgroup[=<file>]??unshare?cgroup?namespace
例如,想要讓network namespace持久化:
#?對于network?namespace持久化,通常指定/var/run/netns/NAME作為持久化文件
$?sudo?mkdir?-p?/var/run/netns
$?sudo?touch?/var/run/netns/ns1#?也可以不在選項--net上指定文件,而是在network?namespace內部
#?將/var/run/netns/ns1通過mount?bind掛載到/proc/$$/ns/net上
$?sudo?unshare?--net=/var/run/netns/ns1?/bin/bash#?現在/var/run/netns/ns1和/proc/$$/ns/net是同一個network?namespace
root@longshuai-vm:/home/longshuai#?ls?-i?/var/run/netns/ns1?
4026532587?/var/run/netns/ns1root@longshuai-vm:/home/longshuai#?readlink?/proc/$$/ns/net
net:[4026532587]#?退出ns1
root@longshuai-vm:/home/longshuai#?exit
exit#?/var/run/netns/ns1仍然指向net?inode
$?ls?-i?/var/run/netns/ns1?
4026532587?/var/run/netns/ns1#?nsenter重新進入ns1
$?sudo?nsenter?--net=/var/run/netns/ns1?/bin/bash
root@longshuai-vm:/home/longshuai#?hostname?-I
192.168.10.20
ip netns
ip netns
命令用于管理network namespace。
ip netns將network namespace與/var/run/netns/NAME相關聯,將/var/run/netns下的每一個NAME作為其所管理的每一個network namespace的名稱。
ip netns將/etc/netns/NAME/作為對應network namespace的全局網絡配置文件的目錄,查找它之后才會查找/etc/目錄。
例如,如果想要為netns名為ns1的network namespace單獨設置DNS,可創建/etc/netns/ns1/resolv.conf,并將DNS相關配置寫入該文件,當該文件不存在時才查找/etc/resolv.conf。
ip netns創建network namespace時,同時會創建mount namespace,以便將網絡相關配置文件/etc/netns/NAME/xxx掛載到對應的/etc/xxx。
-
「ip netns add NAME」
創建名為NAME的network namespace,同時會關聯/var/run/netns/NAME文件,如果文件不存在,則會自動創建$?sudo?ip?netns?add?ns2 $?ls?-i?/var/run/netns/ns2 4026532759?/var/run/netns/ns2
-
「ip netns list」
列出/var/run/netns下的所有network namespace#?帶有id的,表示正在運行(即有進程尚未退出)的network?namespace以及它的ID號 #?ID會自動分配,從0開始,后面通過ip?netns命令也可以自己設置ID號 $?ip?netns?list ns2 ns1?(id:?0)
-
「ip netns attach NAME PID」
將PID對應的network namespace關聯到/var/run/netns/NAME(不存在時會自動創建),使得該network namespace就像是被ip netns創建一樣,之后它將受ip netns管理#?在第一個窗口中執行 #?使用unshare而非ip?netns創建一個network?namespace $?sudo?unshare?-n?/bin/bash root@longshuai-vm:/home/longshuai#?echo?$$ 5094#?在第二個窗口中執行 #?現在這個network?namespace就像是由ip?netns創建一樣 $?sudo?ip?netns?attach?ns3?5094
-
「ip [-all] netns delete [ NAME ]」
刪除/vaer/run/netns/下指定的network namespace,如果指定了--all,則刪除/var/run/netns下所有的network namespace。注意,它同時會卸載mount bind的掛載點/var/run/netns/NAME并刪除該文件。$?ip?netns?list ns3 ns2 ns1?(id:?0)$?sudo?ip?netns?del?ns3 $?ls?/var/run/netns ns1??ns2$?sudo?ip?--all?netns?del $?ls?/var/run/netns
-
「ip netns set NAME NETNSID」
為/var/run/netns/NAME對應的network namespace設置ID號。$?sudo?ip?netns?add?ns1 $?ip?netns?list ns1$?sudo?ip?netns?set?ns1?11 $?ip?netns?list ns1?(id:?11)
-
「ip netns identify [PID]」
根據PID,輸出該PID所在的network namespace的netns name。$?ip?netns?list ns1?(id:?11) $?sudo?nsenter?--net=/var/run/netns/ns1?/bin/bash root@longshuai-vm:/home/longshuai#?echo?$$ 5298#?在另一個窗口中查詢進程PID=5298在哪一個network?namespace中 $?sudo?ip?netns?identify?5298 ns1
-
「ip netns pids NAME」
輸出networ namespace中當前正在運行的所有進程PID$?sudo?ip?netns?pids?ns1 5298
-
「ip [-all] netns exec [ NAME ] cmd ...」
在指定的network namespace中執行命令CMD。如果指定了--all選項,則CMD命令將在/var/run/netns/下的所有network namespace中都執行。$?sudo?ip?netns?exec?ns1?ip?link?set?lo?up $?sudo?ip?netns?exec?ns1?ping?www.baidu.com
注:如果/etc/netns/NAME下有配置文件,執行ip netns exec命令時會自動將其bind到/etc/下對應的配置文件上。此時還需注意,systemd管理的/etc/resolv.conf是一個軟鏈接,ip netns直接bind時會失敗,將其移除后再創建普通文件類型的/etc/resolv.conf才可bind成功。
$?sudo?mkdir?-p?/etc/netns/ns1
$?echo?'nameserver?8.8.8.8'?|?sudo?tee?/etc/netns/ns1/resolv.conf$?sudo?ip?netns?exec?ns1?ping?www.baidu.com
Bind?/etc/netns/ns1/resolv.conf?->?/etc/resolv.conf?failed:?No?such?file?or?directory
PING?www.baidu.com?(36.152.44.96)?56(84)?bytes?of?data.
64?bytes?from?36.152.44.96?(36.152.44.96):?icmp_seq=1?ttl=127?time=36.2?ms
64?bytes?from?36.152.44.96?(36.152.44.96):?icmp_seq=2?ttl=127?time=37.5?ms#?創建普通文件類型的/etc/resolv.conf
$?readlink?/etc/resolv.conf
../run/systemd/resolve/stub-resolv.conf
$?sudo?mv?/etc/resolv.conf{,.bak}
$?sudo?touch?/etc/resolv.conf
$?sudo?ip?netns?exec?ns1?dig?-t?A?www.baidu.com
......
;www.baidu.com.?????????????????IN??????A;;?ANSWER?SECTION:
www.baidu.com.??????????581?????IN??????CNAME???www.a.shifen.com.
www.a.shifen.com.???????51??????IN??????CNAME???www.wshifen.com.
www.wshifen.com.????????70??????IN??????A???????104.193.88.77
www.wshifen.com.????????70??????IN??????A???????104.193.88.123;;?Query?time:?215?msec
;;?SERVER:?8.8.8.8#53(8.8.8.8)??????#?已經成功使用8.8.8.8作為nameserver
;;?WHEN:?Sun?Oct?11?16:58:51?CST?2020
;;?MSG?SIZE??rcvd:?127m