@@ -12,7 +12,7 @@ Kubernetes集群至少应该包含三个网络,如图网络环境所示。一
12
12
13
13
![ [ image-2025-01-27-01-07-54-828.png]]
14
14
15
- ## Docker网络基础
15
+ ## 云网络基础
16
16
17
17
Docker技术依赖于近年来Linux内核虚拟化技术的发展, 所以Docker对Linux内核有很强的依赖。 Docker使用到的技术有网络命名空间( Network Namespace) 、 Veth设备对、 网桥、 ipatables和路由。
18
18
@@ -76,6 +76,238 @@ ip link set dev veth3 up
76
76
ip link set < veth1> up
77
77
```
78
78
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
+
79
311
### Docker的网络实现
80
312
81
313
标准的Docker支持以下4类网络模式:
0 commit comments