使用veth和bridge模拟容器网络

news2024/10/5 19:13:27

使用veth和bridge模拟容器网络

随着虚拟化技术、容器化技术的发展,我们在工作中会使用到虚拟化网络技术。简单来说,以往的网卡、交换机、路由器,当前可以通过操作系统使用程序来进行模拟。

通常使用最为广泛的是下面的虚拟设备:

设备作用
veth一对相互连接的网卡,常用于连接两个namespace
bridge相当于一个二层交换机,如果配置了ip地址,则是一个三层交换机
tun/tap虚拟网卡,常用于实现vpn

在本文搭建容器网络的过程中,主要使用到了veth和bridge。下面将介绍这两种虚拟设备。

veth和brdige

veth

veth pair 全称是 Virtual Ethernet Pair,是一个成对的端口,所有从这对端口一 端进入的数据包都将从另一端出来,反之也是一样。

引入 veth pair 是为了在不同的 Network Namespace 直接进行通信,利用它可以直接将两个 Network Namespace 连接起来。

veth

使用下面的命令可以创建一个veth pair。

ip link add <p1-name> type veth peer name <p2-name>

bridge

veth pair 打破了 Network Namespace 的限制,实现了不同 Network Namespace 之间的通信。但 veth pair 有一个明显的缺陷,就是只能实现两个网络接口之间的通信。

如果我们想实现多个网络接口之间的通信,就可以使用下面介绍的网桥(Bridge)技术。

简单来说,网桥就是把一台机器上的若干个网络接口 “连接” 起来。其结果是,其中一个网口收到的报文会被复制给其他网口并发送出去。以使得网口之间的报文能够互相转发。

bridge

实践

在本文中,将创建三个Network namespace,使得这三个namesapce可以实现:

  • 1.ping通其它namespace
  • 2.ping通主机
  • 3.ping通外网

如果把容器想象成真实的机器,则其网络拓扑图可能是下面这样的:

docker-network

下面便一步一步的去实现。

步骤1:创建三个network namespace

linux中的ip命令可以对network namespace进行操作, 例如使用下面的命令就可以创建一个名叫test的network namespace。

ip netns add <ns>

使用下面的命令可以用于查看已经创建的network namespace。

ip netns ls

有了上述基础,我们分别创建red/blue/green三个network namespace。

ip netns add ns1
ip netns add ns2
ip netns add ns3

ip netns ls查看namespace

[root@localhost ~]# ip netns ls
ns3
ns2
ns1

每个ns都和主机网络一样,有自己的网卡,路由表,ARP表, iptabls等等。

可以使用下面的命令查看ns内部的信息

ip netns exec <namespace> <command>

下面使用下面的命令查看我们刚刚创建的ns1空间的路由表,网卡和arp缓存。

# ns1 中的路由表
[root@localhost ~]# ip netns exec ns1 route -n
Kernel IP routing table
Destination     Gateway         Genmask         Flags Metric Ref    Use Iface

#ns1 中的网卡, 目前只有lo
[root@localhost ~]# ip netns exec ns1 ip addr
1: lo: <LOOPBACK> mtu 65536 qdisc noop state DOWN group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00

#ns1 中的arp缓存
[root@localhost ~]# ip netns exec ns1 arp

step2:创建虚拟交换机

由于我们需要将3个namespace互相连接,因此我们需要使用bridge进行连接。

使用下面的命令可以创建一个虚拟交换机。虽然它的type是bridge,但其实是一个二层交换机。当然如果为其添加ip地址,那么其就是一个三层交换机。这里我们暂时不用设置ip地址,当后面实现和主机进行通讯时,再配置。

创建完毕之后,顺便启动该设备。

ip link add vbridge type bridge 
ip link set dev vbridge up 

接下来要做的是将3个namespace都接入到该vbrdige中。因此我们需要创建3个veth pair。 这里的命令同样是ip link add, 只不过设备的type是veth。

这里需要注意的是, Linux内核对网络接口的名字长度有限制,不能超过15个字符。

ip link add veth-1 type veth peer name veth-1-br
ip link add veth-2 type veth peer name veth-2-br
ip link add veth-3 type veth peer name veth-3-br

有了这些veth-pair,下面要做的是将其安装在合适的地方。

  • 将veth-1连接到namespace ns1, 另一端veth-1-br连接到vbridge
  • 将veth-2连接到namespace ns2, 另一端veth-2-br连接到vbridge
  • 将veth-3连接到namespace ns3, 另一端veth-3-br连接到vbridge
