本文首发于我的公众号 Linux云计算网络(id: cloud_dev),专注于干货分享,号内有 10T 书籍和视频资源,后台回复 「1024」 即可领取,欢迎大家关注,二维码文末可以扫。

来源:https://www.cnblogs.com/wipan/p/9220615.html

在上篇文章中,我们已经探讨了 VxLAN 的概念和基本原理,本文就基于 Linux 对 VxLAN 做一个实践。如果有相关概念不懂的可以先看那篇文章。

01 Linux 上对 VxLAN 的支持

首先,来看下 Linux 对 VxLAN 的支持,Linux 对 VxLAN 协议的支持时间并不久,2012 年 Stephen Hemminger 才把相关的工作合并到 kernel 中,并最终出现在 kernel 3.7.0 版本。为了稳定性和很多的功能,你可以会看到某些软件推荐在 3.9.0 或者 3.10.0 以后版本的 kernel 上使用 VxLAN。

这些版本的 Linux 对 VxLAN 的支持已经完备,支持单播和组播,IPv4 和 IPv6。利用 man 查看 ip 的 link 子命令,可以查看是否有 vxlan type,如下:

man ip link

以下的实验在如下的环境中完成:

  • 操作系统版本:CentOS Linux release 7.4.1708 (Core)
  • 内核版本:3.10.0-693.2.2.el7.x86_64
  • 云虚机vm1 eth0网络接口IP 172.31.0.106,云虚机vm2 eth0网络接口IP 172.31.0.107

02 实验1:最简单的点对点 VxLAN

创建简单的点对点 VxLAN 环境非常简单。如下图所示,只需要在两个机器(物理机或者虚拟机都可以,本实验中是云上的虚拟机环境)中各创建一个 vxlan 类型的网络接口即可,vxlan 类型的接口 vxlan1 可以作为的 VTEP。

在上面的环境中,注意我们将 vxlan 网络接口配置上 IP 地址,在 10.0.0.0/24 网段内。在 IP 地址分配后,Linux 系统的路由表就会创建一条路由,去往 10.0.0.0/24 网段的报文走网络接口 vxlan1 出去。vm1 上去往 10.0.0.0/24 的报文,在 vxlan1 上会做 VxLAN 封装,内层地址是10.0.0.106,外层地址是172.31.0.106。VxLAN 报文通过物理网络达到对端 vm2 上的 VETP vxlan1,在 vm2 的 vxlan1 接口上做VxLAN 协议的解封装,从而结束整个过程。

上图是一个物理上的示意图,在逻辑上形成的 VxLAN overlay 网络环境如下图,虚线部分示意出来的 Overlay Network 和 VxLAN Tunnel 都是逻辑上的概念。如果有容器和虚机被接入逻辑上的 Overlay 网络 10.0.0.0/24,它们完全不用感知底层物理网络,看起来对端是和自己在同一个二层环境里,就是像是在 VTEP 设备的上面直接构建了一条 VxLAN Tunnel,把 Overlay 网络里的网络接口直接在二层打通。

具体的配置只需要 3 条命令。如下,在 vm1 上执行如下命令:

# ip link add vxlan1 type vxlan id 1 remote 172.31.0.107 dstport 4789 dev eth0
# ip link set vxlan1 up
# ip addr add 10.0.0.106/24 dev vxlan1

上面的第一条命令创建了一个 Linux 上类型为 vxlan 的网络接口,名为 vxlan1。

  • id: VNI标识是1。
  • remote: 作为一个VTEP设备来封装和解封VXLAN报文,需要知道将封装好的VXLAN报文发送到哪个对端VTEP。Linux上可以利用group指定组播组地址,或者利用remote指定对端单播地址。在实验的云环境中默认不支持组播,这里利用remote指定点对点的对端IP地址为172.31.0.107。
  • dstport: 指定目的端口为4789。因为当Linux内核3.7版本首次实现VXLAN时,UDP端口还并没有规定下来。很多厂商利用了8472这个端口,Linux也采用了相同的端口。后来IANA分配了4789作为VXLAN的目的UDP端口。如果你需要使用IANA端口,需要用dstport指定。
  • dev: 指定VTEP通过哪个物理device来通信,这里是使用eth0。

