天天讲路由,那 Linux 路由到底咋实现的!?

2023-05-16

大家好,我是飞哥。

容器是一种新的虚拟化技术,每一个容器都是一个逻辑上独立的网络环境。Linux 上提供了软件虚拟出来的二层交换机 Bridge 可以解决同一个宿主机上多个容器之间互连的问题,但这是不够的。二层交换无法解决容器和宿主机外部网络的互通。

容器肯定是需要和宿主机以外的外部网络互通才具备实用价值的。比如在 Kubernets 中,就要求所有的 pod 之间都可以互通。相当于在原先物理机所组成的网络之上,要再建一个互通的虚拟网络出来。这就是 Overlay 网络的概念,用一个简单的示例图表示如下。

0f688f4bca9577712dae823df60addb4.png

回想在传统物理物理网络中,不同子网之间的服务器是如何互联起来的呢,没错,就是在三层工作的路由器,也叫网关。路由器使得数据包可以从一个子网中传输到另一个子网中,进而实现更大范围的网络互通。如下图所示,一台路由器将 192.168.0.x 和 192.168.1.x 两个子网连接了起来。

730cd40b6cce94828d59bec465c8787d.png

在容器虚拟化网络中,自然也需要这么一个角色,将容器和宿主机以外的网络连接起来。其实 Linux 天生就具备路由的功能,只是在云原生时代,它的路由功能再一次找到了用武之地。在容器和外部网络通信的过程中,Linux 就又承担起路由器的角色,实现容器数据包的正确转发和投递。

2b1a83a99c7cd880a49102f17aa420f9.png

在各种基于容器的云原生技术盛行的今天,再次回头深刻理解路由工作原理显得非常有必要,而且也非常的有价值。今天,我们就再来强化一下 Linux 上的路由知识!

一、什么时候需要路由

先来聊聊 Linux 在什么情况下需要路由过程。其实在发送数据时和接收数据时都会涉及到路由选择,为什么?我们挨个来看。

1.1 发送数据时选路

Linux 之所以在发送数据包的时候需要进行路由选择,这是因为服务器上是可能会有多张网卡设备存在的。数据包在发送的时候,一路通过用户态、TCP 层到了 IP 层的时候,就要进行路由选择,以决定使用哪张网卡设备把数据包送出去。详细过程参见25 张图,一万字,拆解 Linux 网络包发送过程

4c8f22ef7ab5cfaecfbfcf1d397ff07b.png

来大致过一下路由相关源码源码。网络层发送的入口函数是 ip_queue_xmit。

//file: net/ipv4/ip_output.c
int ip_queue_xmit(struct sk_buff *skb, struct flowi *fl)
{
 // 路由选择过程
 // 选择完后记录路由信息到 skb 上
 rt = (struct rtable *)__sk_dst_check(sk, 0);
 if (rt == NULL) {
  // 没有缓存则查找路由项
  rt = ip_route_output_ports(...);
  sk_setup_caps(sk, &rt->dst);
 }
 skb_dst_set_noref(skb, &rt->dst);
 ...
 //发送
 ip_local_out(skb);
}

在 ip_queue_xmit 里我们开头就看到了路由项查找, ip_route_output_ports 这个函数中完成路由选择。路由选择就是到路由表中进行匹配,然后决定使用哪个网卡发送出去。

Linux 中最多可以有 255 张路由表,其中默认情况下有 local 和 main 两张。使用 ip 命令可以查看路由表的具体配置。拿 local 路由表来举例。

#ip route list table local
local 10.143.x.y dev eth0 proto kernel scope host src 10.143.x.y
local 127.0.0.1 dev lo proto kernel scope host src 127.0.0.1

1.2 接收数据时选路

没错,接收数据包的时候也需要进行路由选择。这是因为 Linux 可能会像路由器一样工作,将收到的数据包通过合适的网卡将其转发出去。

Linux 在 IP 层的接收入口 ip_rcv 执行后调用到 ip_rcv_finish。在这里展开路由选择。如果发现确实就是本设备的网络包,那么就通过 ip_local_deliver 送到更上层的 TCP 层进行处理。

f0960baec841a15cc77a1a87c6cff58e.png

如果路由后发现非本设备的网络包,那就进入到 ip_forward 进行转发,最后通过 ip_output 发送出去。

