在 Linux 中模拟网络层连通性

CompTIABeginner
立即练习

介绍

在本实验中,你将探索 Linux 环境下网络层连接的基本原理。通过使用 Docker 容器模拟两个独立的节点,你将学习如何使用 ip 命令手动分配静态 IP 地址。然后,你将使用 ping 工具测试这些节点在共享虚拟网络上的直接通信路径。

首先在同一子网配置节点,你将观察到成功的连接。然后,你将重新配置一个节点到不同的子网,以观察可预见的通信失败——“Destination Host Unreachable”(目标主机不可达)错误。这个实践练习清晰、实际地展示了 IP 子网如何控制直接通信,以及为什么不同逻辑网络上的设备在没有路由器的情况下无法连接。

准备双节点实验环境

在此步骤中,你将为我们的实验设置基础环境。我们不使用完整的虚拟机,而是利用轻量级且隔离的 Docker 容器来模拟两个独立的网络节点。为了使它们之间能够通信,我们将首先创建一个自定义的 Docker 网络。这个虚拟网络就像一个物理网络交换机,将我们的两个节点置于同一个共享通信段上。

让我们拉取用于节点的 Ubuntu 22.04 Docker 镜像。

docker pull ubuntu:22.04

首先,让我们创建两个节点将使用的虚拟网络。我们将它命名为 lab_net,并为其分配一个特定的 IP 地址范围,这在后续步骤中很重要。

在你的终端中执行以下命令:

docker network create --subnet=192.168.56.0/24 lab_net

此命令指示 Docker 创建一个名为 lab_net 的新桥接网络,并将其配置为使用 192.168.56.0/24 子网。你将看到一个长而唯一的网络 ID 作为输出,确认其创建成功。

e8c1c2a3b4d5e6f7a8b9c0d1e2f3a4b5c6d7e8f9a0b1c2d3e4f5a6b7c8d9e0f1

接下来,我们将启动第一个节点,一个名为 node1 的容器,并将其连接到 lab_net 网络。

docker run -d --name node1 --network lab_net --cap-add=NET_ADMIN ubuntu:22.04 sleep infinity

让我们分解一下这个命令:

  • docker run: 创建和启动新容器的标准命令。
  • -d: 以“分离”模式运行容器,意味着它在后台运行。
  • --name node1: 为我们的容器分配一个简单易记的名称。
  • --network lab_net: 将容器连接到我们之前创建的虚拟网络。
  • --cap-add=NET_ADMIN: 授予容器 NET_ADMIN 能力,这对于修改网络设置(如添加 IP 地址)是必需的。
  • ubuntu:22.04: 我们正在使用的 Docker 镜像,它提供了一个标准的 Ubuntu 环境。
  • sleep infinity: 一个简单的命令,会一直运行以保持容器处于活动状态。

现在,使用类似的命令启动第二个节点 node2

docker run -d --name node2 --network lab_net --cap-add=NET_ADMIN ubuntu:22.04 sleep infinity

最后,让我们验证两个节点是否都正常运行。docker ps 命令会列出所有当前正在运行的容器。

docker ps

你应该会看到类似以下的输出,确认 node1node2 都已启动并正在运行。

CONTAINER ID   IMAGE          COMMAND                  CREATED          STATUS          PORTS     NAMES
a1b2c3d4e5f6   ubuntu:22.04   "sleep infinity"         5 seconds ago    Up 4 seconds              node2
g7h8i9j0k1l2   ubuntu:22.04   "sleep infinity"         15 seconds ago   Up 14 seconds             node1

现在两个节点都运行在同一个虚拟网络上,我们的实验环境已经准备就绪。在接下来的步骤中,我们将配置它们的 IP 地址并测试它们的连通性。

使用 ip addr 为第一个节点配置静态 IP

在此步骤中,你将为我们的第一个容器 node1 分配一个静态 IP 地址。静态 IP 是手动配置且不会改变的地址,与动态 IP 不同,动态 IP 通常由 DHCP 服务器自动分配。为了我们的模拟,使用静态 IP 可以让我们精确控制网络配置。

我们将从主机上的主终端执行所有操作,使用 docker exec 命令在 node1 容器 内部 运行命令。

首先,基础的 ubuntu:22.04 镜像非常精简。我们需要安装必要的网络工具。让我们开始在 node1 容器内更新软件包列表:

docker exec node1 apt-get update

你将看到容器获取最新软件包信息时的输出。

接下来,安装 iproute2 包(提供 ip 命令)和 iputils-ping 包(提供我们稍后将使用的 ping 命令)。

docker exec node1 apt-get install -y iproute2 iputils-ping

现在工具已安装,让我们检查 node1 当前的网络配置。标准 Docker 容器内的网络接口通常命名为 eth0

docker exec node1 ip addr show eth0

输出将显示 eth0 接口的详细信息。你可能会看到一个已由 Docker 内部 DHCP 服务器分配的 IP 地址(例如 192.168.56.2)。我们将添加自己的静态 IP。

