Veth pair 细讲

1.前言

我们在《Network Namespace 到底是个啥,别人再问你告诉他》文章中设计了一个网络架构,可以如下图:

当时使用了veth pair(虚拟以太网设备对),它由两个成对出现的虚拟以太网接口组成,可以形象地理解为一根双向连通的"虚拟网线"。

最典型的使用场景 :跨网络命名空间通信:veth pair 最核心的应用场景是连接不同的网络命名空间(Network Namespace)。将 veth pair 的一端放在一个命名空间,另一端放在另一个命名空间,就能实现跨命名空间的数据传输。这在 Docker 和 Kubernetes 等容器网络中是基础组件。

在实际架构中,veth pair 的一端通常位于容器命名空间,另一端位于宿主机命名空间并接入虚拟网桥(如 docker0)。此时,宿主机端的 veth 设备作为网桥从设备,不直接调用协议栈,而是将数据包交由网桥进行二层 MAC 地址转发

我在上文提到的文章中就是这种应用场景,将veth pair的一端留在主网络空间,另一端插入隔离空间,实现网络互通。本篇文章接下来会有两个目的:

  1. veth pair这个技术细节进行讨论
  2. 外部主机如何能访问到隔离空间的服务呢,这个在上篇文章中我并没有验证,本篇进行验证。涉及到dnat,iptables的内容。

2.veth pair

2.1创建一个独立的网络空间

我先说一下,我要做的是一件什么事情,就是想创建一个独立的网络空间ns1,然后给这个网络空间一个网卡(当然使用veth pair实现),然后赋予其一个独立的ip。接下来我要在这个独立的网络空间启动一个服务端口占用 9999,然后 通过围绕怎么才能访问到这个9999服务,来讲解网络的通透性。

bash 复制代码
# 1. 创建一个命名空间 (ns1),宿主机 (主空间) 作为另一端
ip netns add ns1
# 2. 当然可以查询都有哪些空间 list命令,查看创建结果
ip netns list

2.2 创建veth pair

bash 复制代码
ip link add veth-ns1 type veth peer name veth-host

执行命令之前你可以通过 ip addr 命令查看目前网卡情况

结果现实为 目前就 lo和eth0 两块网卡,eth0网卡的ip是10.10.30.154

也可以顺道查一下目前内核路由表情况 route -n

当我们创建veth pair 之后,会多一对网卡,名字成对,这正是 veth pair的特征

2.3 把veth pair的一端移动到独立空间ns1中

bash 复制代码
# 3. 把 ns1 那一端移入命名空间,host 那一端留在宿主机
ip link set veth-ns1 netns ns1

接下来你进入到这个独立空间,你会发现多了一块网卡

bash 复制代码
# 4.进入ns1 空间执行ip addr 命令
ip netns exec ns1 ip addr

那块网卡名字叫veth-ns1@if3 @if3 表明对端就是主空间编号为3的那块网卡。也就是我们刚才留在主空间那一端的网卡。现在已经改名了叫veth-ns1@if3

2.4 为 独立空间ns1 配置 IP 并激活

bash 复制代码
# 4. 为 ns1 配置 IP 并激活
ip netns exec ns1 ip addr add 192.168.1.2/24 dev veth-ns1
ip netns exec ns1 ip link set dev veth-ns1 up

#
ip netns exec ns1 ip link set lo up

先分配ip,然后通电激活,只要激活后,你会发现一个问题,进入这个独立空间,执行route -n,发现路由表会多出来一条

bash 复制代码
ip netns exec ns1 route -n

发往 192.168.1.0/24 网段的包,直接从 veth-ns1这个接口发出去。

路由表控制的就是"从当前这个空间出去的包怎么走"。

路由表只回答一个问题:我要往外发包,从哪个口出去、下一跳是谁?

  1. 它不决定别人怎么找到你(那是别人的路由表决定的)
  2. 它不决定进来的包怎么处理(那是 iptables/nftables 的事)
  3. 它只管:这个空间里产生的、或者需要从这个空间转发的包,该往哪走

路由表 = 出站导航。每个网络空间(不管是物理机、容器还是 netns)都有自己独立的路由表,各管各的出站方向,互不干扰。

2.5 宿主机端:配置不同网段的 IP,并激活

bash 复制代码
# 宿主机端:配置不同网段的 IP,并激活
ip addr add 10.0.0.1/24 dev veth-host
ip link set dev veth-host up

这么配置完毕之后,宿主机端的路由表如下图所示,增加了一个条目

2.6 在独立空间ns1中启动一个服务

bash 复制代码
ip netns exec ns1 python2 -m SimpleHTTPServer 8080

2.7 整体的网络图

经过上面几个章节的配置,整个网络 ,veth pair的结构如图所示。

2.7 验证

我们把veth pair的两端配置成不同网段的IP。来验证不同网段之间的通信。现在的网络如下图:

2.7.1 路由表的概念 route-tables

路由表是干啥的?:路由表本质上就是在回答一个问题:"这个目标 IP,我该从哪个网卡接口发出去?下一跳是谁?"

直连路由:只要给网卡配置了 IP,且网卡处于激活(UP)状态,系统就会自动生成一条路由,这条路由匹配该 IP 所在的网段,这就是直连路由

我们举个例子:

当你在电脑上配置好 IP 地址(比如 192.168.1.10/24)时,内核会自动在路由表里生成一条直连路由(Directly Connected Route)。 你可以用命令 ip route 看到类似这样的一条记录:

bash 复制代码
192.168.1.0/24 dev eth0 proto kernel scope link src 192.168.1.10

这条记录的意思就是:"凡是属于 192.168.1.0/24 这个网段的 IP,都直接从 eth0 网卡发出去,不需要经过任何网关

所以当你要发包给192.168.1.20,内核会拿着这个目标IP,去路由表逐个匹配,正好命中这条路由,"直接从这个网卡(eth0)发出去就行了。"

如果没有匹配的路由 → 直接丢弃,返回 "Network is unreachable"

2.7.2 ARP

ARP(Address Resolution Protocol,地址解析协议),它的核心功能就是:给定一个目标 IP 地址,获取对应的 MAC 地址。

当你的主机需要给局域网内的另一台主机发数据时,就需要知道目标主机的MAC地址。

你的主机在发数据前,会先检查自己本地的 ARP 缓存表,看看有没有目标 IP 对应的 MAC 地址记录,当然最开始缓存表肯定是空的,你的主机就会在局域网内发送一个 ARP 广播请求,意思就是:"局域网内谁的ip是这个,把你的mac地址告诉我"。局域网里的所有设备都会收到这个广播,但只有 IP 地址匹配的目标主机会处理它。复一个 ARP 应答报文,告诉你的主机:"我的 IP 是这个,我的 MAC 地址是 xx-xx-xx-xx-xx-xx"。你的主机收到应答后,会把这对"IP-MAC 映射关系"存入本地的 ARP 缓存表中。下次再给这台主机发数据时,直接从缓存里查就行了,不用再发广播

所以 ARP 缓存表里只能存放局域网(同一广播域)内 IP 和 MAC 地址的对应关系.

2.7.3 开始验证

当我们在机器上直接ping独立网络空间ns1的IP 192.168.1.2,肯定是ping不同的,因为你的主机上的路由表目前还没有配置一条路由将目标IP的包发出去

你需要配置

bash 复制代码
ip route add 192.168.1.0/24 dev veth-host

但是配置完 你发现还是不通:

bash 复制代码
[root@k8s-master ~]# ping 192.168.1.2 -c 3
PING 192.168.1.2 (192.168.1.2) 56(84) bytes of data.
From 10.0.0.1 icmp_seq=1 Destination Host Unreachable
From 10.0.0.1 icmp_seq=2 Destination Host Unreachable
From 10.0.0.1 icmp_seq=3 Destination Host Unreachable

--- 192.168.1.2 ping statistics ---
3 packets transmitted, 0 received, +3 errors, 100% packet loss, time 1999ms
pipe 3

但是返回比较特殊: From 10.0.0.1 icmp_seq=3 Destination Host Unreachable

解决方案 :你刚才用 ip route add 只是告诉宿主机:"去 192.168.1.x 网段,走 veth-host 这条路"。 但是,网络通信是双向的! 你没有告诉 ns1 怎么回包,所以 ns1 迷路了。

既然去程的路有了,只要在 ns1 里配置一条回程的静态路由,告诉它"要去 10.0.0.1 走 veth-ns1 这条路",通信就能打通了!

bash 复制代码
sudo ip netns exec ns1 ip route add 10.0.0.0/24 dev veth-ns1

配置完毕后 在ping就通了

bash 复制代码
[root@k8s-master ~]# ping 192.168.1.2 -c 3
PING 192.168.1.2 (192.168.1.2) 56(84) bytes of data.
64 bytes from 192.168.1.2: icmp_seq=1 ttl=64 time=0.022 ms
64 bytes from 192.168.1.2: icmp_seq=2 ttl=64 time=0.018 ms
64 bytes from 192.168.1.2: icmp_seq=3 ttl=64 time=0.019 ms

3. 总结

至此 veth pair中的一些细节就讲清楚了。

1.接下来我会去研究 k8s中不同主机之间的pod是如何通信的。

2.docker中的端口映射怎么实现的?

相关推荐
Elastic 中国社区官方博客1 小时前
将 Vercel 数据导入 Elastic:无需安装任何东西的无服务器可观测性
大数据·运维·elasticsearch·搜索引擎·云原生·serverless·全文检索
普通人62 小时前
win系统docker运行
windows·docker
秦jh_2 小时前
【Docker】容器
运维·docker·容器
三8442 小时前
云安全 · 02 · 容器与 Docker 安全基础
web安全·docker·云安全
天天喝旺仔4 小时前
Prometheus + Grafana 监控告警体系搭建实战:从 Exporter 指标采集、PromQL 查询到 Alertmanager 告警落地
容器·kubernetes·grafana·prometheus·时序数据库
大盛供应链·卢生13 小时前
哪些国产芯片可以替代进口芯片,哪些替代不了
fpga开发·容器
小小的木头人14 小时前
Docker 环境下迁移 containerd 数据目录:从 `/var/lib/containerd` 到 `/data/containerd`
linux·docker
Mr小林18 小时前
Docker 安装教程(在线 + 离线)
运维·docker·容器
阿里云云原生18 小时前
云栖剧透丨四场论坛,看清智能体走进生产的关键路径
云原生