ip link set veth-1 netns ns1
ip link set veth-1-br master vbridge
ip link set veth-2 netns ns2
ip link set veth-2-br master vbridge
ip link set veth-3 netns ns3
ip link set veth-3-br master vbridge

使用bridge link可以查看目前vbridge上所连接的设备。

[root@localhost ~]# bridge link
11: veth-1-br@if12: <BROADCAST,MULTICAST> mtu 1500 master vbridge state disabled priority 32 cost 2
13: veth-2-br@if14: <BROADCAST,MULTICAST> mtu 1500 master vbridge state disabled priority 32 cost 2
15: veth-3-br@if16: <BROADCAST,MULTICAST> mtu 1500 master vbridge state disabled priority 32 cost 2

下面我们需要给veth-1,veth-2,veth-3添加ip地址,ip需要在同一个网段内。

ip netns exec ns1 ip addr add 192.168.28.1/24 dev veth-1
ip netns exec ns2 ip addr add 192.168.28.2/24 dev veth-2
ip netns exec ns3 ip addr add 192.168.28.3/24 dev veth-3

接下来就是将veth启动。

ip netns exec ns1 ip link set veth-1 up
ip link set dev veth-1-br up
ip netns exec ns2 ip link set veth-2 up
ip link set dev veth-2-br up
ip netns exec ns3 ip link set veth-3 up
ip link set dev veth-3-br up

这一些系列操作之后,三个namespace就已经都过vbridge互联在了一起,可以通过ping命令来测试三个namepsace的连通性。

ip netns exec ns1 ping 192.168.28.2
ip netns exec ns1 ping 192.168.28.3
ip netns exec ns2 ping 192.168.28.3
ip netns exec ns2 ping 192.168.28.3
ip netns exec ns3 ping 192.168.28.1
ip netns exec ns3 ping 192.168.28.2

这里是ns1去ping 192.168.28.2的结果,可以看到可以ping通。

ip netns exec ns1 ping 192.168.28.2
[root@localhost ~]# ip netns exec ns1 ping 192.168.28.2
PING 192.168.28.2 (192.168.28.2) 56(84) bytes of data.
64 bytes from 192.168.28.2: icmp_seq=1 ttl=64 time=0.059 ms
64 bytes from 192.168.28.2: icmp_seq=2 ttl=64 time=0.056 ms

到这里为止,我们完成了下面的拓扑图。已经实现了容器之间的相互访问。接下来,我们将实现容器和主机网络的相互访问。

docker-network

step3: 实现可以和主机之间通讯

首先尝试在ns1的namespace下直接ping主机,例如我的主机地址是192.168.17.10。从结果可知,目前网络是无法联通的。

[root@localhost ~]# ip netns exec ns1 ping 192.168.17.10
ping: connect: Network is unreachable

这里无法ping通,原因是容器网络和主机网络处在两个网段,因此二层设备无法实现这样的要求。于是我们需要为bridge添加ip地址。使用下面的命令为bridge添加ip地址。

ip addr add 192.168.28.5/24 dev vbridge

添加完之后,使用route -n查看本机的路由表,可以发现此时192.168.28.0已经有了路由的项,其将被发送给vbridge设备。

0.0.0.0         192.168.17.2    0.0.0.0         UG    100    0        0 ens33
192.168.17.0    0.0.0.0         255.255.255.0   U     100    0        0 ens33
192.168.28.0    0.0.0.0         255.255.255.0   U     0      0        0 vbridge

实际上,此时宿主机已经可以ping通namespace了。