9: eth0@if10: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default
    link/ether 02:42:c0:a8:38:02 brd ff:ff:ff:ff:ff:ff link-netnsid 0
    inet 192.168.56.2/24 brd 192.168.56.255 scope global eth0
       valid_lft forever preferred_lft forever

现在,让我们为 node1 分配静态 IP 地址 192.168.56.10/24 是 CIDR 表示法,代表子网掩码 255.255.255.0,定义了网络的范围。

docker exec node1 ip addr add 192.168.56.10/24 dev eth0

如果成功,此命令不应产生任何输出。要确认更改,请再次运行 ip addr show eth0 命令:

docker exec node1 ip addr show eth0

你现在应该看到你的新静态 IP 地址与原始地址一起列出,并标记为 secondary。这确认 node1 已配置为地址 192.168.56.10

9: eth0@if10: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default
    link/ether 02:42:c0:a8:38:02 brd ff:ff:ff:ff:ff:ff link-netnsid 0
    inet 192.168.56.2/24 brd 192.168.56.255 scope global eth0
       valid_lft forever preferred_lft forever
    inet 192.168.56.10/24 scope global secondary eth0
       valid_lft forever preferred_lft forever

在第二个节点上配置同一子网的静态 IP

在此步骤中,我们将为第二个节点 node2 配置一个静态 IP 地址。为了让两个设备在没有路由器的情况下直接通信,它们必须位于同一个逻辑子网中。我们将为 node2 分配一个来自与 node1 使用的 192.168.56.0/24 相同的子网的 IP 地址。这种设置在逻辑上模拟了两台通过交叉线连接的 PC,它们都属于同一个本地网络。

首先,就像我们为 node1 所做的那样,我们需要在 node2 容器内部安装必要的网络工具。开始更新软件包列表:

docker exec node2 apt-get update

接下来,在 node2 上安装 iproute2iputils-ping 包:

docker exec node2 apt-get install -y iproute2 iputils-ping

工具安装完毕后,我们现在可以为 node2 分配一个静态 IP 地址。我们将使用 192.168.56.11,它与 node1192.168.56.10)位于同一子网,但是一个唯一的地址。

docker exec node2 ip addr add 192.168.56.11/24 dev eth0

此命令将 IP 地址 192.168.56.11/24 子网掩码添加到 node2 容器的 eth0 网络接口。如果命令成功,它将不产生任何输出。

为了验证 IP 地址是否已正确分配,让我们检查 node2 的网络配置:

docker exec node2 ip addr show eth0

输出现在应该显示新添加的静态 IP 地址,标记为 secondary。这确认 node2 已正确配置,并已准备好进行下一步测试连通性。

11: eth0@if12: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default
    link/ether 02:42:c0:a8:38:03 brd ff:ff:ff:ff:ff:ff link-netnsid 0
    inet 192.168.56.3/24 brd 192.168.56.255 scope global eth0
       valid_lft forever preferred_lft forever
    inet 192.168.56.11/24 scope global secondary eth0
       valid_lft forever preferred_lft forever

使用 ping 命令验证节点间的直接连通性

在此步骤中,你将测试 node1node2 之间的网络连通性。现在两个节点都在同一子网拥有 IP 地址,它们应该能够直接通信。我们将使用 ping 命令,这是一个基础的网络工具,它向目标主机发送一个小型数据包(ICMP Echo Request)并等待回复。成功的回复确认了两个设备之间存在网络路径。

这个成功的测试类似于用交叉线连接两台 PC,两台设备都在同一个本地网络上,并且可以互相直接通信。

首先,让我们尝试从 node1 ping node2。我们将在 node1 容器内执行 ping 命令,目标是 node2 的 IP 地址 192.168.56.11

docker exec node1 ping -c 4 192.168.56.11

让我们分解一下这个命令:

  • docker exec node1: 在 node1 容器内执行后续命令。
  • ping: 用于测试连通性的工具。
  • -c 4: 一个标志,告诉 ping 发送 4 个数据包后停止。没有这个标志,ping 会无限运行。
  • 192.168.56.11: node2 的目标 IP 地址。

你应该看到成功的输出,收到来自 node2 的回复。

PING 192.168.56.11 (192.168.56.11) 56(84) bytes of data.
64 bytes from 192.168.56.11: icmp_seq=1 ttl=64 time=0.123 ms
64 bytes from 192.168.56.11: icmp_seq=2 ttl=64 time=0.087 ms
64 bytes from 192.168.56.11: icmp_seq=3 ttl=64 time=0.091 ms
64 bytes from 192.168.56.11: icmp_seq=4 ttl=64 time=0.085 ms

--- 192.168.56.11 ping statistics ---
4 packets transmitted, 4 received, 0% packet loss, time 3074ms
rtt min/avg/max/mdev = 0.085/0.096/0.123/0.015 ms

"4 packets transmitted, 4 received, 0% packet loss" 这行确认了连接正在工作。现在,让我们通过从 node2 ping node1 来验证双向连接。

docker exec node2 ping -c 4 192.168.56.10

同样,你应该看到一系列成功的回复,确认通信是双向的。

