Docker网络原理

网络虚拟化

虚拟网卡

虚拟网卡是用软件来模拟网络环境。

tun/tap

tun/tap虚拟了一套网络接口,这套接口和物理的接口没有任何区别,可以配置IP,路由流量,不同的是,这个流量只能在主机中流通。

tun和tap是两个相对独立的虚拟网络设备,其中tap模拟了以太网设备,操作以太网帧,tun则模拟了网络层设备,操作IP报文。

使用tun/tap设备的目的是实现把来自协议栈的数据包先交由某个打开了/dev/net/tun字符设备的用户进程处理后,再把数据包发回链路中。

应用程序通过tun设备向外发送数据包后,tun设备就会将数据包通过字符设备发送给VPN程序,VPN收到数据包,会修改后再重新封装成报文(也就是原本的数据包被作为报文体,封装到另一个地址的新数据包中),然后再由协议栈发送到物理网卡,发送出去。

实操

基础命令

  • 添加网卡
bash 复制代码
#创建tap
ip tuntap add dev tap0 mode tap
#创建tun
ip tuntap add dev tun0 mode tun
  • 删除网卡
bash 复制代码
#删除tap
ip tuntap del dev tap0 mode tap
#删除tun
ip tuntap del dev tun0 mode tun
  • 激活网卡
bash 复制代码
ip link set tun0 up
  • 设置IP
bash 复制代码
ip addr add 10.5.0.1/24 dev tun0
  • 查看帮助
bash 复制代码
ip tuntap help

操作

  • 查看当前网卡,其中eth0是物理网卡
bash 复制代码
ifconfig
  • 创建网卡
bash 复制代码
ip tuntap add dev tun0 mod tun
  • 查看网卡信息,新添加的网卡默认是down状态,需要使用-a参数显示
bash 复制代码
ifconfig -a
  • 激活网卡
bash 复制代码
ip link set tun0 up
  • 再次ifconfig就可以看到网卡了,接下来分配IP地址
bash 复制代码
ip addr add 10.5.0.1/24 dev tun0
  • ping该地址是可以ping通的,说明虚拟网卡已经添加好了,接下来使用del删除网卡
bash 复制代码
ip tuntap del dev tun0 mod tun

veth

namespace技术可以将Linux网络资源隔离成多个完全独立的网络环境,但隔离之后,这些独立的网络环境之间无法互相通信,veth就是为了让两个隔离的网络空间可以互相通信。

veth其实不是一个设备,而是一块设备,因此也经常被称为veth pair,要使用veth,要在两个独立的网络名称进行才有意义。

veth通信不需要反复经过多次网络协议栈,这让veth比起tap/tun有更好的性能,veth实现了点对点的虚拟连接,可以通过veth连接两个namespace,如果我们需要将三个或者三个以上的namespace接入同一个二层网络时,就不能使用veth了。

实操

常见命令

  • 添加veth
bash 复制代码
ip link add <veth name> type veth peer name <peer name>
  • 删除veth
bash 复制代码
ip link delete <veth name>
  • 查看veth
bash 复制代码
ip link show
  • 添加ns
bash 复制代码
ip netns add <name>
  • 删除ns
bash 复制代码
ip netns del <name>
  • 执行命令
bash 复制代码
ip netns exec <name> <cmd>
  • 遍历ns
bash 复制代码
ip netns list

操作

  • 查看当前网卡的信息
bash 复制代码
ifconfig
  • 创建2个网络空间
bash 复制代码
ip netns add ns1
ip netns add ns2
ip netns list
  • 创建一个虚拟网卡对
bash 复制代码
ip link add testveth1 type veth peer name testveth2
  • 使用ifconfig -a可以看到网卡多了2个,将网卡挪到不同的命名空间中
bash 复制代码
ip link set testveth1 netns ns1
ip link set testveth2 netns ns2
  • 激活网卡
bash 复制代码
ip netns exec ns1 ip link set testveth1 up
ip netns exec ns2 ip link set testveth2 up
  • 给网卡分配ip地址
bash 复制代码
ip netns exec ns1 ip addr add 10.5.0.1/24 dev testveth1
ip netns exec ns2 ip addr add 10.5.0.2/24 dev testveth2
  • 然后在各自的命名空间里ping对方,可以看到网卡是可以通的