[root@localhost ~]# ping 192.168.28.1
PING 192.168.28.1 (192.168.28.1) 56(84) bytes of data.
64 bytes from 192.168.28.1: icmp_seq=1 ttl=64 time=0.037 ms
64 bytes from 192.168.28.1: icmp_seq=2 ttl=64 time=0.099 ms
^C
--- 192.168.28.1 ping statistics ---
2 packets transmitted, 2 received, 0% packet loss, time 1026ms
rtt min/avg/max/mdev = 0.037/0.068/0.099/0.031 ms
[root@localhost ~]# ping 192.168.28.2
PING 192.168.28.2 (192.168.28.2) 56(84) bytes of data.
64 bytes from 192.168.28.2: icmp_seq=1 ttl=64 time=0.047 ms
64 bytes from 192.168.28.2: icmp_seq=2 ttl=64 time=0.057 ms
^C
--- 192.168.28.2 ping statistics ---
2 packets transmitted, 2 received, 0% packet loss, time 1008ms
rtt min/avg/max/mdev = 0.047/0.052/0.057/0.005 ms
[root@localhost ~]# ping 192.168.28.3
PING 192.168.28.3 (192.168.28.3) 56(84) bytes of data.
64 bytes from 192.168.28.3: icmp_seq=1 ttl=64 time=0.094 ms
64 bytes from 192.168.28.3: icmp_seq=2 ttl=64 time=0.153 ms
^C
--- 192.168.28.3 ping statistics ---
2 packets transmitted, 2 received, 0% packet loss, time 1056ms
rtt min/avg/max/mdev = 0.094/0.123/0.153/0.029 ms

不过遗憾的是namespace还不能ping通宿主机。

这是由于创建的三个ns的路由表中还没有默认路由。例如ns1中使用route -n,可以看到目前只有往192.168.28.0/24的路由表。而主机的地址无法匹配上,因此数据包无法发送。

[root@localhost ~]# ip netns exec ns1 route -n
Kernel IP routing table
Destination     Gateway         Genmask         Flags Metric Ref    Use Iface
192.168.28.0    0.0.0.0         255.255.255.0   U     0      0        0 veth-1

下面要做的就是为3个namespace添加默认路由

ip netns exec ns1 ip route add default via 192.168.28.5

ip netns exec ns2 ip route add default via 192.168.28.5

ip netns exec ns3 ip route add default via 192.168.28.5

下面通过ping命令测试连通性,可以看到现在已经可以ping通主机地址了。

[root@localhost ~]# ip netns exec ns1 ping 192.168.17.10
PING 192.168.17.10 (192.168.17.10) 56(84) bytes of data.
64 bytes from 192.168.17.10: icmp_seq=1 ttl=64 time=0.039 ms
64 bytes from 192.168.17.10: icmp_seq=2 ttl=64 time=0.105 ms

到这里为止,我们完成了下面的拓扑图。已经实现了容器之间的相互访问。

docker-network

这里还有一个细节需要讨论。从ns1去ping主机时,ns1首先根据查找路由表发现目的网段匹配了默认路由,于是将数据包发送给了veth-1网卡,从而发送到了vbridge上。vbridge本身的ip地址是192.168.28.5,而目的地址是192.168.17.10,与自身不一致。那么这个时候会如何处理呢?

这里首先会判断目的地址是否是一个本机地址。如果不是,则意味着数据包需要往外部转发。这里时候就会检查ip_forward参数和本机路由表,决定是否转发和从哪个网卡转发。如果可以转发,则将数据包发送发网卡的发送队列上。

如果是目的地址是一个本机地址,则直接将数据包放置到目的地址对应的网卡的接收队列上交由内核协议栈处理。

这里主要讨论了当网卡接收到了目的地址与网卡本身ip地址不同的处理。

docker-network

下面,为了容器可以访问外网,我们需要将主机设置成路由模式,并为其设置nat映射表。

step4: namespace实现可以和外网通讯

目前,创建的namespace还无法ping通外网,

ip netns exec ns1 ping 8.8.8.8
PING 8.8.8.8 56(84) bytes of data.

此时数据包是可以通过网卡发送出去的,但是由于数据包的源地址是一个内网地址,因此无法回数据包。

这就需要我们配置nat,使得ns可以通过主机的ip地址发送数据包。

这里首先需要开机主机的数据包转发功能。可以使用sysctl修改ip_forward的内核参数使得主机可以支持数据包转发。

sysctl net.ipv4.ip_forward
net.ipv4.ip_forward = 0

sysctl -w net.ipv4.ip_forward=1
net.ipv4.ip_forward = 1

在这之后,通过iptables增加SNAT规则,将源ip为192.168.28.0/24内的数据包的源ip修改为ens33的ip:

$ iptables -t nat -A POSTROUTING -s 192.168.28.0/24 -j MASQUERADE

再次ping 8.8.8.8,可以看到现在已经可以ping通了。

[root@localhost ~]# ip netns exec ns1 ping 8.8.8.8
PING 8.8.8.8 (8.8.8.8) 56(84) bytes of data.
64 bytes from 8.8.8.8: icmp_seq=1 ttl=127 time=175 ms
64 bytes from 8.8.8.8: icmp_seq=2 ttl=127 time=175 ms