7eefad5b0aba43f2b9a2a3e22789f9ba.png

具体的代码如下。

//file: net/ipv4/ip_input.c
static int ip_rcv_finish(struct sk_buff *skb){
    ...
    if (!skb_dst(skb)) {
        int err = ip_route_input_noref(skb, iph->daddr, iph->saddr,
                           iph->tos, skb->dev);
        ...
    }
    ...
    return dst_input(skb);
}

其中 ip_route_input_noref 就是在进行路由查找。

//file: net/ipv4/route.c
int ip_route_input_noref(struct sk_buff *skb, __be32 daddr, __be32 saddr,
    u8 tos, struct net_device *dev)
{
 ...
 res = ip_route_input_slow(skb, daddr, saddr, tos, dev);
 return res;
}

这里记着 ip_route_input_slow 就行了,后面我们再看。

1.3 linux 路由小结

路由在内核协议栈中的位置可以用如下一张图来表示。

cb5696841f7bb8ea2a7cf9f1356611db.png

网络包在发送的时候,需要从本机的多个网卡设备中选择一个合适的发送出去。网络包在接收的时候,也需要进行路由选择,如果是属于本设备的包就往上层送到网络层、传输层直到 socket 的接收缓存区中。如果不是本设备上的包,就选择合适的设备将其转发出去。

二、Linux 的路由实现

2.1 路由表

路由表(routing table)在内核源码中的另外一个叫法是转发信息库(Forwarding Information Base,FIB)。所以你在源码中看到的 fib 开头的定义基本上就是和路由表相关的功能。

其中路由表本身是用 struct fib_table 来表示的。

//file: include/net/ip_fib.h
struct fib_table {
 struct hlist_node tb_hlist;
 u32   tb_id;
 int   tb_default;
 int   tb_num_default;
 unsigned long  tb_data[0];
};

所有的路由表都通过一个 hash - fib_table_hash 来组织和管理。它是放在网络命名空间 net 下的。这也就说明每个命名空间都有自己独立的路由表

a688349fc2d253066d7babf99e90499a.png

//file:include/net/net_namespace.h
struct net {
 struct netns_ipv4 ipv4;
 ...
}

//file: include/net/netns/ipv4.h
struct netns_ipv4 {
 // 所有路由表 
 struct hlist_head *fib_table_hash;

 // netfilter
 ...
}

在默认情况下,Linux 只有 local 和 main 两个路由表。如果内核编译时支持策略路由,那么管理员最多可以配置  255 个独立的路由表。

如果你的服务器上创建了多个网络命名空间的话,那么就会存在多套路由表。以除了默认命名网络空间外,又创了了一个新网络命名空间的情况为例,路由表在整个内核数据结构中的关联关系总结如下图所示。

eab63a3e4332c27cc1b811c79116d19c.png

2.2 路由查找

在上面的小节中我们看到,发送过程调用 ip_route_output_ports 来查找路由,接收过程调用 ip_route_input_slow 来查找。但其实这两个函数都又最终会调用到 fib_lookup 这个核心函数,源码如下。

//file: net/ipv4/route.c
struct rtable *__ip_route_output_key(struct net *net, struct flowi4 *fl4)
{
 ...
 // 进入 fib_lookup
 if (fib_lookup(net, fl4, &res)) {
 }
}

//file: net/ipv4/route.c
static int ip_route_input_slow(struct sk_buff *skb, __be32 daddr, __be32 saddr,
          u8 tos, struct net_device *dev)
{
 ...
 // 进入 fib_lookup
 err = fib_lookup(net, &fl4, &res);
}

我们来看下 fib_loopup 都干了啥。为了容易理解,我们只看一下不支持多路由表版本的 fib_lookup。

//file: include/net/ip_fib.h
static inline int fib_lookup(struct net *net, const struct flowi4 *flp,
        struct fib_result *res)
{
 struct fib_table *table;

 table = fib_get_table(net, RT_TABLE_LOCAL);
 if (!fib_table_lookup(table, flp, res, FIB_LOOKUP_NOREF))
  return 0;

 table = fib_get_table(net, RT_TABLE_MAIN);
 if (!fib_table_lookup(table, flp, res, FIB_LOOKUP_NOREF))
  return 0;
 return -ENETUNREACH;
}