第二条命令让 vxlan1 接口 up 起来。第三条命令给设备分配 IP 地址 10.0.0.106, 子网掩码为 24 (255.255.255.0)。

在 vm2 上,利用类似方法创建名为 vxlan1 的网络接口。

# ip link add vxlan1 type vxlan id 1 remote 172.31.0.106 dstport 4789 dev eth0
# ip link set vxlan1 up
# ip addr add 10.0.0.107/24 dev vxlan1

以上简单的命令就完成了所有配置。用 ifconfig 可以看到 vxlan1 网络接口,如下:

# ifconfig vxlan1
vxlan1: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1450
inet 10.0.0.106 netmask 255.255.255.0 broadcast 0.0.0.0
ether 22:2d:c4:f0:c7:29 txqueuelen 1000 (Ethernet)
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

看下 vm1 的如下路由表,去往目的网段 10.0.0.0/24 的报文将走 vxlan1 接口。

# route -n
Kernel IP routing table
Destination Gateway Genmask Flags Metric Ref Use Iface
0.0.0.0 172.31.0.253 0.0.0.0 UG 0 0 0 eth0
10.0.0.0 0.0.0.0 255.255.255.0 U 0 0 0 vxlan1
169.254.0.0 0.0.0.0 255.255.0.0 U 1002 0 0 eth0
172.31.0.0 0.0.0.0 255.255.255.0 U 0 0 0 eth0

在 vm1 上 ping overlay 网络的对端 IP 地址 10.0.0.107,可以 ping 通。

# ping 10.0.0.107 -c 3
PING 10.0.0.107 (10.0.0.107) 56(84) bytes of data.
bytes from 10.0.0.107: icmp_seq=1 ttl=64 time=0.447 ms
bytes from 10.0.0.107: icmp_seq=2 ttl=64 time=0.361 ms
bytes from 10.0.0.107: icmp_seq=3 ttl=64 time=0.394 ms --- 10.0.0.107 ping statistics ---
packets transmitted, 3 received, 0% packet loss, time 2000ms
rtt min/avg/max/mdev = 0.361/0.400/0.447/0.042 ms

在 ping 包的同时,用 tcpdump 抓 vm1 eth0 网卡的包。因为报文到达 eth0 前经过了网络接口 vxlan1, 完成了 VxLAN 的封装,所以在抓包结果里应该能看到完整的 VxLAN 报文。

抓包时可以只抓和对端 172.31.0.107 通信的报文,如下:

# tcpdump -i eth0 host 172.31.0.107 -s0 -v -w vxlan_vni_1.pcap

抓包结果如下,wireshark 自动将 UDP 目的端口为 4789 的报文识别成 VxLAN 报文,直接显示内层的报文,protocol 为 ICMP 协议。如果使用 Linux 默认接口 8472,显示的应该是 UDP 协议,还需要修改 wireshark 的协议设置,让其识别成 VxLAN。

03 实验2:容器跨主机通信

上面最简单的点对点 VxLAN 实验只是个简答的演示,没有太多实际工程意义,本节用容器通信来演示一个更加完整的场景。

场景描述:在 vm1 和 vm2 上各部署一个 docker 容器,默认情况下,一个容器宿主机上的容器能够直接用私网 IP 地址通信,因为它们利用一个网桥接在一起。而不同宿主机上的容器无法直接用私网 IP 地址通信。k8s 等 docker 部署软件中的网络组建实际上完成了这部分工作,让不同宿主机的容器能够直接通信。本节使用原生 docker,以及在宿主机上自建的 vxlan 网络接口,来打通不同宿主机上容器,让它们可以直接利用内网IP通信。