PING 192.168.56.10 (192.168.56.10) 56(84) bytes of data.
64 bytes from 192.168.56.10: icmp_seq=1 ttl=64 time=0.099 ms
64 bytes from 192.168.56.10: icmp_seq=2 ttl=64 time=0.088 ms
64 bytes from 192.168.56.10: icmp_seq=3 ttl=64 time=0.092 ms
64 bytes from 192.168.56.10: icmp_seq=4 ttl=64 time=0.089 ms

--- 192.168.56.10 ping statistics ---
4 packets transmitted, 4 received, 0% packet loss, time 3081ms
rtt min/avg/max/mdev = 0.088/0.092/0.099/0.004 ms

成功!两个节点可以互相通信,就像它们是同一本地网络段上的两台计算机一样。

重新配置第二个节点到不同的子网

在此步骤中,我们将故意破坏两个节点之间的直接连通性。我们将通过将 node2 重新配置到与 node1 完全不同的 IP 子网来实现这一点。这模拟了一个场景:两个设备物理连接,但逻辑上被划分为不同的网络。

当前,node1 位于 192.168.56.0/24 子网。我们现在将 node2 移动到 192.168.58.0/24 子网。由于第三个数字(八位字节)不同(56 对比 58),它们被视为独立的子网。

为了确保 node2 在新网络上完全隔离,我们必须首先移除其 eth0 接口上的所有现有 IP 地址。这包括我们之前添加的静态 IP 和 Docker 自动分配的原始 IP。ip addr flush 命令是完成此任务的正确工具。

docker exec node2 ip addr flush dev eth0

此命令将从 eth0 移除所有 IP 配置,确保一个干净的开始。如果成功,它不应产生任何输出。

现在,让我们添加属于新子网的新 IP 地址 192.168.58.11

docker exec node2 ip addr add 192.168.58.11/24 dev eth0

为了确认更改,让我们再次检查 node2 的网络配置。

docker exec node2 ip addr show eth0

你将看到所有旧 IP 地址都已消失,只剩下新的 IP 地址(192.168.58.11)。这确认 node2 不再位于 192.168.56.0/24 子网。

11: eth0@if12: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default
    link/ether 02:42:c0:a8:38:03 brd ff:ff:ff:ff:ff:ff link-netnsid 0
    inet 192.168.58.11/24 scope global eth0
       valid_lft forever preferred_lft forever

现在 node1 位于 192.168.56.0/24 子网,而 node2 位于 192.168.58.0/24 子网,它们现在在逻辑上是隔离的。

测试连通性并观察失败情况

在最后这个步骤中,我们将再次尝试在节点之间进行 ping 测试。由于 node2 现在已真正隔离在新子网中,我们预计通信会失败,但会以两种不同且重要的方式失败,这揭示了网络层是如何运作的。

首先,让我们尝试从 node1 ping node2 的新 IP 地址(192.168.58.11)。

docker exec node1 ping -c 4 192.168.58.11

观察输出。该命令将超时,导致 100% 的数据包丢失。

PING 192.168.58.11 (192.168.58.11) 56(84) bytes of data.

--- 192.168.58.11 ping statistics ---
4 packets transmitted, 0 received, 100% packet loss, time 3076ms

此超时发生是因为 node1 仍然有一个到 Docker 网络的路由,因此它会发送 ping 数据包。网络将其转发给 node2。然而,由于 node2 不再位于该网络上并且没有回程路由,它无法发送回复。来自 node1 的 ping 请求从未收到响应。

接下来,让我们测试另一个方向。尝试从 node2 ping node1

docker exec node2 ping -c 4 192.168.56.10

这次,你将几乎立即看到一个不同的错误消息。

ping: connect: Network is unreachable

ping: connect: Network is unreachable 这个消息非常重要。这是来自 node2 上操作系统的即时响应。它意味着操作系统检查了其路由表,发现目标 192.168.56.10 位于它没有路径的网络上,并拒绝尝试发送数据包。这是使用 ip addr flush 的直接结果,它清除了所有路由,使容器与其他网络完全隔离。

这个实验成功地展示了 IP 子网划分和路由的关键作用。当设备位于同一子网时,它们可以直接通信。当它们位于不同子网时,需要像路由器这样的第 3 层设备以及适当的路由来在它们之间转发流量。

总结

在这个实验中,你学会了使用连接到自定义桥接网络的 Docker 容器来模拟一个双节点网络环境。你练习了使用 ip addr 命令在网络接口上配置静态 IP 地址这一重要的 Linux 技能。通过为两个节点分配同一子网的 IP 地址,你成功地使用 ping 工具验证了它们之间的直接第 3 层连通性,展示了在本地网络段上通信的基本要求。

通过在完全刷新原始网络配置后,用不同子网的 IP 地址重新配置一个节点,该实验进一步阐释了一个关键的网络概念。后续通过 ping 进行通信的尝试以两种不同的结果失败:一个方向超时,另一个方向出现“目标主机不可达”错误。这一结果有效地表明,位于不同逻辑子网的节点在没有路由器的情况下无法直接通信,并突出了路由表和接口配置如何影响网络连通性。