部落格好读版
前言
近期在阅读《Docker 实战 6堂课:56个实验动手做,掌握 Linux 容器核心技术》这本书,书中涵盖了 Docker 的基础知识、容器技术、网路设定及 Linux 核心概念。透过学习这本书,我深入了解了许多以前未曾完整研究过的 Linux 知识,特别是在 Linux 网路处理的部分。因此,我希望借由这次机会,记录自己的操作,并用我自己的理解来解释其中的逻辑和知识点。
目标
以下是专案的主要目标:完全模拟在 Docker 上建立两个容器间的 bridge 网路。
- ns0 的网路请求必须能透过 veth0,经由 docker1 传送到 ns1,反之亦然。
- ns0 和 ns1 必须要能连到外网。
準备环境
我在 AWS EC2 启用了两个实例,network-test-1 和 network-test-2。network-test-1 作为主机进行各种实验和设置,network-test-2 则用来模拟和验证对外的网路情境,以帮助更全面地测试配置效果,特别是跨主机的网路连通性。
以下所有操作皆在 network-test-1 上进行。
建立 net namespace 与 bridge
当 Docker 容器使用 bridge 类型网路时,会为每个容器建立一个虚拟网路介面,并将这些网路介面放入专属的 net namespace。这样可以确保每个容器的网路环境互相隔离,从而增强安全性和灵活性。net namespace 是 Linux 提供的一种隔离机制,可以让不同的网路空间彼此独立。
首先,我们建立两个 net namespace 来模拟容器的网路隔离:
$ sudo ip netns add ns0
$ sudo ip netns add ns1
查看 network namespace:
$ ip netns list
---
ns1
ns0
新增名为 docker1 的桥接网路(bridge network):
$ sudo ip link add docker1 type bridge
# 查看网路介面
$ ip link list
---
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN mode DEFAULT group default qlen 1000
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
2: enX0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 9001 qdisc fq_codel state UP mode DEFAULT group default qlen 1000
link/ether 06:9d:02:88:52:9d brd ff:ff:ff:ff:ff:ff
altname eni-0cb768586b1414a54
altname device-number-0.0
3: docker1: <BROADCAST,MULTICAST> mtu 1500 qdisc noop state DOWN mode DEFAULT group default qlen 1000
link/ether fa:b6:c0:55:33:71 brd ff:ff:ff:ff:ff:ff
建立 veth pair
接下来,我们将建立两对 veth pair(虚拟网路介面对),并将它们分别与 docker1 配对。这些 veth pair 将用于在不同的 net namespace 之间建立虚拟连接,模拟容器之间的网路通讯:
$ sudo ip link add veth0 type veth peer name veth0-br
$ sudo ip link add veth1 type veth peer name veth1-br
查询当前的网路介面清单:
$ ip link list
---
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN mode DEFAULT group default qlen 1000
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
2: enX0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 9001 qdisc fq_codel state UP mode DEFAULT group default qlen 1000
link/ether 06:9d:02:88:52:9d brd ff:ff:ff:ff:ff:ff
altname eni-0cb768586b1414a54
altname device-number-0.0
3: docker1: <BROADCAST,MULTICAST> mtu 1500 qdisc noop state DOWN mode DEFAULT group default qlen 1000
link/ether fa:b6:c0:55:33:71 brd ff:ff:ff:ff:ff:ff
4: veth0-br@veth0: <BROADCAST,MULTICAST,M-DOWN> mtu 1500 qdisc noop state DOWN mode DEFAULT group default qlen 1000
link/ether 7e:56:1a:0a:5c:8b brd ff:ff:ff:ff:ff:ff
5: veth0@veth0-br: <BROADCAST,MULTICAST,M-DOWN> mtu 1500 qdisc noop state DOWN mode DEFAULT group default qlen 1000
link/ether 9a:0e:80:cc:ac:53 brd ff:ff:ff:ff:ff:ff
6: veth1-br@veth1: <BROADCAST,MULTICAST,M-DOWN> mtu 1500 qdisc noop state DOWN mode DEFAULT group default qlen 1000
link/ether 32:65:8e:22:01:80 brd ff:ff:ff:ff:ff:ff
7: veth1@veth1-br: <BROADCAST,MULTICAST,M-DOWN> mtu 1500 qdisc noop state DOWN mode DEFAULT group default qlen 1000
link/ether 62:31:3a:d4:65:b9 brd ff:ff:ff:ff:ff:ff
现在,我们要把 veth0-br 和 veth1-br 网路介面与 docker1 这个 bridge 连接起来,并且将 veth0 移动到 ns0,veth1 移动到 ns1。这一步是为了确保 ns0 和 ns1 之间的网路流量可以通过桥接网路 docker1,从而模拟容器之间的通讯。
以 veth0-br 为例,将 veth0-br 加入到桥接网路介面 docker1 中,让 veth0-br 成为桥接网路 docker1 的一部分,就像将多条网线插入同一个交换机中:
- 任何通过 veth0-br 的流量会被桥接到 docker1,就像数据包进入了交换机。
- 连接到同一桥接网路的其他介面也可以与 veth0-br 通讯,就像交换机内的网线可以互相通信。
可以将 docker1 想像成一个虚拟的交换机,负责将不同网路介面之间的数据包互相转发。
# 移动网路介面到指定 ns
$ sudo ip link set veth0 netns ns0
$ sudo ip link set veth1 netns ns1
# 将 veth0-br, veth1-br 添加到 docker1
$ sudo ip link set veth0-br master docker1
$ sudo ip link set veth1-br master docker1
查询当前的网路介面清单:
$ ip link list
---
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN mode DEFAULT group default qlen 1000
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
2: enX0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 9001 qdisc fq_codel state UP mode DEFAULT group default qlen 1000
link/ether 06:9d:02:88:52:9d brd ff:ff:ff:ff:ff:ff
altname eni-0cb768586b1414a54
altname device-number-0.0
3: docker1: <BROADCAST,MULTICAST> mtu 1500 qdisc noop state DOWN mode DEFAULT group default qlen 1000
link/ether fa:b6:c0:55:33:71 brd ff:ff:ff:ff:ff:ff
4: veth0-br@if5: <BROADCAST,MULTICAST> mtu 1500 qdisc noop master docker1 state DOWN mode DEFAULT group default qlen 1000
link/ether 7e:56:1a:0a:5c:8b brd ff:ff:ff:ff:ff:ff link-netns ns0
6: veth1-br@if7: <BROADCAST,MULTICAST> mtu 1500 qdisc noop master docker1 state DOWN mode DEFAULT group default qlen 1000
link/ether 32:65:8e:22:01:80 brd ff:ff:ff:ff:ff:ff link-netns ns1
由于 veth0 和 veth1 已经被移动到其他 net namespace 当中,所以不会出现在当前的清单中。还留着的 veth0-br 和 veth1-br,其属性中显示了刚刚设定的 master docker1。
设置 IP 与启动网路介面
接下来,我们要为 veth0 和 veth1 设定 IP:
$ sudo ip netns exec ns0 ip addr add 172.18.0.2/24 dev veth0
$ sudo ip netns exec ns1 ip addr add 172.18.0.3/24 dev veth1
ip netns exec ns0 这个指令的前缀,意思是进入 ns0 执行后面的指令,Kubernetes 也有类似的用法。
启动所有的网路介面:
$ sudo ip netns exec ns0 ip link set veth0 up
$ sudo ip netns exec ns1 ip link set veth1 up
$ sudo ip link set veth0-br up
$ sudo ip link set veth1-br up
$ sudo ip link set docker1 up
查看网路介面的状态。我们可以使用指令 ip addr 或 ip link。为了方便查看,对 ip 指令使用 -br flag(brief 的缩写),让输出结果更简洁:
# 查询 root namespace
$ ip -br link
---
lo UNKNOWN 00:00:00:00:00:00 <LOOPBACK,UP,LOWER_UP>
enX0 UP 06:9d:02:88:52:9d <BROADCAST,MULTICAST,UP,LOWER_UP>
docker1 UP fa:b6:c0:55:33:71 <BROADCAST,MULTICAST,UP,LOWER_UP>
veth0-br@if5 UP 7e:56:1a:0a:5c:8b <BROADCAST,MULTICAST,UP,LOWER_UP>
veth1-br@if7 UP 32:65:8e:22:01:80 <BROADCAST,MULTICAST,UP,LOWER_UP>
# 查询 ns0 namespace
$ sudo ip netns exec ns0 ip -br addr
lo DOWN
veth0@if4 UP 172.18.0.2/24 fe80::980e:80ff:fecc:ac53/64
# 查询 ns1 namespace
$ sudo ip netns exec ns1 ip -br addr
lo DOWN
veth1@if6 UP 172.18.0.3/24 fe80::6031:3aff:fed4:65b9/64
验证网路连线
理论上,现在已经打通了 veth0 与 veth1 之间的连线,来测试一下:
# 从 ns1 ping 位于 ns0 的 veth0 IP
$ sudo ip netns exec ns1 ping -c 2 172.18.0.2
---
PING 172.18.0.2 (172.18.0.2) 56(84) bytes of data.
64 bytes from 172.18.0.2: icmp_seq=1 ttl=64 time=0.077 ms
64 bytes from 172.18.0.2: icmp_seq=2 ttl=64 time=0.046 ms
--- 172.18.0.2 ping statistics ---
2 packets transmitted, 2 received, 0% packet loss, time 1052ms
所以,我们完成了吗?
乍看之下,我们已经完成了第一个目标,但实际上在安装 Docker 后,Docker 会自动修改 iptables 和 NAT 规则,这可能导致我们手动配置的网路连线出现冲突或无法正常运作的情况。因此,在有 Docker 的环境中,上述的网路连线设定可能无法正常运作。下一篇将探讨这些挑战,并试着解决这些问题,以确保网路配置的稳定性和预期行为。
参考
- https://ithelp.ithome.com.tw/articles/10307129