【Docker】Linux网络命名空间

命名空间

Namespace是Linux提供的一种对于系统全局资源的隔离机制;从进程的视角来看,同一个namespace中的进程看到的是该namespace自己独立的一份全局资源,这些资源的变化只在本namespace中可见,对其他namespace没有影响。容器就是采用namespace机制实现了对网络,进程空间等的隔离。不同的Container(在K8S中是以Pod为单位)属于不同namespace,实现了Container或Pod之间的资源互相隔离,互不影响。

命名空间将全局系统资源包装在一个抽象中,使命名空间中的进程看起来拥有自己的全局资源的独立实例。对全局资源的更改对作为命名空间成员的其他进程可见,但对其他进程不可见。名称空间的一个用途是实现容器。

Linux提供了以下七种namespace:

  • Cgroup:Cgroup root directory
  • IPC:System V IPC, POSIX message queues
  • Network:Network devices, stacks, ports, etc.
  • Mount:Mount points
  • PID:Process IDs
  • User:User and group IDs
  • UTS:Hostname and NIS domain name

Network namespace允许你在Linux中创建相互隔离的网络视图,每个网络命名空间都有自己独立的网络配置,包括:网络设备、路由表、IPTables规则,路由表、网络协议栈等。

新建的网络名字空间与主机默认网络名字空间之间是隔离的。我们平时默认操作的是主机的默认网络名字空间。

不同的Network Namespace的资源互相不可见,彼此之间无法通信。

VETH

VETH(Virtual Ethernet)是Linux提供的另外一种特殊的网络设备,中文称为虚拟网卡接口。它总是成对出现,要创建就创建一个pair。一个Pair中的veth就像一个网络线缆的两个端点,数据从一个端点进入,必然从另外一个端点流出。每个veth都可以被赋予IP地址。

veth设备是虚拟以太网设备。它们可以充当网络命名空间之间的隧道,以创建到另一个命名空间中的物理网络设备的桥,但也可以用作独立的网络设备。veth设备总是成对互连。可以使用以下命令创建配对:

shell 复制代码
# ip link add <p1-name> type veth peer name <p2-name> 

在上面的例子中,p1-name和p2-name是分配给两个相连端点的名称。在设备对中的一台设备上传输的数据包会立即被另一台设备接收。当任一设备出现故障时,设备对的链路状态为故障。

veth和tap/tun类似,也是linux提供的一种虚拟网络设备;但与tap/tun不同的是,veth总是成对出现的,从一端进入的数据包将会在另一端出现,因此又常常称为veth pair。我们可以把veth pair看成一条网线两端连接的两张以太网卡。

由于network namespace隔离了网络相关的全局资源,因此从网络角度来看,一个network namespace可以看做一个独立的虚机;即使在同一个主机上创建的两个network namespace,相互之间缺省也是不能进行网络通信的。

管理网络命名空间

Network Namespace是Linux内核提供的功能,本文借助ip命令来完成各种操作。ip命令来自于iproute2安装包,一般系统默认安装,如果没有的话,可自行安装。

查看帮助信息

ip命令管理的功能很多,和Network Namespace有关的操作都在其子命令ip netns下进行的,可以通过ip netns help查询命令帮助信息:

shell 复制代码
$ ip netns help
Usage:  ip netns list
        ip netns add NAME
        ip netns attach NAME PID
        ip netns set NAME NETNSID
        ip [-all] netns delete [NAME]
        ip netns identify [PID]
        ip netns pids NAME
        ip [-all] netns exec [NAME] cmd ...
        ip netns monitor
        ip netns list-id [target-nsid POSITIVE-INT] [nsid POSITIVE-INT]
NETNSID := auto | POSITIVE-INT

创建网络命名空间

通过ip netns add命令创建一个名为ns0的网络命名空间:

shell 复制代码
$ ip netns add ns0

查询网络命名空间

通过ip netns list命令查询网络命名空间列表:

shell 复制代码
$ ip netns list
ns0

查看网络命令空间所在的目录:

shell 复制代码
$ ls /var/run/netns/
ns0

注意:新创建的Network Namespace会出现在/var/run/netns/目录下。如果需要管理其他不是ip netns创建的network namespace,只要在这个目录下创建一个指向对应network namespace文件的链接即可。

删除网络命名空间