这里,宿主机已经相当于一个router, 而ens33和bridge则是router的两个接口。

至此为止,我们实现的网络拓扑图如下所示:

docker-network

总结

本文通过创建network namespace,并使用veth和bridge虚拟设备构建出了容器网络的雏形,实现了下面的功能:

  • ns之间可以互相访问
  • ns与主机之间可以互相访问
  • ns访问公网

本实验主要用于帮助理解docker网络实现的原理。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1034943.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

新一代爬虫工具 katana 配置及使用

新一代爬虫工具 katana 配置及使用。 功能&#xff1a; 快速且完全可配置的网络爬行 标准和无外设模式支持 JavaScript 解析/爬网 可定制的自动表单填写 范围控制 - 预配置字段/正则表达式 可自定义的输出 - 预配置字段 输入 - 标准输入、URL 和列表 输出 - 标准输出、…

【Java Web】CSS

目录 1.CSS(Cascading Style Sheets) 层叠样式表 2.标签 1.注释 2.三种书写样式 1.内部样式 2.内联样式 3.外部样式 3.CSS选择器 &#xff08;1&#xff09;标签选择器 &#xff08;2&#xff09;类选择器 &#xff08;3&#xff09;ID选择器 复合选择器 &#xf…

人力资源HR 怎么选择在线人才测评工具

测评已经是普及度很好了&#xff0c;不仅仅是大企业&#xff0c;中小企业也都在启用人才测评&#xff0c;也有叫素质测评等等&#xff0c;内容多样化。但是根本形式是一样的&#xff0c;那就是在线测评&#xff0c;目的也是一样的&#xff0c;就是为了招来最适合的职员。 而市…

细胞个数统计

1.1 应用示例目的与思路 (1) 对输入图像进行灰度化、滤波和阈值分割&#xff1b; (2) 对区域进行填充、连通域分析和面积筛选&#xff1b; (3) 对区域进行距离变换&#xff0c;对变换后的距离信息图像进行类型转换和图像增强&#xff1b; (4) 使用分水岭算法提取区域&#…

windows安装c环境

一. 下载安装mingw-w64 mingw-w64 解压后放到window环境变量路径 sysdm.cpl参看是否安装成功 二. 安装c idea Dev-Cpp下载及安装 新建文件 运行 编译&#xff08;F9&#xff09;、运行&#xff08;F10&#xff09;以及编译运行&#xff08;F11&#xff09; 参考 安装C…

使用 LangChain 和 Elasticsearch 对私人数据进行人工智能搜索

关于本博文的所有代码可以在地址下载&#xff1a;GitHub - liu-xiao-guo/python-vector-private 我将在本博文中其中深入研究人工智能和向量嵌入的深水区。 ChatGPT 令人大开眼界&#xff0c;但有一个主要问题。 这是一个封闭的托管系统。 在一个被大型网络公司改变的世界里生…

代码随想录—力扣算法题:07.链表相交. Java版(示例代码与导图详解)

版本说明 当前版本号[20230923]。 版本修改说明20230923初版 07. 链表相交 同&#xff1a;160.链表相交 力扣题目链接 给你两个单链表的头节点 headA 和 headB &#xff0c;请你找出并返回两个单链表相交的起始节点。如果两个链表没有交点&#xff0c;返回 null 。 图示…

C/C++正常血压 2019年12月电子学会青少年软件编程(C/C++)等级考试一级真题答案解析

目录 C/C正常血压 一、题目要求 1、编程实现 2、输入输出 二、解题思路 1、案例分析 三、程序代码 四、程序说明 五、运行结果 六、考点分析 C/C正常血压 2019年12月 C/C编程等级考试一级编程题 一、题目要求 1、编程实现 监护室每小时测量一次病人的血压&#x…

链式二叉树的实现及遍历(C语言版)

目录 1 基本概念 1.1 树的概念 1.2 二叉树的链式表示 1.2.1 "左孩子右兄弟"表示法 1.2.2 "左右子树"表示法 1.2.3 手动构建一棵树 2 树的遍历 2.1 前序遍历/先序遍历 2.2 中序遍历 2.3 后序遍历 2.4 层序遍历 2.4.1 算法思想 ​编辑 2.4.2 带头…

堆向上调整及堆向下调整