bash 复制代码
ip netns exec ns1 ping 10.5.0.2

虚拟交换机

一台设备不仅仅只需要和另外一台设备进行通信,还需要和很多其他网络设备进行通信,如果还使用这样的方式,就需要十分复杂的网络连线。

所以这里就需要Linux Bridge,LinuxBridge类似于现实世界中的在数据链路层工作的物理交换机,把很多个物理设备桥接到一个局域网中。由brctl命令创建和管理,Linux Bridge创建以后,真实的物理设备(eth0)或者虚拟的设备(veth或者tap)都可以和Linux Bridge配合工作。

网桥是纯软件实现的虚拟交换机,有着和物理交换机相同的功能,例如二层交换,MAC地址学习等,因此我们可以把tun/tap,veth pair设备绑定到网桥上,就像把设备连接到物理交换机一样。

操作

命令

bash 复制代码
#新建一个网桥
brctl addbr <bridge>
#添加一个设备到网桥
brctl addif <bridge> 设备名
#显示当前存在的网桥及其连接的网络端口
brctl show
#启动网桥
ip link set <bridge> up
#删除网桥
ip link set <bridge> down
brctl delbr <bridge>
#或者直接删除网桥
ip link del <bridge>

增加网桥的时候会自动增加一个同名的虚拟网卡在宿主机上,因此我们可以通过ip link命令操作这个虚拟网卡,实际上也是操作网桥,只有当这个虚拟网卡状态处于up的时候,网桥才会转发数据。
操作

  • 创建3个netns,3对veth pair,分别移动端在netns中,另一端连接在网桥上
bash 复制代码
ip netns add ns1
ip netns add ns2
ip netns add ns3
ip link add veth1-ns type veth peer name veth1-br
ip link add veth2-ns type veth peer name veth2-br
ip link add veth3-ns type veth peer name veth3-br
  • 将ns的一端网卡移入命名空间
bash 复制代码
ip link set dev veth1-ns netns ns1
ip link set dev veth2-ns netns ns2
ip link set dev veth3-ns netns ns3
  • 启动网卡,配置ip,开启本地回环
bash 复制代码
netns exec ns1 ip link set veth1-ns up
netns exec ns2 ip link set veth2-ns up
netns exec ns3 ip link set veth3-ns up
ip netns exec ns1 ip addr add 10.100.0.11/24 dev veth1-ns
ip netns exec ns2 ip addr add 10.100.0.12/24 dev veth2-ns
ip netns exec ns3 ip addr add 10.100.0.13/24 dev veth3-ns
  • 创建网桥,给网桥配置ip地址
bash 复制代码
brctl addbr testbr0
ip addr add 10.100.0.1/24 dev testbr0
ifconfig
  • 启动连接到网桥的网卡,将另外一端的网卡插上网桥
bash 复制代码
ip link set veth1-br up
ip link set veth1-br up
ip link set veth1-br up
brctl addif testbr0 veth1-br
brctl addif testbr0 veth2-br
brctl addif testbr0 veth3-br
  • 测试网络连通性
bash 复制代码
ip netns exec ns3 ping 10.100.0.11

虚拟组网VxLan

物理网络的拓扑结构是固定的,但云原生时代的分布式系统逻辑拓扑结构变动频繁,所以就出现了SDN,核心思路是在物理网络之上再构造一层虚拟化的网络,位于下层的物理网络被称为underkay,上层的逻辑网络被称为overlay。