注意:因为实验在云上的虚拟机上完成,上面提到的容器宿主机,用的是云上的虚拟机。容器宿主机也可以是物理机,实验效果不变。

3.1 准备 docker 容器

安装 docker 的过程不展开了,docker 官方文档有详细的描述。在 Linux 安装了 docker 后,可以看到多了一个 docker0 的网络接口,默认在 172.17.0.0/16 网段。这个是连接本地多个容器的网桥。

# ifconfig docker0
docker0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1450
inet 172.17.0.1 netmask 255.255.0.0 broadcast 172.17.255.255
ether 02:42:44:e8:74:e8 txqueuelen 0 (Ethernet)
RX packets 6548 bytes 360176 (351.7 KiB)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 7489 bytes 40249455 (38.3 MiB)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0

使用默认 172.17.0.0/16 网段,docker 容器的 IP 地址都会从 172.17.0.2 开始分配。为了能使 vm1 和 vm2 上的容器使用不同的IP地址,在利用 docker run 启动容器的时候需要能自定义 IP 地址,而利用 --ip 参数自定义 IP 地址的功能只能在自定网络中支持,所以先创建一个自定义网络,指定网段 172.18.0.0/16。

# docker network create --subnet 172.18.0.0/16 mynetwork
3231f89d69f6b3fbe2550392ebe4d00daa3d19e251f66ed2d81f61f2b9184362
# docker network ls
NETWORK ID NAME DRIVER SCOPE
1cb284a6cb33 bridge bridge local
069538be0246 host host local
3231f89d69f6 mynetwork bridge local
0b7934996485 none null local

利用 docker network ls 查看,可以看到一个新的 bridge 网络被创建,名称为我指定的 mynetwork。利用 ifconfig 可以看到多了一个网络接口,名字不是 dockerXX,而直接以 br 开头,是一个网桥。

br-3231f89d69f6: flags=4099<UP,BROADCAST,MULTICAST>  mtu 1500
inet 172.18.0.1 netmask 255.255.0.0 broadcast 172.18.255.255
ether 02:42:97:22:a5:f9 txqueuelen 0 (Ethernet)
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

创建一个新的容器,如下:

# docker run -itd --net mynetwork --ip 172.18.0.2 centos
16bbaeaaebfccd2a497e3284600f5c0ce230e89678e0ff92f6f4b738c6349f8d
  • --net指定自定义网络
  • --ip指定IP地址
  • centos指定image

查看容器 ID 和状态,并且登录 SHELL,如下:

# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
16bbaeaaebfc centos "/bin/bash" 2 minutes ago Up 2 minutes condescending_swartz
# docker exec -it 16bbaeaaebfc /bin/bash
[root@16bbaeaaebfc /]# ifconfig
bash: ifconfig: command not found

注意:docker 为了创建容器的效率,通常都用了 size 很小的 image,意味着很多常用工具需要安装,比如 centos image 里面的 ifconfig。可以利用 yum whatprovides ifconfig 命令查看 ifconfig 输入哪个包,查到属于 net-tools-2.0-0.22.20131004git.el7.x86_64包,直接用 yum install net-tools -y 安装即可。再执行 ifconfig 命令,可以看到容器 eth0 网卡的 IP 地址为 172.18.0.2。

[root@16bbaeaaebfc /]# ifconfig eth0
eth0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500
inet 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 3319 bytes 19221325 (18.3 MiB)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 2015 bytes 132903 (129.7 KiB)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0

在 vm2 上执行同样的操作,在创建新容器的时候,指定 IP 地址为 172.18.0.3,容器的环境即准备完毕。在 vm1 上的 centos 容器中 ping 172.18.0.3,和预期一致,是无法 ping 通的。