这个函数就是依次到 local 和 main 表中进行匹配,匹配到后就返回,不会继续往下匹配。从上面可以看到 local 表的优先级要高于 main 表,如果 local 表中找到了规则,则路由过程就结束了。

这也就是很多同学说为什么 ping 本机的时候在 eth0 上抓不到包的根本原因。所有命中 local 表的包都会被送往 loopback 设置,不会过 eth0。

三、路由的使用方法

3.1 开启转发路由

在默认情况下,Linux 上的转发功能是关闭的,这时候 Linux 发现收到的网络包不属于自己就会将其丢弃。

但在某些场景下,例如对于容器网络来说,Linux 需要转发本机上其它网络命名空间中过来的数据包,需要手工开启转发。如下这两种方法都可以。

# sysctl -w net.ipv4.ip_forward=1
# sysctl net.ipv4.conf.all.forwarding=1

开启后,Linux 就能像路由器一样对不属于本机(严格地说是本网络命名空间)的 IP 数据包进行路由转发了。

3.2 查看路由表

在默认情况下,Linux 只有 local 和 main 两个路由表。如果内核编译时支持策略路由,那么管理员最多可以配置  255 个独立的路由表。在 centos 上可以通过以下方式查看是否开启了 CONFIG_IP_MULTIPLE_TABLES 多路由表支持。

# cat /boot/config-3.10.0-693.el7.x86_64 
CONFIG_IP_MULTIPLE_TABLES=y
...

所有的路由表按照从 0 - 255 进行编号,每个编号都有一个别名。编号和别名的对应关系在 /etc/iproute2/rt_tables 这个文件里可以查到。

# cat /etc/iproute2/rt_tables
255     local
254     main
253     default
0       unspec
200     eth0_table

查看某个路由表的配置,通过使用 ip route list table {表名} 来查看。

#ip route list table local
local 10.143.x.y dev eth0 proto kernel scope host src 10.143.x.y
local 127.0.0.1 dev lo proto kernel scope host src 127.0.0.1

如果是查看 main 路由表,也可以直接使用 route 命令

# route -n
Kernel IP routing table
Destination     Gateway         Genmask         Flags Metric Ref    Use Iface
10.0.0.0        10.*.*.254      255.0.0.0       UG    0      0        0 eth0
10.*.*.0        0.0.0.0         255.255.248.0   U     0      0        0 eth0

上面字段中的含义如下

  • Destination:目的地址,可以是一个具体的 IP,也可以是一个网段,和 Genmask 一起表示。

  • Gateway:网关地址,如果是 0.0.0.0 表示不需要经过网关。

  • Flags: U 表示有效,G 表示连接路由,H 这条规则是主机路由,而不是网络路由。

  • Iface:网卡设备,使用哪个网卡将包送过去。

上述结果中输出的第一条路由规则表示这台机器下,确切地说这个网络环境下,所有目标为 10.0.0.0/8(Genmask 255.0.0.0 表示前 8 位为子网掩码) 网段的网络包都要通过 eth0 设备送到 10...254 这个网关,由它再帮助转发。

第二条路由规则表示,如果目的地址是 10...0/21(Genmask 255.255.248.0 表示前 21 位为子网掩码)则直接通过 eth0 发出即可,不需要经过网关就可通信。

3.3 修改路由表

默认的 local 路由表是内核根据当前机器的网卡设备配置自动生成的,不需要手工维护。对于main 的路由表配置我们一般只需要使用 route add 命令就可以了,删除使用 route del。

修改主机路由

# route add -host 192.168.0.100 dev eth0 //直连不用网关
# route add -host 192.168.1.100 dev eth0 gw 192.168.0.254 //下一跳网关

修改网络路由

# route add -net 192.168.1.0/24 dev eth0 //直连不用网关
# route add -net 192.168.1.0/24 dev eth0 gw 10.162.132.110 //下一跳网关

也可以指定一条默认规则,不命中其它规则的时候会执行到这条。

# route add default gw 192.168.0.1 eth0

对于其它编号的路由表想要修改的话,就需要使用 ip route 命令了。这里不过多展开,只用 main 表举一个例子,有更多使用需求的同学请自行搜索。

# ip route add 192.168.5.0/24 via 10.*.*.110 dev eth0 table main

3.4 路由规则测试

在配置了一系列路由规则后,为了快速校验是否符合预期,可以通过 ip route get 命令来确认。