vlan
  • 交换机工作在数据链路层,插在同一个交换机的网络设备组成了一个网络,这个网络之间依靠MAC地址进行通信,这个L2网络也是一个广播域,同属于一个广播域的两个设备需要通信,一设备需要向网络中所有设备发送请求信息,只有对应MAC设备的地址才是真正的接收方,但数据帧却传遍整个网络。
    这就会导致网络整体带宽被占用,还有信息安全风险等问题。
  • VLAN就是虚拟局域网,是一个将物理局域网在逻辑上划分成多个广播域的技术,通过在交换机上配置vlan,可以实现在同一vlan用户在数据链路层进行互相访问。
    解决的问题
  • 用于限制广播域,广播域被限制在一个局域网内,节省了带宽。
  • 增强了安全性,一个vlan内的用户不能和其他vlan中的用户直接通信,如果不同vlan要通信,则需要经过路由器或者三层交换机。
  • 用局域网可以划分不同的用户到不同的工作组,同一工作组的用户不必局限于某一固定的物理范围,网络构建和维护更灵活。
    缺点
  • 随着虚拟化的发展,一台服务器会承载多态虚拟机,比如说公有云可能更多,而vlan最多只支持4000多个vlan(协议里只给了12个bit位),逐渐无法满足需求。
  • 公有云提供商的业务要求将实体网络租借给不同的用户,可能会出现IP地址,MAC地址的重叠(由于虚拟机/容器的模板复制,手动配置或者软生成,造成同样的IP地址或者MAC地址),同一个网络里出现两个相同的IP或者MAC,交换机可能会不知道将报文转发给谁。
  • 每台虚拟机都有唯一的MAC地址,为了保证集群中的所有虚拟机都可以正常通信,交换机必须保存每台虚拟机的MAC地址,导致交换机中的MAC表很庞大。
vxlan

在传统vlan网络中,共享同一底层L2网段的VLAN不能超过4096个,只有12个bit用于对vlan id进行编码,而vxlan定义了8个字节的vxlan header,引入类似vlan id的标识,称为VNI,由24bit组成,总共是1600多万个,满足更高的需求。

vxlan是基于L3网络构建的虚拟L2网络,是一种overlay网络,不关心底层的物理网络拓扑,它将数据链路层报文封装到UDP包中,只要它能承载udp数据包,就可以在远端网段进行通信。

每个vxlan节点的出站数据链路层报文都会被捕获,封装成UDP数据包,并通过网络层网络发送到目标vxlan节点,当数据链路层报文到达vxlan节点时,就从UDP数据包中提取,并注入目标设备的网络接口,这是隧道技术。

MacVLan

MacVLan是一种L2网络虚拟化技术,它允许同一个物理网卡(eth0)被切分为多个虚拟接口,每个虚拟接口都有自己的MAC地址和IP的地址。

MACVLAN在物理设备之上,网络栈之下生成了多个虚拟的Device,每一个Device都有一个MAC地址,新增Device的操作本质上是在系统内核中注册了一个收发特定数据包的回调函数,每个回调函数都能对一个MAC地址的数据包进行想要,当物理设备收到数据包时,会先根据MAC地址进行一次判断,确定要交给哪一个Device处理。

IPVLan

ipvlan和macvlan非常相似,但又存在不同,ipvlan的子接口上并不拥有独立的MAC地址,所有共享父接口MAC地址的子接口拥有自己独立的IP。

共享MAC地址会影响DHCP相关的操作,如果虚拟机,容器需要通过DHCP获取网络配置,要确保它们在DHCP请求中使用各自独立的ClientID,DHCP服务器会根据请求中的ClientID而非MAC地址来分配IP地址。

docker bridge

下图是docker container的桥接模式

  • 首先,docker daemon利用veth pair技术,在宿主机上创建两个虚拟网络接口设备,假设为veth0和veth1,而veth pair技术的特性可以保证无论哪一个veth接收到网络报文,都会将报文传输给另外一方。
  • docker daemon将veth0附加到docker daemon创建的docker0网桥上,保证宿主机的网络报文可以发送给veth0
  • docker daemon将veth1添加到docker container所属的namespace下,并改名为eth0,这样,保证宿主机的网络报文发送给veth0,则会被eth0接收,实现宿主机和容器网络的连通性。

操作

veth pair

  • 运行一个busybox容器
bash 复制代码
docker run --name test1 -itd busybox
  • 在容器内部查看pair的信息
bash 复制代码
docker exec -it test1 sh
ip link
  • 在宿主机上执行ip link,找到对应的id为32的,这样两个pair就对应上了

容器与宿主机/外界主机通信