通过ip netns delete NAME命令删除网络命名空间:

shell 复制代码
$ ip netns delete ns0

操作网络命名空间

对于每个Network Namespace来说,它会有自己独立的网卡、路由表、ARP表、iptables等和网络相关的资源。

ip命令提供了ip netns exec子命令可以在对应的Network Namespace中执行命令。

查看网络命名空间ns0的网卡信息:

shell 复制代码
$ ip netns exec ns0 ip addr
1: lo: <LOOPBACK> mtu 65536 qdisc noop state DOWN group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00

$ ip netns exec ns0 ping 127.0.0.1
ping: connect: Network is unreachable

每个namespace在创建时会自动创建一个回环接口lo,默认不启用。它的作用和Linux系统中默认看到的lo一样,都是为了实现loopback通信,如果希望lo口能工作,可以通过下面的命令启用它。

启用lo回环网卡:

shell 复制代码
$ ip netns exec ns0 ip link set lo up

$ ip netns exec ns0 ip addr
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
    inet6 ::1/128 scope host
       valid_lft forever preferred_lft forever

$ ip netns exec ns0 ping 127.0.0.1 -c 3
PING 127.0.0.1 (127.0.0.1) 56(84) bytes of data.
64 bytes from 127.0.0.1: icmp_seq=1 ttl=64 time=0.014 ms
64 bytes from 127.0.0.1: icmp_seq=2 ttl=64 time=0.022 ms
64 bytes from 127.0.0.1: icmp_seq=3 ttl=64 time=0.028 ms

--- 127.0.0.1 ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 2091ms
rtt min/avg/max/mdev = 0.014/0.021/0.028/0.005 ms

再次ping回环网卡,发现已经可以ping通了。

ns0中打开一个shell终端:

shell 复制代码
$ ip netns exec ns0 /bin/bash

$ ip addr
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
    inet6 ::1/128 scope host
       valid_lft forever preferred_lft forever

$ exit
exit

通过执行ip netns exec ns0 /bin/bash进入ns0的shell终端,后面所有的命令都在这个Network Namespace中执行,好处是不用每次执行命令时都要带上ip netns exec,缺点是我们无法清楚知道自己当前所在的shell,容易混淆。

可以采用下面的方法解决:

shell 复制代码
$ ip netns exec ns0 /bin/bash --rcfile <(echo "PS1=\"ns0> \"")
ns0>

Network Namespace之间的通信

默认情况下,network namespace是不能和主机网络,或者其他network namespace通信的。

可以使用Linux提供的veth pair来完成通信,veth pair你可以理解为使用网线连接好的两个接口,把两个端口放到两个namespace中,那么这两个namespace就能打通。

接下来我们通过实验进行验证:

创建veth pair

创建veth pair:

shell 复制代码
$ ip link add type veth

$ ip link
21: veth0@veth1: <BROADCAST,MULTICAST,M-DOWN> mtu 1500 qdisc noop state DOWN mode DEFAULT group default qlen 1000
    link/ether 7e:8d:e9:81:ee:6e brd ff:ff:ff:ff:ff:ff
22: veth1@veth0: <BROADCAST,MULTICAST,M-DOWN> mtu 1500 qdisc noop state DOWN mode DEFAULT group default qlen 1000
    link/ether 8e:fd:3c:5b:fd:52 brd ff:ff:ff:ff:ff:ff

可以看到,此时系统中新增了一对veth pair:veth0和veth1,需要记住的是veth pair无法单独存在,删除其中一个,另一个也会自动消失。

如果需要指定veth pair两个端点的名称,可以使用下面的命令:

shell 复制代码
$ ip link add veth001 type veth peer name veth002

$ ip link
23: veth002@veth001: <BROADCAST,MULTICAST,M-DOWN> mtu 1500 qdisc noop state DOWN mode DEFAULT group default qlen 1000
    link/ether ce:e8:7d:d3:31:07 brd ff:ff:ff:ff:ff:ff
24: veth001@veth002: <BROADCAST,MULTICAST,M-DOWN> mtu 1500 qdisc noop state DOWN mode DEFAULT group default qlen 1000
    link/ether 12:bc:27:1c:64:cc brd ff:ff:ff:ff:ff:ff

$ ip link delete veth001

创建Network Namespace

