Linux network namespace 实践

Docker 中使用 linux namespace 机制来隔离不同容器的网络配置。

简而言之,一般情况下每个容器有一个独立的 network namespace,这个 namespace 和主机的网络配置是隔离的,而他们之间通过一条虚拟链路以及网桥相连。

接下来的过程通过命令行模拟了这个网络建立的过程,以便粗浅的理解 linux namespace 的原理。

目标

最终网络拓扑如下图所示。在 Docker 中,网桥的名字是 docker0。

操作

首先创建网桥:

1
2
3
4
5
6
7
8
# 新建一个网桥br0,模仿docker0
brctl addbr br0

# 关闭 STP (生成树协议),因为只有拓扑结构很简单,用不到,关闭可以提升性能
brctl stp br0 off

# 为网桥设置IP地址
ifconfig br0 192.168.10.1/24 up

然后创建一个名为“ns1”的 network namespace:

1
ip netns add ns1

创建一个虚拟链路,两端分别对应 ns0 中的一个 interface (veth-ns1) 和主机上的一个 interface (br0.1),显然这条链路连接了 ns0 和 default namespace

1
2
3
4
5
# 增加一个pair虚拟网卡,注意其中的veth类型
ip link add veth-ns1 type veth peer name br0.1

# 把 veth-ns1 放进 namespace ns1 中,这样容器中就会有一个新的网卡了
ip link set veth-ns1 netns ns1

进入“容器”(namespace ns0)内部,做一些操作:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
# 首先用这条命令开启一个 ns0 的 shell
ip netns exec ns0 sh

# 看一下当前的接口
$ ls /sys/class/net
veth-ns1

# 改成 eth0 是个好主意(容器外会冲突,容器内就不会了)
$ ip link set dev veth-ns1 name eth0

# 给 eth0 分配一个 IP 并开启
$ ifconfig eth0 192.168.10.11/24 up

# 开启 loopback
$ ip link set dev lo up

$ ifconfig
eth0 Link encap:Ethernet HWaddr 7a:41:f4:e4:44:92
inet addr:192.168.10.11 Bcast:192.168.10.255 Mask:255.255.255.0
UP BROADCAST MULTICAST MTU:1500 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:1000
RX bytes:0 (0.0 B) TX bytes:0 (0.0 B)

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)

# 最后别忘了设置一下路由
ip route add default via 192.168.10.1

最后,回到 default namespace,把链路这头的 br0.1 打开

1
2
3
4
5
# 开启这个 interface
ifconfig br0.1 up

# 并把它接到网桥上(千万别忘了这步!)
brctl addif br0 br0.1

好了,这时候已经能 ping 通“容器”的IP地址了:

1
2
3
root@eric-vm:/home/eric# ping 192.168.10.11
PING 192.168.10.11 (192.168.10.11) 56(84) bytes of data.
64 bytes from 192.168.10.11: icmp_seq=1 ttl=64 time=0.034 ms

如果想访问外网,还要额外做两件事——把你的主机配置成一台工作在 NAT 模式的路由器:

1
2
3
4
5
# 开启端口转发
echo 1 > /proc/sys/net/ipv4/ip_forward

# 添加一条 SNAT 规则(eth0 是连接 Internet 的物理网卡)
iptables -t nat -A POSTROUTING -s 192.168.10.0/24 -o eth0 -j MASQUERADE

通过同样的方法你可以创建更多的 network namespace,这里就不重复了。