容器网络工作原理
一句话概括就是通过namespace隔离并通过veth连接到root namespace
namespace
1
| python3 -m http.server 8080
|
查看效果
返回如下
1
2
3
4
5
6
7
8
9
10
11
12
13
14
| <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<title>Directory listing for /</title>
</head>
<body>
<h1>Directory listing for /</h1>
<hr>
<ul>
</ul>
<hr>
</body>
</html>
|
此时再次执行python3 -m http.server 8080
会报错端口已经被占用, 但是可以使用linux的net namespace特性, 在一个隔离的网络命名空间中使用8080端口
- 查看ns1下的所有网络设备, 使用
ip netns exec ns1
命令指定namespace执行命令.
1
2
3
4
| # sudo ip netns exec ns1 ip a
1: lo: <LOOPBACK> mtu 65536 qdisc noop state DOWN group default qlen 1000
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
|
可以看到新的namespace里面有一个默认的回环网卡lo, 状态是DOWN, 需要启动它, 不然新的namespace中的localhost访问不到的
1
| sudo ip netns exec ns1 ip link set dev lo up
|
1
2
3
4
5
6
7
8
| # sudo ip netns exec ns1 ip a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
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
|
然后在新的 namespace 中启动一个http server
1
| sudo ip netns exec ns1 python3 -m http.server 8080
|
这里启动正常, 此时执行 sudo ip netns exec ns1 curl localhost:8080
也可以访问到这个新的namespace中的http服务
vethpair
vethpair是linux的一种虚拟网卡, 成对出现, 一般用于连接两个不同的namespace, 可以理解成一根网线连接着两张网卡. 这里我们需要创建一个vethpair让host空间可以访问到ns1中的http服务.
1
| sudo ip link add dev veth1a type veth peer name veth1b
|
1
2
3
4
5
6
| # ip a
21: veth1b@veth1a: <BROADCAST,MULTICAST,M-DOWN> mtu 1500 qdisc noop state DOWN group default qlen 1000
link/ether 0e:73:a9:02:85:6c brd ff:ff:ff:ff:ff:ff
22: veth1a@veth1b: <BROADCAST,MULTICAST,M-DOWN> mtu 1500 qdisc noop state DOWN group default qlen 1000
link/ether 52:89:67:a9:41:d3 brd ff:ff:ff:ff:ff:ff
|
这里可以看到有连个网络设备 veth1a 和 veth1b.
1
| sudo ip link set dev veth1a up
|
- 将veth1b 设置到 namespace ns1 中, 并启动 veth1b
1
2
| sudo ip link set veth1b netns ns1
sudo ip netns exec ns1 ip link set dev veth1b up
|
1
2
3
4
| # ip a
22: veth1a@if21: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc noqueue state LOWERLAYERDOWN group default qlen 1000
link/ether 52:89:67:a9:41:d3 brd ff:ff:ff:ff:ff:ff link-netns ns1
|
此时 veth1b 在列表中消失了, 这是因为 veth1b 已经在ns1中, 对host不可见了.
1
2
3
4
5
6
7
8
9
10
11
12
| sudo ip netns exec ns1 ip a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
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
21: veth1b@if22: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default qlen 1000
link/ether 0e:73:a9:02:85:6c brd ff:ff:ff:ff:ff:ff link-netnsid 0
inet6 fe80::c73:a9ff:fe02:856c/64 scope link
valid_lft forever preferred_lft forever
|
1
2
| sudo ip address add 172.18.0.10/24 dev veth1a
sudo ip netns exec ns1 ip address add 172.18.0.11/24 dev veth1b
|
1
2
3
4
5
6
7
8
9
10
11
12
13
| 22: veth1a@if21: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default qlen 1000
link/ether 52:89:67:a9:41:d3 brd ff:ff:ff:ff:ff:ff link-netns ns1
inet 172.18.0.10/24 scope global veth1a
valid_lft forever preferred_lft forever
inet6 fe80::5089:67ff:fea9:41d3/64 scope link
valid_lft forever preferred_lft forever
...
21: veth1b@if22: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default qlen 1000
link/ether 0e:73:a9:02:85:6c brd ff:ff:ff:ff:ff:ff link-netnsid 0
inet 172.18.0.11/24 scope global veth1b
valid_lft forever preferred_lft forever
inet6 fe80::c73:a9ff:fe02:856c/64 scope link
valid_lft forever preferred_lft forever
|
此时两张网卡都被设上了ip, 分别是
- host veth1a: 172.18.0.10
- ns1 veth1b: 172.18.0.11
他们已经可以相互ping通了,
1
2
3
4
5
6
7
| ping 172.18.0.11
PING 172.18.0.11 (172.18.0.11) 56(84) bytes of data.
64 bytes from 172.18.0.11: icmp_seq=1 ttl=64 time=0.127 ms
------------
sudo ip netns exec ns1 ping 172.18.0.10
PING 172.18.0.10 (172.18.0.10) 56(84) bytes of data.
64 bytes from 172.18.0.10: icmp_seq=1 ttl=64 time=0.046 ms
|
http 服务也可以通过这个ip访问
1
2
3
4
5
6
7
| curl 172.18.0.11:8080
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
...
|
至此, 一个隔离的网段被创建出来了, 宿主机可以通过 172.18.0.11 访问ns1中的服务, 但此时有一个问题, ns1中的网络访问不了外网, 也不能通过host的ip访问host namespace. 这个问题可以通过创建一个网桥来解决.
网桥
1
2
3
| sudo ip link add dev br1 type bridge
sudo ip address add 172.18.0.1/24 dev br1
sudo ip link set br1 up
|
1
| sudo ip link set dev veth1a master br1
|
1
| sudo ip netns exec ns1 ip route add default via 172.18.0.1
|
1
2
3
| sudo iptables --append FORWARD --in-interface br1 --out-interface eth0 --jump ACCEPT
sudo iptables --append POSTROUTING --table nat --out-interface eth0 --jump MASQUERADE
sudo iptables --append FORWARD --in-interface eth0 --out-interface br1 --jump ACCEPT
|
1
| sudo ip address delete 172.18.0.10/24 dev veth1a
|
此时可以ns1可以连接外网了
清理配置
1
2
3
4
5
6
| sudo iptables --delete FORWARD --in-interface br1 --out-interface eth0 --jump ACCEPT
sudo iptables --delete FORWARD --in-interface eth0 --out-interface br1 --jump ACCEPT
sudo iptables --delete POSTROUTING --table nat --out-interface eth0 --jump MASQUERADE
sudo ip link delete dev br1
sudo ip link delete dev veth1a
sudo ip netns delete ns1
|
参考
本文主要参考 How Do Kubernetes and Docker Create IP Addresses