一文搞懂 Linux network namespace
本文首發於我的公眾號 CloudDeveloper(ID: cloud_dev) ,專注於乾貨分享,號內有大量書籍和視訊資源,後臺回覆 「1024」 即可領取,歡迎大家關注,二維碼文末可以掃。
本文通過 IP 命令操作來簡單介紹 network namespace 的基本概念和用法。深入瞭解可以看看我之前寫的兩篇文章 Docker 基礎技術之 Linux namespace 詳解 和 Docker 基礎技術之 Linux namespace 原始碼分析。
和 network namespace 相關的操作的子命令是 ip netns
。
1. ip netns add xx 建立一個 namespace
# ip netns add net1 # ip netns ls net1
2. ip netns exec xx yy 在新 namespace xx 中執行 yy 命令
# ip netns exec net1 ip addr 1: lo: <LOOPBACK> mtu 65536 qdisc noop state DOWN group default qlen 1 link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00 # ip netns exec net1 bash // 在 net1 中開啟一個shell終端 # ip addr // 在net1中的shell終端 1: lo: <LOOPBACK> mtu 65536 qdisc noop state DOWN group default qlen 1 link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00 # exit // 退出net1
上面 bash 不好區分是當前是在哪個 shell,可以採用下面的方法解決:
# ip netns exec net1 /bin/bash --rcfile <(echo "PS1=\"namespace net1> \"") namespace net1> ping www.baidu.com
每個 namespace 在建立的時候會自動建立一個迴環介面 lo
,預設不啟用,可以通過 ip link set lo up
啟用。
3. network namespace 之間的通訊
新建立的 namespace 預設不能和主機網路,以及其他 namespace 通訊。
可以使用 Linux 提供的 veth pair
來完成通訊。下面顯示兩個 namespace 之間通訊的網路拓撲:
3.1 ip link add type veth 建立 veth pair
# ip link add type veth # ip link 3: veth0@veth1: <BROADCAST,MULTICAST,M-DOWN> mtu 1500 qdisc noop state DOWN mode DEFAULT group default qlen 1000 link/ether 1a:53:39:5a:26:12 brd ff:ff:ff:ff:ff:ff 4: veth1@veth0: <BROADCAST,MULTICAST,M-DOWN> mtu 1500 qdisc noop state DOWN mode DEFAULT group default qlen 1000 link/ether 46:df:46:1f:bf:d6 brd ff:ff:ff:ff:ff:ff
使用命令 ip link add xxx type veth peer name yyy
指定 veth pair 的名字。
3.2 ip link set xx netns yy 將 veth xx 加入到 namespace yy 中
# ip link set veth0 netns net0 # ip link set veth1 netns net1 # # ip netns exec net0 ip addr 1: lo: <LOOPBACK> mtu 65536 qdisc noop state DOWN group default qlen 1 link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00 10: veth0@if11: <BROADCAST,MULTICAST> mtu 1500 qdisc noop state DOWN group default qlen 1000 link/ether 1a:53:39:5a:26:12 brd ff:ff:ff:ff:ff:ff link-netnsid 1
3.3 給 veth pair 配上 ip 地址
# ip netns exec net0 ip link set veth0 up # ip netns exec net0 ip addr 1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1 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 10: veth0@if11: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc noqueue state LOWERLAYERDOWN group default qlen 1000 link/ether 1a:53:39:5a:26:12 brd ff:ff:ff:ff:ff:ff link-netnsid 1 # ip netns exec net0 ip addr add 10.1.1.1/24 dev veth0 # ip netns exec net0 ip route 10.1.1.0/24 dev veth0proto kernelscope linksrc 10.1.1.1 linkdown # # ip netns exec net1 ip link set veth1 up # ip netns exec net1 ip addr add 10.1.1.2/24 dev veth1
可以看到,在配完 ip 之後,還自動生成了對應的路由表資訊。
3.4. ping 測試兩個 namespace 的連通性
# ip netns exec net0 ping 10.1.1.2 PING 10.1.1.2 (10.1.1.2) 56(84) bytes of data. 64 bytes from 10.1.1.2: icmp_seq=1 ttl=64 time=0.069 ms 64 bytes from 10.1.1.2: icmp_seq=2 ttl=64 time=0.054 ms 64 bytes from 10.1.1.2: icmp_seq=3 ttl=64 time=0.053 ms 64 bytes from 10.1.1.2: icmp_seq=4 ttl=64 time=0.053 ms
Done!
4. 多個不同 namespace 之間的通訊
2 個 namespace 之間通訊可以藉助 veth pair
,多個 namespace 之間的通訊則可以使用 bridge 來轉接,不然每兩個 namespace 都去配 veth pair
將會是一件麻煩的事。下面就看看如何使用 bridge 來轉接。
拓撲圖如下:
4.1 使用 ip link 和 brctl 建立 bridge
通常 Linux 中和 bridge 有關的操作是使用命令 brctl
( yum install -y bridge-utils
) 。但為了前後照應,這裡都用 ip 相關的命令來操作。
// 建立一個 bridge # ip link add br0 type bridge # ip link set dev br0 up 9: br0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UNKNOWN group default qlen 1000 link/ether 42:55:ed:eb:a0:07 brd ff:ff:ff:ff:ff:ff inet6 fe80::4055:edff:feeb:a007/64 scope link valid_lft forever preferred_lft forever
4.2 建立 veth pair
//(1)建立 3 個 veth pair # ip link add type veth # ip link add type veth # ip link add type veth
4.3 將 veth pair 的一頭掛到 namespace 中,一頭掛到 bridge 上,並設 IP 地址
// (1)配置第 1 個 net0 # ip link set dev veth1 netns net0 # ip netns exec net0 ip link set dev veth1 name eth0 # ip netns exec net0 ip addr add 10.0.1.1/24 dev eth0 # ip netns exec net0 ip link set dev eth0 up # # ip link set dev veth0 master br0 # ip link set dev veth0 up // (2)配置第 2 個 net1 # ip link set dev veth3 netns net1 # ip netns exec net1 ip link set dev veth3 name eth0 # ip netns exec net1 ip addr add 10.0.1.2/24 dev eth0 # ip netns exec net1 ip link set dev eth0 up # # ip link set dev veth2 master br0 # ip link set dev veth2 up // (3)配置第 3 個 net2 # ip link set dev veth5 netns net2 # ip netns exec net2 ip link set dev veth5 name eth0 # ip netns exec net2 ip addr add 10.0.1.3/24 dev eth0 # ip netns exec net2 ip link set dev eth0 up # # ip link set dev veth4 master br0 # ip link set dev veth4 up
這樣之後,竟然通不了,經查閱 參見 ,是因為
原因是因為系統為bridge開啟了iptables功能,導致所有經過br0的資料包都要受iptables裡面規則的限制,而docker為了安全性(我的系統安裝了 docker),將iptables裡面filter表的FORWARD鏈的預設策略設定成了drop,於是所有不符合docker規則的資料包都不會被forward,導致你這種情況ping不通。
解決辦法有兩個,二選一:
- 關閉系統bridge的iptables功能,這樣資料包轉發就不受iptables影響了:echo 0 > /proc/sys/net/bridge/bridge-nf-call-iptables
- 為br0新增一條iptables規則,讓經過br0的包能被forward:iptables -A FORWARD -i br0 -j ACCEPT
第一種方法不確定會不會影響docker,建議用第二種方法。
我採用以下方法解決:
iptables -A FORWARD -i br0 -j ACCEPT
結果:
# ip netns exec net0 ping -c 2 10.0.1.2 PING 10.0.1.2 (10.0.1.2) 56(84) bytes of data. 64 bytes from 10.0.1.2: icmp_seq=1 ttl=64 time=0.071 ms 64 bytes from 10.0.1.2: icmp_seq=2 ttl=64 time=0.072 ms --- 10.0.1.2 ping statistics --- 2 packets transmitted, 2 received, 0% packet loss, time 999ms rtt min/avg/max/mdev = 0.071/0.071/0.072/0.008 ms # ip netns exec net0 ping -c 2 10.0.1.3 PING 10.0.1.3 (10.0.1.3) 56(84) bytes of data. 64 bytes from 10.0.1.3: icmp_seq=1 ttl=64 time=0.071 ms 64 bytes from 10.0.1.3: icmp_seq=2 ttl=64 time=0.087 ms --- 10.0.1.3 ping statistics --- 2 packets transmitted, 2 received, 0% packet loss, time 1000ms rtt min/avg/max/mdev = 0.071/0.079/0.087/0.008 ms
Done!
我的公眾號 CloudDeveloper(ID: cloud_dev) ,號內有大量書籍和視訊資源,後臺回覆 「1024」 即可領取,分享的內容包括但不限於雲端計算虛擬化、容器、OpenStack、K8S、霧計算、網路、工具、SDN、OVS、DPDK、Linux、Go、Python、C/C++程式設計技術等內容,歡迎大家關注。