[root@16bbaeaaebfc /]# ping 172.18.0.3 -c 2
PING 172.18.0.3 (172.18.0.3) 56(84) bytes of data.
From 172.18.0.2 icmp_seq=1 Destination Host Unreachable
From 172.18.0.2 icmp_seq=2 Destination Host Unreachable --- 172.18.0.3 ping statistics ---
packets transmitted, 0 received, +2 errors, 100% packet loss, time 1000ms
pipe 2
[root@16bbaeaaebfc /]# ping 172.18.0.1 -c 2
PING 172.18.0.1 (172.18.0.1) 56(84) bytes of data.
bytes from 172.18.0.1: icmp_seq=1 ttl=64 time=0.060 ms
bytes from 172.18.0.1: icmp_seq=2 ttl=64 time=0.079 ms --- 172.18.0.1 ping statistics ---
packets transmitted, 2 received, 0% packet loss, time 999ms
rtt min/avg/max/mdev = 0.060/0.069/0.079/0.012 ms

3.2 创建 VxLAN 接口接入 docker 网桥

先来梳理下docker及docker容器在Linux宿主机网络模块中做的操作,梳理清楚之后会发现打通不同宿主机上docker容器的方法非常简单。从宿主Linux系统的视角看操作系统中的网络设备,总结如下:

  • docker0接口:网桥,在安装完docker后默认被创建,网段是172.17.0.0/16,网桥的默认IP地址为172.17.0.1。
  • br-xxxx接口:网桥,在创建完自定义docker网络完被创建,网段是被用户指定的172.18.0.0/16,网桥的默认IP地址为172.18.0.1。
  • vethxxxx接口:veth网络接口,在创建一个具体的docker容器后被创建,如果有N个运行的容器,就会有N个veth网络接口。容器中的eth0接口和宿主机的veth网络接口是一个veth网络对,Linux上的veth接口作为一个端口连接入docker网桥,如docker0或其他自定义网桥。这也是为什么一个宿主机上的docker容器能够默认通信的原因,因为它们创建后就被接入到了同一个网桥上。

为了方便理解,在默认网段172.17.0.0/16中创建2个容器,在自定义网段中上文已经创建了1个docker容器,利用btctl查看网桥及其接口,如下:

# brctl show
bridge name bridge id STP enabled interfaces
br-3231f89d69f6 8000.02429722a5f9 no veth2fa4c50
docker0 8000.024244e874e8 no vethc7cd982
            vethd3d0c18

从上面的输出结果可以看到,默认网桥docker0上,有vethc7cd982和vethd3d0c18两个网络接口接入。在定义网络网桥br-3231f89d69f6一个端口上,veth2fa4c50网络接口接入。这三个veth网络接口分别连接着一个docker容器的eth0网络接口,连接着同一个网桥的veth网络接口vethc7cd982和vethd3d0c18默认二层能通。

有了上面的梳理和本文第一节VXLAN网络接口的基础知识,想必打通不同宿主机上docker容器的方法也比较清晰了。思路就是在两个容器宿主机上各创建一个VXLAN接口,并且将VXLAN接口接入docker网桥的端口上,如下图:

有了VXLAN接口的连接后,从vm1上docker容器发出的包到达docker网桥后,可以从网桥的VXLAN接口出去,从而报文在VETP(VXLAN接口)处被封装成VXLAN报文,再从物理网络上到达对端VETP所在的主机vm2。对端VTEP能正确解包VXLAN报文的话,随后即可将报文通过vm2上的docker网桥送到上层的docker容器中。

具体的配置如下,在vm1上:

# ip link add vxlan_docker type vxlan id 200 remote 172.31.0.107 dstport 4789 dev eth0
# ip link set vxlan_docker up
# brctl addif br-3231f89d69f6 vxlan_docker
  • 第一条命令创建VNI为200的VXLAN网络接口,名称为vxlan_docker,参数设置和场景1中的各个参数类似。
  • 第三条命令把新创建的VXLAN接口vxlan_docker接入到docker网桥br-3231f89d69f6中。

在vm2上,输入如下命令:

# ip link add vxlan_docker type vxlan id 200 remote 172.31.0.106 dstport 4789 dev eth0
# ip link set vxlan_docker up
# brctl addif br-f4b35af34313 vxlan_docker

