在容器中搭建简单的Vxlan隧道

现在,Docker 已经成为了一个非常主流的虚拟化技术,它集合了 Linux 中的许多虚拟化技术,如 Namespace、cgroup 和 AUFS 等等,所以我们可以使用 Docker 搭建一个开箱即用的虚拟化容器。但是,Docker 网路在很多时候依旧不能满足应用场景中的需求,这就需要我们对 Docker 中的网络进行自定义了。

这篇博客就是关于位于不同虚拟机中的两个容器实现 vxlan 通信的实验。

拓扑和环境

首先讲一下实验拓扑吧,它大概长下面这个样子。

一共两台虚拟机,在两台虚拟机之间使用交换机连接。在虚拟机内部各有一个容器,容器和主机通过 bridge 连接,而这两个虚拟机之间需要通过 vxlan 进行连接。

而我所使用的虚拟机是位于 vSpere 中的两台主机,他们采用 vSphere 中的虚拟机端口组相连,并且该端口组开启了混杂模式。两台虚拟机均为 Ubutnu18.04 系统,Docker 也为最新版本。

我还基于 Docker Hub 中的 Ubuntu 镜像封装了一个带有 ifconfig、ping、ip 和 python 命令的镜像,可以通过docker pull bzzdzc/myubuntu命令获取。

当然你可以通过在容器中的命令行内运行如下 apt-get 命令来安装这些软件:

1
2
3
4
5
6
7
apt update
// ifconfig
apt install net-tools
//ping
apt install iputils-ping
//ip
apt install iproute2

开始搭建网络

配置 VM1 中的网络

现在开始在 VM1 中对网络进行配置。

关闭防火墙

1
sudo ufw disable

运行容器

需要注意的是,在运行 Docker 时,选择不连接网络,之后我会将它连接到自己建立的网桥上面去。

1
docker run -itd --network none --name myubuntu1 myubuntu

记录容器中主进程的名称空间

首先,查看容器主进程的 PID,然后将其保存到$pid 变量中。

1
2
docker inspect --format '{{.State.Pid}}' myubuntu1
pid=$(docker inspect --format '{{.State.Pid}}' myubuntu1)

然后,将该 pid 链接至/var/run/netns 中,以便 ip netns 命令可以访问到它。

1
2
sudo mkdir -p /var/run/netns
sudo ln -s /proc/$pid/ns/net /var/run/netns/$pid

创建 veth peer

我们通过创建一对 veth peer A 和 B 来维持虚拟机主机通容器之间的通信。

1
sudo ip link add A type veth peer name B

创建新的网桥

这一步创建了一个名为 br-vx 的网桥,并将其状态设置为 up。然后,为网桥分配 ip 地址 192.168.0.1/24。

1
2
3
sudo brctl addbr br-vx
sudo ip link set br-vx up
sudo ip addr add 192.168.0.1/24 dev br-vx

将物理端口和 veth 端口 A 分别链接至网桥

虚拟机的物理端口一般为 ens32,我们将该端口连接至刚创建的网桥中。

然后我们将刚刚创建的 veth 的 A 端口绑定到网桥中,并设置其为 up。

1
2
3
sudo brctl addif br-vx ens32
sudo brctl addif br-vx A
sudo ip link set A up

将 veth 端口 B 绑定到容器的名称空间中

这一步将 veth 的 B 端口放置于容器中,并将其命名为容器中的 eth0.

1
2
3
sudo ip link set B netns $pid
sudo ip netns exec $pid ip link set dev B name eth0
sudo ip netns exec $pid ip link set eth0 up

为容器中的 eth0 端分配 IP 地址

为容器中分配 IP 地址 192.168.0.100/24。

1
sudo ip netns exec $pid ip addr add 192.168.0.100/24 dev eth0

现在,如果配置正确,使用命令docker exec -it myubuntu1 bash进入容器中的命令行后,运行ping 192.168.0.1 ,应该已经可以得到主机的回应了。

配置 VM1 中的 vxlan 连接

这里需要现在容器中创建一个 vxlan 连接。我们规定它的 id 为 100,而 vxlan 底层网络连接两端的 IP 分别为两个容器的 IP 192.168.0.100(本段)和 192.168.0.101(对端)。

然后设置 vxlan 隧道的 IP 为 10.0.0.1/24。

1
2
3
4
sudo ip netns exec $pid ip link add vxlan1 type vxlan id 100 remote 192.168.0.101 local 192.168.0.100 dstport 4789
sudo ip netns exec $pid ip link set vxlan1 up
sudo ip netns exec $pid ip addr add 10.0.0.1/24 dev vxlan1

配置 VM2 中的网络

VM2 中网络的配置方法和 VM1 中类似,只是分配的 IP 地址有些变化。

  • 给 VM2 中网桥分配的 IP 地址变为 192.168.0.2/24
  • 给 VM2 中容器里的 eth0 端口分配的 IP 地址变为 192.168.0.101/24
  • 给 VM2 中 vxlan 连接分配的本端 IP 地址变为 10.0.0.2/24

另外,在 vxlan 网络配置好之前,就已经可以使用 ping 命令测试底层网络网路的连通性了。

1
2
3
4
5
6
//测试和容器的连通性
ping 192.168.0.101
//测试和VM1的连通信
ping 192.168.0.1
//测试和VM1中容器的连通性
ping 192.168.0.100

测试 vxlan 网络的连通性

这个其实非常简单,依旧是使用 ping 命令。

首先,在 VM1 中进入容器内测试:

1
2
docker exec -it myubuntu1 bash
ping 10.0.0.2

然后,在 VM2 中进入容器内测试:

1
2
docker exec -it myubuntu1 bash
ping 10.0.0.1

如果双方都可以连通,那就说明基于 vxlan 的 隧道已经创建成功。

其它注意事项

有关混杂模式

首先需要注意的是,由于我们在 VM 中创建了容器,而又把 VM 和容器中的端口都绑定到了网桥上,所以,从 VM 中出去的包的 MAC 地址可能 VM 的也可能是容器的。由于安全策略限制,esxi 中的虚拟交换机默认会丢弃非 VM MAC 地址的包,这时就必须要开启混杂模式,让它不对源 MAC 地址进行验证。具体原理可以参考:http://blog.51cto.com/9843231/2294188?source=drh

一些参考资料