# ip route get 192.168.2.25
192.168.2.25 via 10.*.*.110 dev eth0 src 10.*.*.161
    cache

本文总结

在现如今各种网络虚拟化技术里,到处都能看着对路由功能的灵活应用。所以我们今天专门深入研究了一下 Linux 路由工作原理。

在 Linux 内核中,对于发送过程和接收过程都会涉及路由选择,其中接收过程的路由选择是为了判断是该本地接收还是将它转发出去。

8aa3b2e171971d48eec1165a8b61a86d.png

默认有 local 和 main 两个路由表,不过如果安装的 linux 开启了 CONFIG_IP_MULTIPLE_TABLES 选项的话,最多能支持 255 张路由表。

路由选择过程其实不复杂,就是根据各个路由表的配置找到合适的网卡设备,以及下一跳的地址,然后把包转发出去就算是完事。

通过合适地配置路由规则,容器中的网络环境和外部的通信不再是难事。通过大量地干预路由规则就可以实现虚拟网络互通。

9d28beedba98589cb94eedfa1e91ffcd.png

好了,今天的分享就到这里了,期待你的点赞、再看和转发~~

544dde1bda9feb12a96d60553948de9f.png

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

天天讲路由,那 Linux 路由到底咋实现的!? 的相关文章

  • 解决pip is configured with locations that require TLS/SSL问题

    python3 7安装 xff0c 解决pip is configured with locations that require TLS SSL问题 1 安装相关依赖 yum install gcc libffi devel zlib o
  • tensorflow 如何共享网络参数

    问题描述 今天想做一个实验 xff0c 需要用到两个网络共享一套参数 初步想来 xff0c 是想利用tensorflow的变量空间和命名空间来实现的 xff0c 就像 1 和 2 中提到的那样 xff0c 后来发现涉及类初始化的关系 xff
  • STL std::sort 源码分析

    转载自http feihu me blog 2014 sgi std sort 最近在看sort源码 xff0c 看到这篇博文很好 xff0c 转发作为记录 xff0c 转载侵权联系我删除 背景 在校期间 xff0c 为了掌握这些排序算法
  • STL vector map遍历失效,顺序容器和关联容器遍历时的erase()注意事项

    1 区别 关联容器 Associative Container xff1a 通过键 key 存储和读取元素的 xff08 vector list deque stack queue heap priority queue slist xff
  • 正向,反向,闪烁流水灯

    单片机 中简单的流水灯问题 include 34 reg52 h 34 void led delay void unsigned char i j for i 61 0 i lt 200 i 43 43 for j 61 0 j lt 22
  • DS18B20温度传感源代码

    本文是对DS18b20传感器 xff0c 驱动代码的编写 include lt reg52 h gt include lt intrins h gt define uint unsigned int define uchar unsigne
  • 归并(合并)排序

    归并排序 xff0c 一种比较排序 xff0c 通过对数组中的元素进行比较得出排序结果 时间复杂度 O nlogn 空间复杂度 O n 43 O logn 排序时间与输入无关 xff0c 最佳情况 xff0c 最坏情况都是如此 稳定 原理
  • 字符串加减计算

    描述 xff1a 通过键盘输入100以内正整数的加 减运算式 xff0c 请编写一个程序输出运算结果字符串 输入字符串的格式为 xff1a 操作数1 运算符 操作数2 xff0c 操作数 与 运算符 之间以一个空格隔开 补充说明 xff1a
  • C/C++中,为什么字符串可以赋值给字符指针变量,单引号和双引号的区别

    本文是通过几篇转帖的文章整理而成的 xff1a http www slyar com blog c quotation marks html http www cnblogs com KingOfFreedom archive 2012 1
  • 操作符优先级全列表,一览表

    在一个表达式中可能包含多个有不同运算符连接起来的 具有不同数据类型的数据对象 xff1b 由于表达式有多种运算 xff0c 不同的运算顺序可能得出不同结果甚至出现错误运算错误 xff0c 因为当表达式中含多种运算时 xff0c 必须按一定顺
  • 枚举类的赋值和基本操作

    枚举类型的定义 枚举类型 enumeration 是C 43 43 中的一种派生数据类型 xff0c 它是由用户定义的若干枚举常量的集合 定义格式 xff1a 枚举类型的定义格式为 xff1a enum lt 类型名 gt lt 枚举常量表
  • AttributeError: module ‘gym.envs.atari‘ has no attribute ‘atari_env‘ 解决方案

    问题描述 今天在执行以下代码时 xff1a is atari 61 hasattr gym envs 39 atari 39 and isinstance env unwrapped gym envs atari atari env Ata
  • linux 防火墙常用命令

    firewalld防火墙 CentOS 7 1 查看firewalld服务状态 systemctl status firewalld 出现 Active active running 高亮显示则表示是启动状态 出现 Active inact
  • 2020年百度之星·程序设计大赛-初赛三 补题/解题

    Title Discount题解 Game题解 Permutation题解 Intersection题解 ChessAntFightGraph Discount Problem Description 学皇来到了一个餐馆吃饭 他觉得这家餐馆
  • 实习前言

    2018年4 19正式实习入职科大讯飞 xff0c 18日下午到达科大讯飞股份有限公司 第一时间拎着行李赶到提前与HR约定好的时间下午3 30地点A2综合服务中心 xff0c 进行住宿安排 入住的是所有实习生住的某工业园 xff0c 离A1
  • 科大讯飞总裁办实习第1周

    正式实习第一周 xff0c 从4月23日开始 根据上周五灿武哥给的邮件任务为导向 xff0c 开始学习输入 对于ibpms设计器的学习 xff0c 灿武哥给了3份学习文档资料 xff0c 其实是针对不同使用人群的使用手册 xff08 针对管
  • 科大讯飞总裁办实习第二周

    五一放假回来第一天上班就是周三啦 xff0c 这周只需要上三天班 xff0c 在上下午间隙时间 xff0c 研读流程管理使用手册 xff0c 整理以下几个概念知识点 流程管理的目标 xff1a 使企业的流程运作从整体上更加顺畅 协调 xff
  • 科大讯飞总裁办实习第三周

    5 7号周一上班收到的第一个消息 xff1a 带我流程的瑞哥请假三天 经理在群里说这块找张杰确认 xff0c 突然就一个重担砸到我的身上了 之前一直站在瑞哥的身后 xff0c 需求来自于瑞哥直接吩咐 xff0c 现在直接需求来源于项目推进
  • 科大讯飞总裁办实习第5周

    5 21 周一上班首先补充试点推广流程文件详细信息 xff0c 上午共导出5 xff0c 6 xff0c 7 xff0c 8四个流程文件 上午经理在群里下达最后截止日期 xff0c 所有的规范 xff0c 流程 xff0c 文件都要周五之前
  • 科大讯飞实习第八周日志

    0611早上欲打算与业务部门尽快完善流程 xff0c 大早上联系那边的流程设计人员 xff0c 不巧的是他早上有会 xff0c 然后就自己看华为变革及管理流程框架 xff0c 下午一点半和宇婴哥一起参加了销委会商机研讨会议 xff0c 回来

