Skip to content

Commit e91e58d

Browse files
添加linux bridge说明
1 parent 088fa4a commit e91e58d

6 files changed

+233
-1
lines changed
114 KB
Loading
144 KB
Loading
115 KB
Loading
156 KB
Loading
154 KB
Loading

k8s/kubernetes_network.md

Lines changed: 233 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ Kubernetes集群至少应该包含三个网络,如图网络环境所示。一
1212

1313
![[image-2025-01-27-01-07-54-828.png]]
1414

15-
## Docker网络基础
15+
## 云网络基础
1616

1717
Docker技术依赖于近年来Linux内核虚拟化技术的发展, 所以Docker对Linux内核有很强的依赖。 Docker使用到的技术有网络命名空间( Network Namespace) 、 Veth设备对、 网桥、 ipatables和路由。
1818

@@ -76,6 +76,238 @@ ip link set dev veth3 up
7676
ip link set <veth1> up
7777
```
7878

79+
### 网络命名空间实践
80+
创建一个名为netns1的 network namespace
81+
```bash
82+
ip netns add netns1
83+
```
84+
这个时候查看网络命名空间,只有一个lo网卡,而且是down状态
85+
```bash
86+
sudo ip netns exec netns1 ip addr
87+
1: lo: <LOOPBACK> mtu 65536 qdisc noop state DOWN group default qlen 1000
88+
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
89+
```
90+
通过 `ip netns list` 查看 netns1也已经创建成功,但是这个时候lo网卡并不能使用,需要先将lo网卡拉起才能使用
91+
```bash
92+
ip netns exec netns1 ip link set dev lo up
93+
# 执行 ping 命令 lo网卡通了
94+
sudo ip netns exec netns1 ping 127.0.0.1
95+
PING 127.0.0.1 (127.0.0.1) 56(84) bytes of data.
96+
64 bytes from 127.0.0.1: icmp_seq=1 ttl=64 time=0.030 ms
97+
64 bytes from 127.0.0.1: icmp_seq=2 ttl=64 time=0.028 ms
98+
```
99+
100+
但是只有一个本地的回环网卡设备是无法和外界进行通讯的,如果我们想与外界进行通讯,就需要在namespace里创建一对虚拟的以太网卡,即所谓的veth pair,顾名思义, veth pair总是成对出现且相互连接, 它就像Linux的双向管道(pipe) , 报文从veth pair一端进去就会由另一端收到。
101+
下面的命令将创建一对虚拟以太网卡, 然后把veth pair的一端放到netns1 network namespace。
102+
```bash
103+
ip link add veth0 type veth peer name veth1
104+
# 将其中一块虚拟网卡veth1通过ip link set命令移动到netns1 network namespace
105+
ip link set veth1 netns netns1
106+
```
107+
这两块网卡刚创建出来还都是DOWN状态, 需要手动把状态设置成UP。 这个步骤的操作和上文对lo网卡的操作类似, 只是多了一步绑定IP地址,
108+
```bash
109+
ip netns exec netns1 ifconfig veth1 10.1.1.1/24 up
110+
ifconfig veth0 10.1.1.2/24 up
111+
```
112+
网卡拉起来之后两边就能进行相互通信了,比如在主机上可以 `ping 10.1.1.1` 或者进入到netns1里面 `ping 10.1.1.2`
113+
114+
用户可以随意将虚拟网络设备分配到自定义的network namespace里, 而连接真实硬件的物理设备则只能放在系统的根network namesapce中。 并且, 任何一个网络设备最多只能存在于一个network namespace中。
115+
116+
### veth pair
117+
veth是虚拟以太网卡(Virtual Ethernet) 的缩写。 veth设备总是成对的, 因此我们称之为veth pair。 veth pair一端发送的数据会在另外一端接收, 非常像Linux的双向管道。 根据这一特性, veth pair常被用于跨network namespace之间的通信, 即分别将veth pair的两端放在不同的namespace里
118+
![[Pasted image 20250903152200.png]]
119+
前文已经提到, 仅有veth pair设备, 容器是无法访问外部网络的。为什么呢? 因为从容器发出的数据包, 实际上是直接进了veth pair设备的协议栈。 如果容器需要访问网络, 则需要使用网桥等技术将veth pair设备接收的数据包通过某种方式转发出去。
120+
可以使用 `ip link list` 查看创建的veth pair
121+
122+
veth pair设备的原理较简单, **就是向veth pair设备的一端输入数据,数据通过内核协议栈后从veth pair的另一端出来**
123+
![[Pasted image 20250903153424.png]]
124+
veth网卡内核实现代码,任意一端网卡收到信息,都会无串改的发送到另一端
125+
```c
126+
static netdev_tx_t veth_xmit(struct sk_buff *skb, struct net_device *dev)
127+
{
128+
struct veth_priv *rcv_priv, *priv = netdev_priv(dev);
129+
struct veth_rq *rq = NULL;
130+
struct netdev_queue *txq;
131+
struct net_device *rcv;
132+
int length = skb->len;
133+
bool use_napi = false;
134+
int ret, rxq;
135+
136+
rcu_read_lock();
137+
rcv = rcu_dereference(priv->peer);
138+
if (unlikely(!rcv) || !pskb_may_pull(skb, ETH_HLEN)) {
139+
kfree_skb(skb);
140+
goto drop;
141+
}
142+
143+
rcv_priv = netdev_priv(rcv);
144+
rxq = skb_get_queue_mapping(skb);
145+
if (rxq < rcv->real_num_rx_queues) {
146+
rq = &rcv_priv->rq[rxq];
147+
148+
/* The napi pointer is available when an XDP program is
149+
* attached or when GRO is enabled
150+
* Don't bother with napi/GRO if the skb can't be aggregated
151+
*/
152+
use_napi = rcu_access_pointer(rq->napi) &&
153+
veth_skb_is_eligible_for_gro(dev, rcv, skb);
154+
}
155+
156+
skb_tx_timestamp(skb);
157+
158+
ret = veth_forward_skb(rcv, skb, rq, use_napi);
159+
switch (ret) {
160+
case NET_RX_SUCCESS: /* same as NETDEV_TX_OK */
161+
if (!use_napi)
162+
dev_sw_netstats_tx_add(dev, 1, length);
163+
else
164+
__veth_xdp_flush(rq);
165+
break;
166+
case NETDEV_TX_BUSY:
167+
/* If a qdisc is attached to our virtual device, returning
168+
* NETDEV_TX_BUSY is allowed.
169+
*/
170+
txq = netdev_get_tx_queue(dev, rxq);
171+
172+
if (qdisc_txq_has_no_queue(txq)) {
173+
dev_kfree_skb_any(skb);
174+
goto drop;
175+
}
176+
/* Restore Eth hdr pulled by dev_forward_skb/eth_type_trans */
177+
__skb_push(skb, ETH_HLEN);
178+
/* Depend on prior success packets started NAPI consumer via
179+
* __veth_xdp_flush(). Cancel TXQ stop if consumer stopped,
180+
* paired with empty check in veth_poll().
181+
*/
182+
netif_tx_stop_queue(txq);
183+
smp_mb__after_atomic();
184+
if (unlikely(__ptr_ring_empty(&rq->xdp_ring)))
185+
netif_tx_wake_queue(txq);
186+
break;
187+
case NET_RX_DROP: /* same as NET_XMIT_DROP */
188+
drop:
189+
atomic64_inc(&priv->dropped);
190+
ret = NET_XMIT_DROP;
191+
break;
192+
default:
193+
net_crit_ratelimited("%s(%s): Invalid return code(%d)",
194+
__func__, dev->name, ret);
195+
}
196+
rcu_read_unlock();
197+
198+
return ret;
199+
}
200+
```
201+
202+
203+
#### 容器与host veth pair的关系
204+
容器中的eth0实际上和外面host上的某个veth是成对的(pair) 关系, 那么,有没有办法知道host上的vethxxx和哪个container eth0是成对的关系呢?
205+
206+
- 如果没有ip命令可以通过 `iflink` 关系查看
207+
首先在容器中查看
208+
```bash
209+
~ $ cat /sys/class/net/eth0/iflink
210+
38
211+
```
212+
然后再主机上遍历 `/sys/class/net` 下面的全部子目录中的ifindex的值和容器中查出来的iflink值一样的veth名,这个veth就是和容器里面成对的veth
213+
214+
- 如果能使用ip命令,可以通过ip命令查看
215+
先查看容器内部eth0的网卡信息
216+
```bash
217+
/prometheus $ ip link show eth0
218+
4: eth0@if38: <BROADCAST,MULTICAST,UP,LOWER_UP,M-DOWN> mtu 1500 qdisc noqueue
219+
link/ether 4a:50:c6:a2:74:94 brd ff:ff:ff:ff:ff:ff
220+
```
221+
这里的4是eth0接口的index,38就是和他成对的veth的index
222+
223+
然后去主机上,查找index为38的网卡
224+
```bash
225+
[root@andrew ~]# ip link show |grep ^38
226+
38: caliee699845661@if4: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP mode DEFAULT group default
227+
```
228+
229+
- 如果容器有 `ethtool`工具
230+
先查看容器网卡eth0的peer网卡index
231+
```bash
232+
# ethtool -S eth0
233+
NIC statistics:
234+
peer_ifindex: 38
235+
```
236+
然后在主机上查看index为38的网卡
237+
```bash
238+
ip addr
239+
```
240+
241+
### linux bridge - 连接你我他
242+
两个network namespace可以通过veth pair连接, 但要做到两个以上network namespace相互连接, veth pair就显得捉襟见肘了。
243+
244+
我们在计算机网络课本上学的网桥正如其字面含义所描述的,有“牵线搭桥”之意, 用于连接两个不同的局域网, 是网线的延伸。 网桥是二层网络设备, 两个端口分别有一条独立的交换信道, 不共享一条背板总线, 可隔离冲突域。 网桥比集线器(hub) 性能更好, 集线器上各端口都是共享同一条背板总线的。 后来, 网桥被具有更多端口、 可隔离冲突域的交换机(switch) 所取代。
245+
246+
顾名思义, Linux bridge就是Linux系统中的网桥, 但是Linux bridge的行为更像是一台虚拟的网络交换机, 任意的真实物理设备(例如eth0) 和虚拟设备(例如, 前面讲到的veth pair和后面即将介绍的tap设备) 都可以连接到Linux bridge上。 需要注意的是, Linux bridge不能跨机连接网络设备。
247+
248+
Linux bridge与Linux上其他网络设备的区别在于, 普通的网络设备只有两端, 从一端进来的数据会从另一端出去。 例如, 物理网卡从外面网络中收到的数据会转发给内核协议栈, 而从协议栈过来的数据会转发到外面的物理网络中。 Linux bridge则有多个端口, 数据可以从任何端口进来, 进来之后从哪个口出去取决于目的MAC地址, 原理和物理交换机差不多。
249+
250+
#### linux bridge实践
251+
使用 iproute2如那件包里面的ip命令创建一个bridge
252+
```bash
253+
ip link add name br0 type bridge
254+
ip link set br0 up
255+
```
256+
除了使用ip命令,我们还可以使用bridge-utils软件包里面的brctl工具管理网桥,例如创建网桥
257+
```bash
258+
brctl addbr br0
259+
```
260+
261+
刚创建一个bridge时, 它是一个独立的网络设备, 只有一个端口连着协议栈, 其他端口什么都没连接, 这样的bridge其实没有任何实际功能
262+
![[Pasted image 20250903162541.png]]
263+
将eth0连接到 br0
264+
```bash
265+
ip link set dev veth0 master br0
266+
```
267+
同样也能使用 brctl 命令添加到一个设备到网桥上
268+
```bash
269+
brctl addif br0 veth0
270+
```
271+
设置成功之后就可以通过 bridge命令查看网桥上都有哪些设备
272+
```bash
273+
bridge link
274+
```
275+
也可以使用brctl命令显示当前存在的网桥及其连接的网络端口
276+
```bash
277+
brctl show
278+
```
279+
经过上述设置可以看到上述网络拓扑变为了
280+
![[Pasted image 20250903164021.png]]
281+
br0和veth0相连之后发生了如下变化:
282+
- br0和veth0之间连接起来了, 并且是双向的通道
283+
- 协议栈和veth0之间变成了单通道, 协议栈能发数据给veth0, 但veth0从外面收到的数据不会转发给协议栈;
284+
- br0的MAC地址变成了veth0的MAC地址。
285+
286+
#### 把ip让给linux bridge
287+
通过上面的分析可以看出, 给veth0配置IP没有意义, 因为就算协议栈传数据包给veth0, 回程报文也回不来。 这里我们就把veth0的IP地址“让给”Linux bridge:
288+
```bash
289+
ip addr del 1.2.3.101 dev eth0
290+
ip addr add 1.2.3.101 dev br0
291+
```
292+
![[Pasted image 20250903164708.png]]
293+
#### 将物理网卡添加到Linux bridge
294+
将物理网卡eth0添加到bridge
295+
```bash
296+
ip link set dev eth0 master br0
297+
298+
bridge link
299+
2: ens18: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 master br0 state forwarding priority 32 cost 100
300+
```
301+
Linux bridge不会区分接入进来的到底是物理设备还是虚拟设备, 对它来说没有区别。因此, eth0加入br0后, 落得和上面veth0一样的“下场”, 从外面网络收到的数据包将无条件地转发给br0, 自己变成了一根网线。
302+
303+
### linux bridge 在虚拟化中的应用
304+
305+
306+
307+
308+
309+
310+
79311
### Docker的网络实现
80312

81313
标准的Docker支持以下4类网络模式:

0 commit comments

Comments
 (0)