自己动手实现 Docker bridge network
source link: https://hiberabyss.github.io/2018/02/02/docker-bridge-network-practice/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
最近详细了解了 Docker 的网桥网络的工作原理, 便想一步一步地实现 Docker 地网桥网络.
Docker 网桥网络工作原理
Docker 的网络实现主要会用到以下功能:
- Network Namespace: 用于隔离容器和宿主机之间地网络;
- Veth 设备对: 用于连接宿主机和容器, 每个容器都会有一对 Veth 设备, 一个在容器内, 一个在宿主机内;
- 网桥: 通过网桥可以很方便地管理宿主机上的多个 veth 设备, 同时实现不同容器之间地互联;
- Iptables/NetFilter: SNAT 以实现容器内对外网的访问; 实现容器地端口映射等;
详细原理如下图所示 (转自这篇博客):
创建 Network Namespace
通过命令 sudo ip netns add ns1
即可创建名为 ns1 的 network namespace, 我们可以通过下面地命令查看当前系统中已有的 network namespace:
[ec2-user@ip-10-24-254-11 ~]$ sudo ls -1 /var/run/netns
ns1
Note: 我们可以通过命令 sudo ip netns del ns1
来删除之前创建地 network namespace.
创建 veth 设备对
通过下面的命令创建:
[ec2-user@ip-10-24-254-11 ~]$ sudo ip link add veth0 type veth peer name veth1
[ec2-user@ip-10-24-254-11 ~]$ ip addr show | grep veth
311: veth1@veth0: <BROADCAST,MULTICAST,M-DOWN> mtu 1500 qdisc noop state DOWN group default qlen 1000
312: veth0@veth1: <BROADCAST,MULTICAST,M-DOWN> mtu 1500 qdisc noop state DOWN group default qlen 1000
我们把 veth1 移动到 ns1 namespace 里: sudo ip link set veth1 netns ns1
. 现在我们在宿主机中就看不到 veth1 了:
[ec2-user@ip-10-24-254-11 ~]$ ip addr show | grep veth
312: veth0@if311: <BROADCAST,MULTICAST> mtu 1500 qdisc noop state DOWN group default qlen 1000
当我们切换到 ns1 就可以查看到 veth1 了:
[ec2-user@ip-10-24-254-11 ~]$ sudo ip netns exec ns1 bash
[root@ip-10-24-254-11 ec2-user]# ip addr show
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
311: veth1@if312: <BROADCAST,MULTICAST> mtu 1500 qdisc noop state DOWN group default qlen 1000
link/ether 3e:a0:ce:a7:8f:4c brd ff:ff:ff:ff:ff:ff link-netnsid 0
Note: 可以通过 sudo ip del link veth0
来删除这个设备对.
创建网桥并实现 veth1 和宿主机的互联
我们在系统默认 namespace 创建网桥 br-demo
, 并将 veth0 加入到网桥中:
[ec2-user@ip-10-24-254-11 ~]$ sudo brctl addbr br-demo
[ec2-user@ip-10-24-254-11 ~]$ sudo brctl addif br-demo veth0
[ec2-user@ip-10-24-254-11 ~]$ sudo brctl show | grep 'br-demo'
br-demo 8000.0aa51a826228 no veth0
我们给网桥添加 ip 地址: sudo ifconfig br-demo 172.8.0.1
. 同时启动 veth0 sudo ip link set dev veth0 up
. 再登录进 ns1 namespace 给 veth1 设置 ip 地址:
[root@ip-10-24-254-11 ec2-user]# ifconfig veth1 172.8.0.8
[root@ip-10-24-254-11 ec2-user]# ifconfig veth1 172.8.0.8
这时我们便可以 ping 通 br-demo 的地址了:
[root@ip-10-24-254-11 ec2-user]# ping -c 1 172.8.0.1
PING 172.8.0.1 (172.8.0.1) 56(84) bytes of data.
64 bytes from 172.8.0.1: icmp_seq=1 ttl=255 time=0.044 ms
给 veth1 添加外网访问
在宿主机上编辑 iptables , 添加以下规则:
sudo iptables -t filter -A FORWARD -i br-demo ! -o br-demo -j ACCEPT
sudo iptables -t filter -A FORWARD -i br-demo -o br-demo -j ACCEPT
sudo iptables -t filter -A FORWARD -o br-demo -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
sudo iptables -t nat -A POSTROUTING -s 172.8.0.0/16 ! -o br-demo -j MASQUERADE
在 ns1 namespace 里测试是否可以连通外网:
[root@ip-10-24-254-11 ec2-user]# ping baidu.com -c1
PING baidu.com (111.13.101.208) 56(84) bytes of data.
64 bytes from 111.13.101.208: icmp_seq=1 ttl=39 time=258 ms
映射 ns1 内部端口到宿主机
我们现在 ns1 内部通过 python 启动一个简单地 http server:
[root@ip-10-24-254-11 ec2-user]# python -m SimpleHTTPServer
Serving HTTP on 0.0.0.0 port 8000 ...
在宿主机上我们可以通过 veth1 的 ip 地址访问这个服务:
[ec2-user@ip-10-24-254-11 ~]$ curl -I 172.8.0.8:8000
HTTP/1.0 200 OK
Server: SimpleHTTP/0.6 Python/2.7.12
Date: Mon, 05 Feb 2018 05:54:10 GMT
Content-type: text/html; charset=UTF-8
Content-Length: 1718
从别的机器直接访问宿主机映射后的端口
把下面地规则加入到 iptalbe 里, 我们便可以通过宿主机的 8088 端口访问到这个 service 了:
[ec2-user@ip-10-24-254-11 ~]$ sudo iptables -t nat -A PREROUTING -p tcp -m tcp --dport 8088 -j DNAT --to-destination 172.8.0.8:8000
[ec2-user@ip-10-24-254-11 ~]$ sudo iptables -t filter -A FORWARD -p tcp -m tcp --dport 8000 -j ACCEPT
# need to be in another machine
centos@ip-10-24-255-93:~ · 09:50 AM Mon Feb 05 ·
!56 $ curl -I 10.24.254.11:8088
HTTP/1.0 200 OK
Server: SimpleHTTP/0.6 Python/2.7.12
Date: Mon, 05 Feb 2018 09:51:40 GMT
Content-type: text/html; charset=UTF-8
Content-Length: 1718
注意这里必须要在别地机器上访问宿主机的 8088 端口!
直接在宿主机上访问映射后的端口
添加如下两条 iptables 规则后即可直接在本机地址上访问:
[ec2-user@ip-10-24-254-11 ~]$ sudo iptables -t nat -A OUTPUT -p tcp -m tcp --dport 8088 -j DNAT --to-destination 172.8.0.8:8000
[ec2-user@ip-10-24-254-11 ~]$ sudo iptables -t nat -A POSTROUTING -p tcp -m tcp --dport 8000 -j MASQUERADE
[ec2-user@ip-10-24-254-11 ~]$ curl -I 127.0.0.1:8088
HTTP/1.0 200 OK
Server: SimpleHTTP/0.6 Python/2.7.12
Date: Tue, 06 Feb 2018 02:33:42 GMT
Content-type: text/html; charset=UTF-8
Content-Length: 1718
References
Recommend
About Joyk
Aggregate valuable and interesting links.
Joyk means Joy of geeK