在vm1的docker容器上再ping 172.18.0.3,结果如下,ping可以通。注意RTT的时间,ping 172.18.0.3的RTT在10^(-1)毫秒级别,ping 172.18.0.1的RTT在10^(-2)毫秒级别,前者是走物理网络的延迟,后者是协议栈的延迟,两者有量级上的差别。

# docker exec -it 16bbaeaaebfc ifconfig eth0
eth0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500
inet 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 3431 bytes 19230266 (18.3 MiB)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 2132 bytes 141908 (138.5 KiB)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0 # docker exec -it 16bbaeaaebfc ping 172.18.0.3 -c 2
PING 172.18.0.3 (172.18.0.3) 56(84) bytes of data.
bytes from 172.18.0.3: icmp_seq=1 ttl=64 time=0.544 ms
bytes from 172.18.0.3: icmp_seq=2 ttl=64 time=0.396 ms --- 172.18.0.3 ping statistics ---
packets transmitted, 2 received, 0% packet loss, time 1001ms
rtt min/avg/max/mdev = 0.396/0.470/0.544/0.074 ms
#
# docker exec -it 16bbaeaaebfc ping 172.18.0.1 -c 2
PING 172.18.0.1 (172.18.0.1) 56(84) bytes of data.
bytes from 172.18.0.1: icmp_seq=1 ttl=64 time=0.072 ms
bytes from 172.18.0.1: icmp_seq=2 ttl=64 time=0.072 ms --- 172.18.0.1 ping statistics ---
packets transmitted, 2 received, 0% packet loss, time 999ms
rtt min/avg/max/mdev = 0.072/0.072/0.072/0.000 ms

04 总结

最后说明,本节只是为了演示 Linux VxLAN 的用于而构造了这个简单但没有实际用处的场景,在跨主机环境的容器之间利用 VxLAN 从二层打通。在工程中做容器跨主机通信时有很多方面需要考虑,也有很多项目在致力于这方面的研究。比如 Flannel,通过给每台宿主机分配一个子网的方式为容器提供虚拟网络,它基于 Linux TUN/TAP,使用 UDP 封装 IP 包来实现 L3 overlay 网络,并借助 etcd 维护网络的分配情况。


公众号后台回复“加群”,带你进入高手如云交流群

我的公众号 「Linux云计算网络」(id: cloud_dev) ,号内有 10T 书籍和视频资源,后台回复 「1024」 即可领取,分享的内容包括但不限于 Linux、网络、云计算虚拟化、容器Docker、OpenStack、Kubernetes、工具、SDN、OVS、DPDK、Go、Python、C/C++编程技术等内容,欢迎大家关注。