bridge桥接模式,实现了容器到宿主机乃至于其他及其的网络连通性,但用于宿主机的IP地址和veth pair的IP地址均不在同一个网段,仅仅依靠veth pair和namespace技术是无法让宿主机以外的网络主动发现docker container的存在,所以,为了让外界感知到容器的存在,docker使用NAT技术。
外界网络访问容器

  • 外界访问宿主机的IP和宿主机的端口port1
  • 当宿主机接收到请求后,由于NAT存在,会将该请求的IP和目的端口port1进行转换,转换为容器的IP和容器的端口
  • 由于宿主机认识容器的IP,故可以将请求发送给veth pair
  • veth pair的veth0将请求发送给容器内部的eth0,最终交给内部服务去处理。
    容器访问外界网络
  • docker 容器内部进程获取宿主机以外的服务的IP和port,container发起请求,容器的独立网络环境保证了请求中报文的源IP为容器的IP,另外Linux内核会自动为进程分配一个可用源端口
  • 请求通过容器内部eth0发送只veth pair的另外一端,到达veth0,也就是网桥。
  • 网桥开启了数据报转发功能,故将请求发送给宿主机的eth0处
  • 宿主机处理请求时,用SNAT技术对请求进行源地址IP转换,即将请求中的源地址IP转换为宿主机eth0的IP
    但这样就会存在一个问题,对于docker container内部主动向外发送的请求,当外界响应请求时,响应报文的目的IP地址肯定是docker宿主机的IP地址,如果宿主机要转发给容器,需要用到iptables规则(也就是在宿主机上发往docker0网桥的数据报文,如果该数据报文所处的连接已经建立的化,则无条件接受,并由Linux内核将其发送到原来的连接上,即回到docker容器内部)
  • 用iptables命令查看NAR转换表
bash 复制代码
iptables -t nat -nvL

容器网络命名空间

nsenter是一个可用在指定进程的命名空间下运行指定程序的命令,位于util-linux包中,一个典型的用途就是接入容器的网络命名空间,并且可用使用宿主机的命令来调试容器网络

bash 复制代码
nsenter [options] <program> [<arg1...>]

-t, --target < pid > 进入目标进程的命名空间

-i, --ipc[=< file >] 进入 IPC 空间

-m, --mount[=< file >] 进入 Mount 空间

-n, --net[=< file >] 进入 Net 空间

-p, --pid[=< file >] 进入 Pid 空间

-u, --uts[=< file >] 进入 UTS 空间

-U, --user[=< file >] 进入用户空间

-V, --version 版本查看

实操
  • 首先启动一个容器,查看容器的信息里的SandboxKey,这个目录就是容器所在的网络命名空间
bash 复制代码
docker run -itd --name test1 -p 81:80 busybox
docker inspect test1 | grep Sand -A 10
  • 通过nsenter接入命名空间,查看IP地址
bash 复制代码
nsenter --net=SandboxKey目录 sh
ip a
  • 通过shell进入容器,可用看到两个打印的ip地址是一样的
bash 复制代码
docker exec -it test1 sh
ip a
相关推荐
寒秋花开曾相惜2 小时前
(学习笔记)3.9 异质的数据结构(3.9.1 结构)
c语言·网络·数据结构·数据库·笔记·学习
春日见2 小时前
从底层思维3分钟彻底弄清卷积神经网络CNN
人工智能·深度学习·神经网络·计算机视觉·docker·cnn·计算机外设
wudl55662 小时前
MySQL 8.0.42 Docker 开发部署手册
数据库·mysql·docker
IT一氪2 小时前
K8s Admin:一个轻量级的多集群 Kubernetes 管理平台
云原生·容器·kubernetes
大新新大浩浩2 小时前
Deerflow部署-X86架构-在ubuntu2204操作系统上使用docker模式部署
docker·容器·架构
魔都吴所谓2 小时前
【Linux】Ubuntu22.04 Docker+四大数据库(挂载本地)一键安装脚本
linux·数据库·docker
大道V至简3 小时前
解决docker apt安装缓慢,切换国内源
docker
常利兵3 小时前
Spring Boot 实现网络限速:让流量“收放自如”
网络·spring boot·后端
上海云盾安全满满3 小时前
服务器很卡,是CC攻击造成的吗
运维·服务器·网络