随机推荐

  • PostgreSQL数据库导出建表语句的方法

    pg dump U postgres d dbname s gt sql txt
  • spyder导入tensorflow包

    一 xff0e spyder介绍 Anaconda中自带的集成开发环境用于科学计算还是蛮好的 xff0e 它和其他的Python开发环境相比 xff0c 它最大的优点就是模仿MATLAB的 工作空间 的功能 xff0c 可以很方便地观察和修
  • ValueError: Disable frame-skipping in the original env. 解决方案

    问题描述 今天试图在Atari上运行以下代码时 xff0c 出现了题目中的bug xff1a env 61 AtariPreprocessing env grayscale obs 61 True scale obs 61 True ter
  • OpenKylin适配和虚拟打印机

    最近在测国产OS客户端部分 首先客户端程序在CentOS全部使用没毛病 xff0c 但是CentOS桌面体验比较差 然后就试了UOS xff0c 在UOS上测试到打印这块花了很多时间 xff0c 碰到问题是CUPS有反应 xff0c 但是没
  • 基础命令整理

    1 who显示的是当前真正登录系统中的用户 16 05 59 root 64 localhost who ZT tty2 2021 11 03 10 45 tty2 ZT pts 1 2021 11 05 08 28 10 0 0 1 2
  • ubuntu apache2 配置安装ssl证书,https

    1 申请免费阿里证书 2 配置证书 在这里 xff0c 我假设你已经会配置基本的 etc apache2 sites available 000 default conf这个文件来达到已经可以通过 http 的方式来访问你的站点 在 etc
  • 今日头条2018校招笔试题之字符串的问题

    今日头条 xff0c 很干脆 xff0c 直接就四个编程 xff0c 一个改错 做的很烂 xff0c 只能来写一个题 字符串S由小写字母构成 xff0c 长度为N xff0c 定义一种操作 xff0c 每次都可以挑选字符串中任意的两个相邻字
  • ios 瀑布流

    瀑布流 xff0c 又称瀑布流式布局 是比较流行的一种 页面布局 xff0c 视觉表现为参差不齐的多栏布局 xff0c 随着页面滚动条向下滚动 xff0c 这种布局还会不断加载 数据块并附加至当前尾部 说明 xff1a xff08 1 xf
  • 旧电脑变废为宝成为nas

    老台式机1台 可用任意电脑一台 xff0c 用来调试nas U盘1个 xff0c 64M以上 黑群晖安装包 显示器 下载黑群晖安装工具包 xff1a http pan baidu com s 1eRSAwAQ 使用ChipEasy检查并记录
  • debian的初始化操作

    设置默认的编辑器为vim uppdate alternatives config editor 输入你选择的编辑器即可 配置visudo z ALL ALL ALL NOPASSWD ALL 增加开启termial的快捷键 系统设置 快捷键
  • go调用本地python代码

    go调用本地python代码 1 mac环境下测试 目录结构 xff1a go代码 xff1a xff08 windows没有python3命令 xff0c windows的话改成python即可 xff09 span class toke
  • (四) Docker之Dockerfile编写与指令解析,自定义镜像实战

    Docker之编写Dockerfile 1 Dockerfile介绍1 1 docker build1 2 dockerignore文件1 3 Dockerfile格式 2 Dockerfile构建过程解析2 1 Dockerfile内容基
  • 如何安装指定版本Pytorch:

    如何安装指定版本Pytorch 使用conda安装指定版本 conda install pytorch 61 0 1 10 c soumith 使用pip安装指定版本 pip install pytorch 61 61 0 1 10 如何查
  • gym ValueError: too many values to unpack (expected 4) 解决方案

    问题描述 今天在执行以下代码时出现了题述错误 xff1a new obs rew done info 61 self env step action new obs rew done info 61 self env step action
  • linux查看openjdk的安装的路径(环境变量)

    前言 xff1a 现在基本上linux为了避免版权问题都会默认的为你安装开源的openjdk xff0c 而不是jdk 有些时候需要运行一些环境需要用到jdk的环境变量 xff0c 本文就是简单描述下如何查看openjdk的环境变量 1 e
  • linux图形界面基本知识(X、X11、Xfree86、Xorg、GNOME、KDE之间的关系)

    转载 xff1a http apps hi baidu com share detail 11596555 LINUX初学者经常分不清楚linux和X之间 xff0c X和Xfree86之间 xff0c X和KDE xff0c GNOME等
  • windows xp远程连接

    本节将用到windows网络共享 xff0c 实现外网可以远程连接局域网内的任意主机 实验环境 两台windows xp虚拟机 xff08 内网 43 外网 xff09 xff0c 一台主机 配置外网虚拟机 首先 xff0c 为虚拟机添加两
  • 系统架构之三(业务运营支撑系统)

    本人从事过3年的移动业务运营支撑系统开发 xff0c 行业术语叫做boss系统 xff0c 后又转入游戏行业进行游戏开发 现设计一个业务运营支撑系统的架构如下 xff1a 详细解释各模块如下 xff1a gateway dispatch x
  • Debian下安装xfce4

    环境 Debian wheezy stable 步骤 1 第一张光盘安装Debian 速度快 xff0c 可定制 2 设置 etc apt sources list 去掉cdrom的路径 3 安装apt spy xff0c 搜索最符合自己的
  • 天天讲路由,那 Linux 路由到底咋实现的!?

    大家好 xff0c 我是飞哥 容器是一种新的虚拟化技术 xff0c 每一个容器都是一个逻辑上独立的网络环境 Linux 上提供了软件虚拟出来的二层交换机 Bridge 可以解决同一个宿主机上多个容器之间互连的问题 xff0c 但这是不够的