• 【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设备总是成对互连。可以使用以下命令创建配对:

    # ip link add  type veth peer name  
    
    • 1

    在上面的例子中,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查询命令帮助信息:

    $ 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
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    创建网络命名空间

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

    $ ip netns add ns0
    
    • 1

    查询网络命名空间

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

    $ ip netns list
    ns0
    
    • 1
    • 2

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

    $ ls /var/run/netns/
    ns0
    
    • 1
    • 2

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

    删除网络命名空间

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

    $ ip netns delete ns0
    
    • 1

    操作网络命名空间

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

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

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

    $ 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
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

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

    启用lo回环网卡:

    $ 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
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

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

    ns0中打开一个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
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

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

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

    $ ip netns exec ns0 /bin/bash --rcfile <(echo "PS1=\"ns0> \"")
    ns0>
    
    • 1
    • 2

    Network Namespace之间的通信

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

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

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

    创建veth pair

    创建veth pair:

    $ 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
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

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

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

    $ 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
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    创建Network Namespace

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

    $ ip netns add ns1
    
    $ ip netns list
    ns1
    ns0
    
    • 1
    • 2
    • 3
    • 4
    • 5

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

    将veth0加入到ns0:

    $ ip link set veth0 netns ns0
    
    • 1

    将veth1加入到ns1:

    $ ip link set veth1 netns ns1
    
    • 1

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

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

    $ 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
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

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

    $ 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
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    验证两个Network Namespace之间的互通

    在ns0中ping通ns1:

    $ 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
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    在ns1中ping通ns0:

    $ 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
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

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

  • 相关阅读:
    Java中main方法是单线程还是多线程?启动后有多少个线程会被创建?
    il2cpp分析-gobal-metadata.dat解密
    Flutter教程之在 Flutter 中显示 TextField 上的日期选择器(教程含源码)
    我从未结束的Java之旅(二)
    论文笔记: 度量学习基本理解
    vs code实现XML代码补全
    Spring的重试机制-SpringRetry
    指针练习(2)
    vue3父组件调用子组件的方法
    Sentinel安装与部署
  • 原文地址:https://blog.csdn.net/u022812849/article/details/134069555