【网络】容器网络基础实践

Bennie

Namespace

容器技术中的 Namespace 是 Linux 内核的一个功能,它使得容器可以在同一主机上共享同一系统资源,但是各自拥有独立的视图和感知,从而可以实现容器的隔离和安全性。Namespace 分为以下几种类型:

Mount Namespace(挂载命名空间):每个容器都有自己的挂载命名空间,可以独立挂载文件系统,使得容器之间不会影响到其他容器的文件系统。一个容器的挂载命名空间改变不会影响到其他容器的挂载命名空间。

UTS Namespace(UTS 命名空间):每个容器都有自己的主机名和域名,使得容器之间的主机名和域名是隔离的。

PID Namespace(PID 命名空间):每个容器都有自己的进程命名空间,使得容器内的进程 ID 和主机上的进程 ID 是隔离的。这样做可以避免容器内的进程影响到宿主机上的进程。

Network Namespace(网络命名空间):每个容器都有自己的网络命名空间,使得容器之间的网络资源是隔离的。这样做可以使得容器之间有自己的网络接口和 IP 地址。

IPC Namespace(IPC 命名空间):每个容器都有自己的 IPC 命名空间,使得容器之间的进程间通信资源是隔离的。

User Namespace(用户命名空间):每个容器都有自己的用户命名空间,使得容器之间的用户和用户组是隔离的。

在容器中使用 Namespace 功能,可以有效地隔离容器和宿主机,使得容器之间不会互相影响。这样做可以使得容器更加安全、可靠和高效。

veth 设备

veth 是 Linux 内核中的虚拟以太网设备 (Virtual Ethernet Device),用于连接两个命名空间 (Namespace) 中的网络栈。在容器化环境中,每个容器都有一个网络命名空间,veth 设备通常用于将容器的网络栈连接到宿主机的网络栈,从而实现容器之间的通信。

在使用 veth 设备时,通常会创建一对相互连接的 veth 设备,这对设备的一个端点(一般记作 veth0)会被移动到容器的网络命名空间中,另一个端点则保留在宿主机的网络命名空间中(一般记作 veth1)。这样做会使得两个网络命名空间相互隔离,但容器和宿主机之间仍然可以通信。

veth 设备在实现容器网络隔离的同时,还能够通过 Linux 网桥 (bridge) 或者 Linux 路由器来实现不同容器之间的通信。在 veth 设备的两端设置 IP 地址,并将它们连接到一个 Linux 网桥上,可以使得多个容器之间能够互相通信。此外,veth 设备也可以与宿主机的物理网卡连接,从而实现容器和外部网络的通信。

veth 设备的工作原理是,将发送到其中一个端点的数据包,从另一个端点转发出去,这样做可以使得容器内的网络流量能够流向宿主机的网络栈,也可以使得外部网络的数据流量能够流向容器的网络栈。因此,veth 设备是容器网络隔离的基础之一,在容器化环境中被广泛使用。

模拟容器网络拓扑

tp0
这里我们使用 namespace server0、server1、server2 和 server3 模拟各个容器。

单个主机上容器和容器的通信

在 192.168.209.3 上创建两个 namespace 代替 server0 和 server1

1
2
3
ip netns add server0
ip netns add server1
ip netns show

创建连接 server0 和 root namespace 的 veth 设备

1
2
3
ip link add veth0 type veth peer name seth0
ip link set veth0 up
ip link set seth0 netns server0 # 将seth0 接口放在server0的namespace

进入到 server0 中

1
2
3
4
5
6
ip netns exec server0 bash
ip link
ip link set lo up
ip link set seth0 up
ip addr add 172.168.3.2/24 dev seth0
exit

server1 类似

1
2
3
4
5
6
7
8
9
10
ip link add veth1 type veth peer name seth1
ip link set veth1 up
ip link set seth1 netns server1 # 将seth0 接口放在server1namespace

ip netns exec server1 bash
ip link
ip link set lo up
ip link set seth1 up
ip addr add 172.168.3.3/24 dev seth1
exit

创建一个 br0 的 bridge 设备

1
2
ip link add br0 type bridge
ip link set br0 up

将刚才两个网线插到交换机上

1
2
ip link set veth0 master br0
ip link set veth1 master br0

server0 和 server1 就可以通信了

1
2
3
ip netns exec server1 bash
ip add show
ping 172.168.3.2

这个时候会发现不能 ping 通宿主机的网络

给 br0 分配一个 IP 地址

1
ip addr add 172.168.3.1/24 dev br0

给 br0 分配了 ip 地址后,在 server0 和 server1 上就会生成一条路由