Linux 下实践 VxLAN:虚拟机和 Docker 场景的更多相关文章

  1. [转帖]Linux 下实践 VxLAN:虚拟机和 Docker 场景

    Linux 下实践 VxLAN:虚拟机和 Docker 场景 https://www.cnblogs.com/bakari/p/11264520.html 实践了下 没问题 作者写的很perfect ...

  2. Linux 下实践 VxLAN

    本文首发于我的公众号 Linux云计算网络(id: cloud_dev),专注于干货分享,号内有 10T 书籍和视频资源,后台回复 「1024」 即可领取,欢迎大家关注,二维码文末可以扫. 来源:ht ...

  3. Linux 下非root用户使用docker

    Linux 下非root用户使用docker 通常我们使用linux系统的时候,最好是不要直接使用root账号,但是使用Docker的时候,默认又是不能使用非root用户的,关于原因,官方说法如下: ...

  4. VXLAN学习之路-结合VRF在Linux中实践VXLAN网络

    一.概述 近期在在搞网络安全HCIE.CISP的认证的事,顺便将VXLAN技术再次系统的学习一下,学习过程中看到云原生实验室里的一篇文章,就是关于VXLAN在Linux系统中的实践,感觉文章写得很好, ...

  5. Windows和Linux下与VMware虚拟机通过内网IP通讯

    首先分两种情况:一种是你的电脑已经在一个内网的环境下且有额外的内网IP地址,和另一种只是想给自己电脑上的虚拟机分配个内网IP来通讯. ①有可用的内网IP 找到一个空闲的IP地址(这里以192.168. ...

  6. Linux下的java虚拟机性能监控与故障处理命令

    java包中提供了很多监控JVM的工具类,作为java程序员必须得掌握常用的几个工具,下面是几个常用的JVM性能监控与故障处理工具的介绍与使用. 1.jstack 该命令用于生成当前时刻虚拟机的线程快 ...

  7. linux下实践导入导出MySQL数据库

    一.导出: 用mysqldump命令行 命令格式 mysqldump -u 用户名 -p 数据库名 > 数据库名.sql 范例: mysqldump -u root -p abc > ab ...

  8. Linux 下虚拟机——Virtual Box

    下面介绍几款可以在Linux下运行的虚拟机 VMware,VirtualBox, QEMU, Xen.(其中我只用过VirtualBox 和Xen, 我是新手,其他两个没时间折腾,所以下面的资料都几乎 ...

  9. linux下安装VMware出错:Gtk-Message: Failed to load module "canberra-gtk-module"解决方法

    最近又有兴趣在linux下搭建个虚拟机,于是去找了个VMWorkstation,安装的过程中报了两个错误 Gtk-Message: Failed to load module "pk-gtk ...

随机推荐

  1. spring常见十大异常

    一.找不到配置文件的异常 [plain] view plaincopy org.springframework.beans.factory.BeanDefinitionStoreException:  ...

  2. Spring_two

    Spring_two 基于注解的IOC配置 准备工作(参考上一篇) ); 接口的实现类AccountDaoImpl.java修改 /** * 账户的持久层实现类 */ @Repository(&quo ...

  3. iOS开发如何避免安全隐患

    现在很多iOS的APP没有做任何的安全防范措施,导致存在很多安全隐患和事故,今天我们来聊聊iOS开发人员平时怎么做才更安全. 一.网络方面 用抓包工具可以抓取手机通信接口的数据.以Charles为例, ...

  4. CSS3之响应式布局及其应用

    使用 Media 属性前需添加兼容移动设备优先代码 <meta name=”viewport”content=”width=device-width,initial-scale=1.0,maxi ...

  5. Js笛卡尔乘积

    self.getDescartesSku = function (selSaleProp, i, nowLst, allALst) {         if (selSaleProp.length = ...

  6. Maven项目读取resources下文件的路径

    要取编译后的路径,而不是你看到的src/main/resources的路径.如下: URL url = 类名.class.getClassLoader().getResource("conf ...

  7. Codeforces 755B:PolandBall and Game(map+思维)

    http://codeforces.com/problemset/problem/755/B 题意:A可以喊出n个字符串,B可以喊出m个字符串,如果一个字符串之前被喊过,那么它再也不能喊了,A先喊,最 ...

  8. python接口自动化(三十三)-python自动发邮件总结及实例说明番外篇——下(详解)

    简介 发邮件前我们需要了解的是邮件是怎么一个形式去发送到对方手上的,通俗点来说就是你写好一封信,然后装进信封,写上地址,贴上邮票,然后就近找个邮局,把信仍进去,其他的就不关心了,只是关心时间,而电子邮 ...

  9. Skyline WEB端开发5——添加标签后移动

    针对于标签或者模型,在skyline上可以进行移动.可以让一个模型可以像无人机似的飞行,或者描述从一个点到另一个点的飞行轨迹. 话不多说,直接上干货. 第一步 添加标签 参考网址:https://ww ...

  10. 【深入浅出-JVM】(8):TLAB

    概念 TLAB(Thread Local Allocation Buffer)线程本地分配缓冲区(线程私有分配区,私有分配,公共查看),占用 Eden 区(缺省 Eden 的1%),默认开启,JVM ...