一, 什么是容器网络栈
所谓容器能看见的 "网络栈", 被隔离在自己的 Network Namespace 当中
网卡(network interface)
回环设备(loopback device)
路由表(Routing Table)
iptables 规则
当然 , 容器可以直接声明使用宿主机的网络栈:-net=host(不开启 Network Namespace), 这样可以为容器提供良好的网络性能, 但是也引入了共享网络资源的问题, 比如端口冲突.
大多数情况下, 我们希望容器能使用自己的网络栈, 拥有属于自己的 IP 和端口
二, 容器如何和其他不同 Network Namespace 的容器交互 ?
将容器看做一台主机, 两台主机通信直接的办法, 就是用一根网线连接, 如果是多台主机之间通信, 就需要用网络将它们连接到一台交换机上
在 Linux 中, 能够起到虚拟交换机作用的设备是网桥(Bridge)
网桥是一个工作在数据链路层的设备, 功能是根据 Mac 地址学习来将数据包转发到网桥不同端口上, 为了实现上述上的, Docker 项目默认在宿主机上创建了一个 docker0 的网桥, 连接在上面的容器, 可以通过它来通信
三, 如何把容器连接到 docker0 网络上
需要一种叫 Veth Pair 的虚拟设备. 当 Veth Pair 被创建出来后, 总是以两张虚拟网卡 (Veth Peer) 的形式成对出现 , 而且从其中一个网卡出的数据包, 可以直接出现在它对应的另一张网卡上, 即使两张卡在不同的 Network Namespace 里
这就使 Veth Pair 常常被用作连接不同的 Network Namspace 的网线
四, 示例演示
下面将在宿主机启动两个容器, 分别是 nginx-1 和 nginx-2 演示, nginx-1 容器访问 nginx-2 是如何通信的
1. 运行一个 nginx-1 容器
docker run -d --name=nginx-1 nginx
2. 查看 nginx-1 的网络设备
- root@d5bfaab9222e:/# ifconfig
- eth0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500
- .NET 172.18.0.2 netmask 255.255.0.0 broadcast 172.18.255.255
- ether 02:42:ac:12:00:02 txqueuelen 0 (Ethernet)
- RX packets 3520 bytes 8701343 (8.2 MiB)
- RX errors 0 dropped 0 overruns 0 frame 0
- TX packets 3010 bytes 210777 (205.8 KiB)
- TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
- lo: flags=73<UP,LOOPBACK,RUNNING> mtu 65536
- .NET 127.0.0.1 netmask 255.0.0.0
- loop txqueuelen 1000 (Local Loopback)
- RX packets 0 bytes 0 (0.0 B)
- RX errors 0 dropped 0 overruns 0 frame 0
- TX packets 0 bytes 0 (0.0 B)
- TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
eth0 的网卡, 是 Veth Pari 设备的其中一端
3. 查看 nginx-1 的路由表, 通过 route 命令查看
- root@d5bfaab9222e:/# route
- Kernel IP routing table
- Destination Gateway Genmask Flags Metric Ref Use Iface
- default 172.18.0.1 0.0.0.0 UG 0 0 0 eth0
- 172.18.0.0 0.0.0.0 255.255.0.0 U 0 0 0 eth0
所有 172.18.0.0/16 网段的请求, 都交给 eth0 处理, 这是 Veth Pair 设备的一端, 另一端在宿主机上
5. 查看宿主机的网桥
通过 brctl show 查看
- root@VM-0-8-Ubuntu:/home/Ubuntu# brctl show
- bridge name bridge id STP enabled interfaces
- docker0 8000.0242c635ddb8 no veth4d1c130
可以看到, docker0 上插入上 veth4d1c130 网卡
6. 再运行一个 nginx-2
docker run -d --name=nginx-2 nginx
7. 再查看宿主机网桥
- root@VM-0-8-Ubuntu:/home/Ubuntu# brctl show
- bridge name bridge id STP enabled interfaces
- docker0 8000.0242c635ddb8 no veth4d1c130
- vetha9356e9
可以看到, docker0 插上了两张网卡 veth4d1c130, vetha9356e9
此时, 当容器 nginx-1(172.18.0.2)ping 容器 nginx-2(172.18.0.3)的时候, 就会发现两个容器是可以连通的
五, nginx-1 能访问 nginx-2 的原理是什么?
被限制在 Network Namespace 的容器进程, 是通过 Veth Pair 设备 + 宿主机网桥的方式, 实现跟其它容器的数据交换
1.nginx-1 访问 nginx-2 时, IP 地址会匹配到 nginx-1 容器的每二条路由规则
172.18.0.0 0.0.0.0 255.255.0.0 U 0 0 0 eth0
凡是匹配这条规则 的 IP 包, 应该经过本机的 eth0 网卡, 通过二层网络发往目的主机
2. 要通过二层网络到达 nginx-2 , 需要有 172.18.0.3 的 Mac 地址, 找到
nginx-1 容器需要通过 eth0 网卡发送一个 ARP 广播, 通过 IP 来查看对应的 Mac 地址
这个 eth0 网卡, 是一个 Veth Pair, 它的一端在 nginx-1 容器的 Network Namespace, 另一端位于宿主机上(Host Namespace), 并且插入在了宿主机的 docker0 网桥上
一旦虚假网卡 (veth4d1c130) 被插在网桥 (docker0) 上, 调用网络的数据包会被转发给对应的网桥, 所以 ARC 请求会被发给 docker0
3.docker0 转发数据到到相应的 nginx-2
docker0 会继续扮演二层交换机的角色, 根据数据包的上的 Mac 地址(nginx-2 的 Mac 地址), 在它的 CAM 表里查对应的端口, 会找到 vetha9356e9, 然后将数据包发往这个端口, 这样数据包就到了 nginx-2 容器的 Network Namespace 里
nginx-2 看到它自己的 eth0 网卡上出现了流入的数据包, 然后对请求进行处理, 再返回响应给 nginx-1
可以打开 iptables 的 TRACE 功能查看数据包的传输过程, 通过 tail -f /var/log/syslog
- # iptables -t raw -A OUTPUT -p icmp -j TRACE
- # iptables -t raw -A PREROUTING -p icmp -j TRACE
原理图如下
来源: https://www.cnblogs.com/chenqionghe/p/11699466.html