个人主页&#xff1a;Lei宝啊 愿所有美好如期而遇 前言&#xff1a; 在堆这一节中&#xff0c;孩子和其父节点有如下关系&#xff1a; 左孩子&#xff1a;left_child parent * 2 1; 右孩子&#xff1a;right_child parent * 2 2; 父节点在计算时&#xff0c;因为兄弟…

【Java 基础篇】Java 接口组成与更新详解

在Java编程中&#xff0c;接口&#xff08;interface&#xff09;是一种非常重要的概念。它允许类定义一组抽象方法&#xff0c;这些方法可以在不同的类中实现。接口在Java中起到了重要的角色&#xff0c;被广泛应用于代码的组织和设计中。本文将详细解释Java接口的组成和最新的…

C++ - 红黑树 介绍 和 实现

前言 前面 学习了 AVL树&#xff0c;AVL树虽然在 查找方面始终拥有 O(log N &#xff09;的极高效率&#xff0c;但是&#xff0c;AVL 树在插入 ,删除等等 修改的操作当中非常的麻烦&#xff0c;尤其是 删除操作&#xff0c;在实现当中细节非常多&#xff0c;在实现上非常难掌控…

3、靶场——Pinkys-Place v3(3)

文章目录 一、获取flag41.1 关于SUID提权1.2 通过端口转发获取setuid文件1.3 运行pinksecd文件1.4 利用nm对文件进行分析1.5 构建payload1.6 Fire 二、获取flag52.1 生成ssh公钥2.2 免密登录ssh2.3 以pinksecmanagement的身份进行信息收集2.4 测试程序/usr/local/bin/PSMCCLI2.…

Vue的详细教程--Vue路由与nodejs

&#x1f973;&#x1f973;Welcome Huihuis Code World ! !&#x1f973;&#x1f973; 接下来看看由辉辉所写的关于Vue的相关操作吧 目录 &#x1f973;&#x1f973;Welcome Huihuis Code World ! !&#x1f973;&#x1f973; 一.Vue路由是什么 二.使用Vue路由的步骤 1、…

【无标题】显示TIFF格式文件

显示TIF文件 运行结果 package src;import com.sun.media.jai.codec.*;import com.sun.media.jai.codec.FileSeekableStream; import com.sun.media.jai.codec.ImageDecoder; import com.sun.media.jai.codec.ImageCodec; import com.sun.media.jai.codec.TIFFEncodeParam; imp…

2010-2017年WIND分省政府性债务余额面板数据

2010-2017年WIND分省政府性债务余额面板数据 1、时间&#xff1a;2010-2017年 2、指标&#xff1a;债务余额 3、范围&#xff1a;30个省 4、来源&#xff1a;wind 5、指标解释&#xff1a;地方政府债务分为一般债务和专项债务。 一般债务对应的是一般公共预算&#xff0c…

操作系统权限提升(三十)之数据库提权-SQL Server sp_oacreate+sp_oamethod(dba权限)提权

SQL Server sp_oacreate+sp_oamethod(dba权限)提权 sp_oacreate+sp_oamethod介绍 在xp_cmdshell被删除或不能利用是可以考虑利用sp_oacreate,利用前提需要sqlserver sysadmin账户服务器权限为system(sqlserver2019默认被降权为mssql)。sp_oacreate 是一个存储过程,可以…

Kubernetes 部署 nfs-subdir-external-provisioner

概述 官方GitHub及参考文档:GitHub - kubernetes-sigs/nfs-subdir-external-provisioner: Dynamic sub-dir volume provisioner on a remote NFS server. 部署nfs-subdir-external-provisioner提供StorageClass服务 步骤 nfs 服务器准备 /etc/exports # cat /etc/exports…

数据链路层--以太网

文章目录 以太网1. 以太网帧格式2. mac地址与IP地址 代表协议:以太网. 以太网 以太网" 不是一种具体的网络&#xff0c;而是一种技术标准&#xff1b;既包含了数据链路层的内容&#xff0c;也包含了一些物理层的内容。例如&#xff1a;规定了网络拓扑结构&#xff0c;访…

laravel框架 - 消息队列如何使用

业务场景&#xff1a;项目里边有很多视频资源需要上传到抖音资源库&#xff0c;通过队列一条一条上传。 参考实例&#xff1a;发送邮件&#xff0c;仅供参考 (1)创建任务【生成任务类】 在你的应用程序中&#xff0c;队列的任务类都默认放在 app/Jobs 目录下。如果这个目录不存…