1
2
ip netns  exec server0 ip route
172.168.3.0/24 dev seth0 proto kernel scope link src 172.168.3.2

这个时候主机就可以 ping 通容器了,但是如果需要容器也 ping 通宿主机的网卡地址,就需要在 server0 和 server1 上添加一条默认路由

1
2
3
4
5
6
7
ip netns exec server0 bash
ip route add default via 172.168.3.1
exit

ip netns exec server1 bash
ip route add default via 172.168.3.1
exit

server0 和 server1 就可以 ping 通主机了。

同样的道理来在主机 192.168.209.4 上配置一下 server2 和 server3

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
ip netns add server2
ip netns add server3

ip link add veth0 type veth peer name seth0
ip link set veth0 up
ip link set seth0 netns server2

ip netns exec server2 bash
ip link set lo up
ip link set seth0 up
ip addr add 172.168.4.2/24 dev seth0
exit

ip link add veth1 type veth peer name seth1
ip link set veth1 up
ip link set seth1 netns server3

ip netns exec server3 bash
ip link set lo up
ip link set seth1 up
ip addr add 172.168.4.3/24 dev seth1
exit


ip link add br1 type bridge
ip link set br1 up
ip link set veth0 master br1
ip link set veth1 master br1

ip addr add 172.168.4.1/24 dev br1

ip netns exec server2 bash
ip route add default via 172.168.4.1
exit

ip netns exec server3 bash
ip route add default via 172.168.4.1
exit

主机之间容器的通信

需要两台主机开启net.ipv4.ip_forward = 1
在 192.168.209.3 上需要添加路由

1
ip route add 172.168.4.0/24 via 192.168.209.4 dev ens33

在 192.168.209.4 上需要添加路由

1
ip route add 172.168.3.0/24 via 192.168.209.3 dev ens33

宿主机上的 server 之间就可以通信了

1
2
3
4
5
6
7
8
ip netns exec server3 bash
ping 172.168.3.3
PING 172.168.3.3 (172.168.3.3) 56(84) bytes of data.
64 bytes from 172.168.3.3: icmp_seq=1 ttl=62 time=0.458 ms
64 bytes from 172.168.3.3: icmp_seq=2 ttl=62 time=1.20 ms
64 bytes from 172.168.3.3: icmp_seq=3 ttl=62 time=0.941 ms

exit

容器访问外网

这个时候容器是无法访问外部网络的

1
2
ping -c 2 www.baidu.com
^C

需要在宿主机 192.168.209.3 上配置 nat

1
iptables -t nat -A POSTROUTING -s 172.168.3.0/24 ! -o br0 -j MASQUERADE

需要在宿主机 192.168.209.4 上配置 nat

1
iptables -t nat -A POSTROUTING -s 172.168.4.0/24 ! -o br1 -j MASQUERADE

这个时候就可以访问外部网络了

1
2
3
4
5
6
7
8
ping -c 2 www.baidu.com
PING www.a.shifen.com (180.101.50.188) 56(84) bytes of data.
64 bytes from 180.101.50.188 (180.101.50.188): icmp_seq=1 ttl=49 time=9.15 ms
64 bytes from 180.101.50.188 (180.101.50.188): icmp_seq=2 ttl=49 time=9.51 ms

--- www.a.shifen.com ping statistics ---
2 packets transmitted, 2 received, 0% packet loss, time 1002ms
rtt min/avg/max/mdev = 9.159/9.337/9.515/0.178 ms

端口发布

实现可以通过主机的端口访问容器的端口,类似于 docker 的-p选项
我们在 server0 上监听一个端口

1
2
ip netns exec server0 bash
nc -kl 172.168.3.2 5000

在主机 192.168.209.3 配置 DNAT

1
2
iptables -t nat -A PREROUTING -d 192.168.209.3 -p tcp -m tcp --dport 5000 -j DNAT --to-destination 172.168.3.2:5000 # 来自主机外部的流量
iptables -t nat -A OUTPUT -d 192.168.209.3 -p tcp -m tcp --dport 5000 -j DNAT --to-destination 172.168.3.2:5000 # 主机本身不会经过PREROUTING链,需要在OUTPUT上配置

测试

1
nc 172.168.3.2 5000
  • 标题: 【网络】容器网络基础实践
  • 作者: Bennie
  • 创建于 : 2024-02-26 10:36:35
  • 更新于 : 2024-02-26 10:57:12
  • 链接: https://liubin.ink/2024/02/26/network/【网络】容器网络基础实践/
  • 版权声明: 本文章采用 CC BY-NC-SA 4.0 进行许可。
 评论