前面我们已创建了一个名为ns0的Network Namespace,下面再创建一个名称为ns1的网络命名空间。

shell 复制代码
$ ip netns add ns1

$ ip netns list
ns1
ns0

把veth pair分别加入到这两个namespace中

将veth0加入到ns0:

shell 复制代码
$ ip link set veth0 netns ns0

将veth1加入到ns1:

shell 复制代码
$ ip link set veth1 netns ns1

分别为这对veth pair配置上ip地址,并启用

为veth0配置IP,并启用该虚拟网卡:

shell 复制代码
$ ip netns exec ns0 ip addr add 192.168.1.1/24 dev veth0

$ ip netns exec ns0 ip link set veth0 up

$ ip netns exec ns0 ip addr
1: lo: <LOOPBACK> mtu 65536 qdisc noop state DOWN group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
21: veth0@if22: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default qlen 1000
    link/ether 7e:8d:e9:81:ee:6e brd ff:ff:ff:ff:ff:ff link-netns ns1
    inet 192.168.1.1/24 scope global veth0
       valid_lft forever preferred_lft forever
    inet6 fe80::7c8d:e9ff:fe81:ee6e/64 scope link
       valid_lft forever preferred_lft forever

为veth1配置IP,并启用该虚拟网卡:

shell 复制代码
$ ip netns exec ns1 ip addr add 192.168.1.2/24 dev veth1

$ ip netns exec ns1 ip link set veth1 up

$ ip netns exec ns1 ip addr
1: lo: <LOOPBACK> mtu 65536 qdisc noop state DOWN group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
22: veth1@if21: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default qlen 1000
    link/ether 8e:fd:3c:5b:fd:52 brd ff:ff:ff:ff:ff:ff link-netns ns0
    inet 192.168.1.2/24 scope global veth1
       valid_lft forever preferred_lft forever
    inet6 fe80::8cfd:3cff:fe5b:fd52/64 scope link
       valid_lft forever preferred_lft forever

验证两个Network Namespace之间的互通

在ns0中ping通ns1:

shell 复制代码
$ ip netns exec ns0 ping 192.168.1.2 -c 2
PING 192.168.1.2 (192.168.1.2) 56(84) bytes of data.
64 bytes from 192.168.1.2: icmp_seq=1 ttl=64 time=0.447 ms
64 bytes from 192.168.1.2: icmp_seq=2 ttl=64 time=0.031 ms

--- 192.168.1.2 ping statistics ---
2 packets transmitted, 2 received, 0% packet loss, time 1009ms
rtt min/avg/max/mdev = 0.031/0.239/0.447/0.208 ms

在ns1中ping通ns0:

shell 复制代码
$ ip netns exec ns1 ping 192.168.1.1 -c 2
PING 192.168.1.1 (192.168.1.1) 56(84) bytes of data.
64 bytes from 192.168.1.1: icmp_seq=1 ttl=64 time=0.019 ms
64 bytes from 192.168.1.1: icmp_seq=2 ttl=64 time=0.032 ms

--- 192.168.1.1 ping statistics ---
2 packets transmitted, 2 received, 0% packet loss, time 1000ms
rtt min/avg/max/mdev = 0.019/0.025/0.032/0.006 ms

可以看到,veth pair成功实现了两个不同Network Namespace之间的网络交互。

相关推荐
伤不起bb2 小时前
MySQL 高可用
linux·运维·数据库·mysql·安全·高可用
意倾城4 小时前
Docker数据卷
docker·容器
shykevin4 小时前
python开发Streamable HTTP MCP应用
开发语言·网络·python·网络协议·http
whgjjim4 小时前
docker迅雷自定义端口号、登录用户名密码
运维·docker·容器
tmacfrank5 小时前
网络编程中的直接内存与零拷贝
java·linux·网络
数据与人工智能律师7 小时前
虚拟主播肖像权保护,数字时代的法律博弈
大数据·网络·人工智能·算法·区块链
QQ2740287567 小时前
Soundness Gitpod 部署教程
linux·运维·服务器·前端·chrome·web3
qwfys2008 小时前
How to configure Linux mint desktop
linux·desktop·configure·mint
南方以南_8 小时前
Ubuntu操作合集
linux·运维·ubuntu
purrrew8 小时前
【Java ee初阶】HTTP(2)
网络·网络协议·http