一、整体架构
在ubuntu系统中创建了两个Docker容器,即left和right。在创建Docker容器的同时会创建一对veth pair的互联接口,这对互联接口类似于一根通信管道,当向任意一端发送数据包时,另一端都能自动收到相同的包。互联接口的一端与容器内的网卡eth0相连,另一端则是veth(Virtual ethernet)接口master在bridge上。网桥的另一端连接的是用户或者程序创建的Tap设备。Tap设备是链路层的虚拟网络设备,其作用等同于一个以太网设备,它可以收/发链路层的第二层数据报文包,如以太网数据帧。Tap设备的另一端则连接着我们在NS3脚本中创建的TapBridge网络设备,TapBridge网络设备存在于NS3的用户空间。
通过以上这些网络设备,从容器内应用产生的数据包最终可以进入NS3的用户空间,这些数据包最终通过TapBridge网络设备被转发到相应的CSMA网络设备,同理NS3中CSMA网络设备产生的数据包也可以通过同样的方式传递到容器内。最终实现容器与NS3之间互联互通如图1所示(第二部分案例实现完全按照图1 实现)。
图1
二、案例实现
2.1、环境配置
1、创建tap
ip tuntap add tap-left mode tap
ip tuntap add tap-right mode tap
2、将tap设备设为混杂模式并启动
设置混杂模式是为了使tap能够监听系统中所有正在发送的数据包以及允许docker容器之间通过虚拟网络通信
sudo ifconfig tap-left 0.0.0.0 promisc up
sudo ifconfig tap-right 0.0.0.0 promisc up
3、创建docker网桥
docker network create net-left
docker network create net-right
4、根据docker网桥的命名规则获取所有bridg
# 获取所有网络桥接的名称
readarray -t bridges < <(ip link show type bridge | awk -F': ' '{print $2}'| grep -i 'br-')
5、将tap绑定到相应的bridge上并启动
sudo brctl addif ${bridges[0]} tap-left
sudo brctl addif ${bridges[1]} tap-right
ip link set dev ${bridges[0]} up
ip link set dev ${bridges[1]} up
6、创建两个docker容器并指定容器挂载在不同的docker网桥上
docker run -id --name left --network net-left ubuntu /bin/bash
docker run -id --name right --network net-right ubuntu /bin/bash
7、在Linux中允许操作系统将以太网帧转发到新创建的网桥(可以忽略这步的错误)
sudo iptables -I FORWARD -m physdev --physdev-is-bridged -i ${bridges[0]} -p tcp -j ACCEPT
sudo iptables -I FORWARD -m physdev --physdev-is-bridged -i ${bridges[0]} -p arp -j ACCEPT
sudo iptables -I FORWARD -m physdev --physdev-is-bridged -i ${bridges[1]} -p tcp -j ACCEPT
sudo iptables -I FORWARD -m physdev --physdev-is-bridged -i ${bridges[1]} -p arp -j ACCEPT
8、分别获取Docker容器进程 ID (PID) ---容器一定要启动状态
pid_left=$(docker inspect --format '{{ .State.Pid }}' left)
pid_right=$(docker inspect --format '{{ .State.Pid }}' right)
9、创建一个新的网络命名空间,该命名空间将以符号方式链接到第一个Docker容器left
mkdir -p /var/run/netns sudo
ln -s /proc/$pid_left/ns/net /var/run/netns/$pid_left
10、创建 veth pair将容器连接到自定义网桥
sudo ip link add internal-left type veth peer name external-left
sudo ip link set internal-left master ${bridges[0]}
sudo ip link set internal-left up
11、为容器left分配 IP 地址和 MAC 地址
sudo ip link set external-left netns $pid_left
sudo ip netns exec $pid_left ip link set dev external-left name eth0
sudo ip netns exec $pid_left ip link set eth0 address 12:34:88:5D:61:BD
sudo ip netns exec $pid_left ip link set eth0 up
sudo ip netns exec $pid_left ip addr add 10.0.0.1/16 dev eth0
12、right容器配置
sudo ln -s /proc/$pid_right/ns/net /var/run/netns/$pid_right
sudo ip link add internal-right type veth peer name external-right
sudo ip link set internal-right master ${bridges[1]}
sudo ip link set internal-right up
sudo ip link set external-right netns $pid_right
sudo ip netns exec $pid_right ip link set dev external-right name eth0
sudo ip netns exec $pid_right ip link set eth0 address 12:34:88:5D:62:BD
sudo ip netns exec $pid_right ip link set eth0 up
sudo ip netns exec $pid_right ip addr add 10.0.0.2/16 dev eth0
2.2、在root权限下运行ns3
添加图片注释,不超过 140 字(可选)
2.3、进入docker中相互ping
添加图片注释,不超过 140 字(可选)
三、多个容器与ns3通信
通过 docker Compose,可以使用 YML 文件来配置应用程序需要的所有服务。然后,使用一个命令,就可以从 YML 文件配置中创建并启动所有服务。
3.1、安装docker compose
1.安装curl(可选)
sudo apt update
sudo apt install curl
2.下载Docker Compose二进制文件
sudo curl -L "https://github.com/docker/compose/releases/download/v2.5.0/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose
3.设置权限
sudo chmod +x /usr/local/bin/docker-compose
4.检查
docker-compose version
3.2、使用docker compose管理多个docker
yaml文件
Docker Compose 是一个用于定义和运行多容器Docker应用程序的工具。可以使用YAML文件来配置docker的服务。
services:
node1:
container_name: node1
build:
dockerfile: images/ueDevice.Dockerfile
context: .
networks:
- net-node1
tty: true
node2:
container_name: node2
build:
dockerfile: images/Device.Dockerfile
context: .
networks:
- net-node2
tty: true
networks:
net-node1:
external: true
net-node2:
external: true
#create nodes
dockerfile文件
用于自动构建 Docker 镜像
FROM ubuntu
RUN apt-get update && apt-get install -y \
iputils-ping \
iproute2 \
netcat \
traceroute \
curl \
&& rm -rf /var/lib/apt/lists/*
CMD ["/bin/bash"]
其他配置
在这里使用一个脚本文件配置两个docker分别为node1和node2(用于和上面区分),bridges变量会获取到所有的br-****的bridge(因为我系统中只有两个br-***的bridge所以我使用的是 ${bridges[0]} 和 ${bridges[1]})。
在文件没有创建新的veth pair互联接口使用docker自带的互联接口
#创建tap
echo "Create tap ..."
ip tuntap add tap-node1 mode tap
ip tuntap add tap-node2 mode tap
#将tap设备设为混杂模式并启动
sudo ifconfig tap-node1 0.0.0.0 promisc up
sudo ifconfig tap-node2 0.0.0.0 promisc up
echo "Done."
#创建docker网桥
echo "Create docker bridge ..."
docker network create --driver bridge net-node1
docker network create --driver bridge net-node2
# 获取所有网络桥接的名称
readarray -t bridges < <(ip link show type bridge | awk -F': ' '{print $2}'| grep -i 'br-')
echo ${bridges[0]}
echo ${bridges[1]}
#将tap绑定到相应的网桥上并启动
sudo brctl addif ${bridges[0]} tap-node1
sudo brctl addif ${bridges[1]} tap-node2
ip link set dev ${bridges[0]} up
ip link set dev ${bridges[1]} up
echo "Done."
#创建两个docker容器并指定容器挂载在不同的docker网桥上
echo "Create docker bridge ..."
docker-compose -f test.yaml up -d
echo "Done."
#分别获取Docker容器进程 ID (PID) ---容器一定要启动状态
echo "get pid ..."
pid_node1=$(docker inspect --format '{{ .State.Pid }}' node1)
pid_node2=$(docker inspect --format '{{ .State.Pid }}' node2)
echo "Done."
#创建一个新的网络命名空间,该命名空间将以符号方式链接到第一个Docker容器left
echo "set node1..."
mkdir -p /var/run/netns
sudo ln -s /proc/$pid_node1/ns/net /var/run/netns/$pid_node1
echo "set node1..."
#为容器node1分配 IP 地址和 MAC 地址
sudo ip netns exec $pid_node1 ip link set eth0 up
sudo ip netns exec $pid_node1 ip addr add 10.0.0.3/16 dev eth0
echo "Done."
#容器node2
echo "set node2..."
sudo ln -s /proc/$pid_node2/ns/net /var/run/netns/$pid_node2
sudo ip link set external-node2 netns $pid_node2
sudo ip netns exec $pid_node2 ip link set eth0 up
sudo ip netns exec $pid_node2 ip addr add 10.0.0.4/16 dev eth0
echo "Done."
添加图片注释,不超过 140 字(可选)
参考文献: