美文网首页DockerDocker容器
Docker网络学习第一篇-Linux虚拟网络

Docker网络学习第一篇-Linux虚拟网络

作者: rdgbrain | 来源:发表于2020-07-04 12:02 被阅读0次

    作者: 耳朵里有风

    温馨提示,本篇内容较多,主要内容包括:

    • namespace概念,及基本操作;
    • 几种网络虚拟设备,分别介绍了原理以及演示创建过程;

    Linux Namespace

    Namespace 是Linux内核级别隔离系统资源的解决方案,将网络、进程等资源进行封装隔离,使得各资源彼此透明,互不干扰,这一机制为实现基于容器的虚拟化技术提供了很好的基础。我们可以在Linux一个Host中创建多个Namespace, 比如在一个主机中启动若干个Docker容器。

    Namespace 可隔离的资源有:

    • Mount: 隔离文件系统挂载点
    • UTS: 隔离主机名和域名信息
    • IPC: 隔离进程间通信
    • PID: 隔离进程的ID
    • Network: 隔离网络资源
    • User: 隔离用户和用户组的ID

    其中Network是实现网络虚拟化的重要功能,其会创建多个隔离的网络空间,各自拥有独自的网卡、路由表、iptables、网络协议栈等。下面演示一下namespace的简单命令行操作。

    Namespace操作

    命令 ip netns add testns 创建一个新的namespace, ip netns ls 命令查看是否创建成功(namespace会出现在/var/run/netns下)。

    [root@2030-edu-01-no ~]# ip netns help
    Usage: ip netns list
           ip netns add NAME
           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
    [root@2030-edu-01-no ~]# ip netns add testns
    [root@2030-edu-01-no ~]# ip netns ls
    testns
    [root@2030-edu-01-no netns]# ll /var/run/netns/
    total 0
    -r--r--r-- 1 root root 0 Jul  3 13:49 testns
    

    创建好的namespace后,使用ip netns exec 在对应的 network namespace 中执行命令,比如查看namespace中的IP地址即可使用 ip netns exec testns ip addr

    [root@2030-edu-01-no netns]# ip netns exec testns 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
    [root@2030-edu-01-no netns]# 
    

    也可以使用ip netns exec testns /bin/bash 进入namespace窗口执行多个命令

    [root@2030-edu-01-no netns]# ip netns exec testns bash
    [root@2030-edu-01-no netns]# 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
    [root@2030-edu-01-no netns]# exit
    exit
    

    通过修改 bash 的前缀信息可以区分不同 shell, ip netns exec testns /bin/bash --rcfile <(echo "PS1=\"namespace testns> \"")

    图1

    通过ip addr可以看出,创建namespace时自动创建一个 lo 的 interface,但是并有其他网络接口,此时的namespace是无法和主机或者其他namespace通信的。那么namespace之间要如何通信呢,暂时按下不表,先来看一看Linux中虚拟网络设备。

    虚拟网络设备

    Linux 虚拟网络的背后其实是由一个个的虚拟设备所构成的,比如 tap、tun 和 veth-pair。对于一个网络设备来说,就像一个管道(pipe)一样,有两端,从其中任意一端收到的数据将从另一端发送出去。对于物理设备,如网卡eth0,它的两端分别是内核协议栈和外面的物理网络,从物理网络收到的数据,会转发给内核协议栈,而应用程序从协议栈发过来的数据将会通过物理网络发送出去。对于虚拟网络设备,作为一个网络设备,它也能配置IP,路由数据,不同的是虚拟网络设备从协议栈接收数据,但怎么发送,发送到哪些是由驱动自身决定的。

    图2

    tun/tap

    在linux下,要实现核心态和用户态数据的交互,有多种方式:可以通用socket创建特殊套接字,利用套接字实现数据交互;通过proc文件系统创建文件来进行数据交互;还可以使用设备文件的方式,访问设备文件会调用设备驱动相应的例程,设备驱动本身就是核心态和用户态的一个接口,Tun/tap驱动就是利用设备文件实现用户态和核心态的数据交互。

    tun/tap设备的用处是将协议栈中的部分数据包转发给用户空间的应用程序,给用户空间的程序一个处理数据包的机会。设备最常用的场景是VPN。下图描述了tun/tap数据流程示意图,实际的原理比这个更加复杂。


    图3

    tun/tap设备实现VPN原理,vpn通过操作系统的接口直接虚拟出一张网卡,后续整个操作系统的网络通讯都将通过这张虚拟的网卡进行收发。这和任何一个代理的实现思路都差不多,应用层并不知道网卡是虚拟的,这样vpn虚拟网卡将以中间人的身份对数据进行加工,从而实现各种效果,而虚拟网卡就是tun或者tap。
    比如在家里要通过VPN访问内网的一个网站,而要访问的内网网址是172.31.130.23。通常情况下,VPN客户端拨入VPN服务器后,本机的默认网关会改为VPN的IP地址,当你访问网站时,具体流程如下:
    1、数据包A到达网关1,网关1接收到请求后,构造新的数据包B, (数据包A根据路由规则指定到tun或tap设备(假设设备为tun0)来接收请求,tun0会进一步将数据传递给连接在另一端的VPN应用,应用接收到数据后做一系列处理,将原来数据包封装为新的包B),数据包B指向由原来的172.31.130.23变成了42.62.43.137,接着将数据包交给物理网卡发送到网关2。

    2、网关2接收到数据包后,将其还原为包A(原理和上一步相同,也是利用了tun或tap将数据传至应用层再解封还原的过程),最终转到目标服务器, 图4

    下面演示了如何在centos7中创建一个tun设备
    第一步,确认内核是否有tun模块 modinfo tun, 如果有的话会输出tun相关描述信息;
    第二步,确定内核模块是否已经加载,lsmod | grep tun , 如果没有加载使用modprobe tun命令加载;

    [root@2030-edu-01-no ~]# lsmod | grep tun
    tun                    31740  0
    

    0 表示tun设备数量

    第三步,查看是否安装了 tunctl, 如果没有安装,参考下面的命令

    cat << EOF > /etc/yum.repos.d/nux-misc.repo
    > [nux-misc]
    > name=Nux Misc
    > baseurl=http://li.nux.ro/download/nux/misc/el7/x86_64/
    > enabled=0
    > gpgcheck=1
    > gpgkey=http://li.nux.ro/download/nux/RPM-GPG-KEY-nux.ro
    > EOF
    
    yum -y --enablerepo=nux-misc install tunctl
    

    第四步,创建虚拟网卡设备 tunctl -t tap0 -u root, 此时再运行lsmod | grep tun,数量由0变成了1,ifconfig -a 也可以查看到刚刚创建的设备

    tap0: flags=4098<BROADCAST,MULTICAST>  mtu 1500
            ether fa:91:9d:65:af:13  txqueuelen 1000  (Ethernet)
            RX packets 0  bytes 0 (0.0 B)
            RX errors 0  dropped 0  overruns 0  frame 0
            TX packets 0  bytes 0 (0.0 B)
            TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0
    

    第五步,设置虚拟网卡, 设置网关、ip等,如 ifconfig tap0 192.168.1.5 netmask 255.255.255.0 promisc

    [root@2030-edu-01-no ~]# ifconfig tap0 192.168.1.5 netmask 255.255.255.0 promisc
    [root@2030-edu-01-no ~]# ping 192.168.1.5
    PING 192.168.1.5 (192.168.1.5) 56(84) bytes of data.
    64 bytes from 192.168.1.5: icmp_seq=1 ttl=64 time=0.041 ms
    64 bytes from 192.168.1.5: icmp_seq=2 ttl=64 time=0.040 ms
    

    veth-pair

    veth-pair 是一对的虚拟设备接口,和 tap/tun 设备不同的是,它都是成对出现的。一端连着协议栈,一端彼此相连。


    图5

    当veth0接收到协议栈的数据发送请求后,会将数据发送到veth1上去, veth常常充当桥梁,连接各种虚拟网络设备,比如上文说的namespace与host以及namespace之间的通信就可以使用veth实现。下面演示一组host与namespace实现通信的过程。

    • 使用 ip link add veth0 type veth peer name vethtestns 添加一组设备,直接使用 ip link add type veth 则不指定名称,系统自动生成. (删除使用 ip link delete veth0, 会同时删除vethtestns)
    • 使用ip l s vethtestns netns testns 将vethtestns设备加入testns namespace中
    • 配置IP, 并启用
    ip a a 10.1.1.2/24 dev veth0 
    ip l s veth0 up
    ip netns exec testns ip a a 10.1.1.3/24 dev vethtestns
    ip netns exec testns ip l s vethtestns up
    
    • 在testns中ping host 的ip, 可以看到网络是联通的
    [root@2030-edu-01-no /]# ip netns exec testns ping 10.1.1.2
    PING 10.1.1.2 (10.1.1.2) 56(84) bytes of data.
    64 bytes from 10.1.1.2: icmp_seq=1 ttl=64 time=0.059 ms
    64 bytes from 10.1.1.2: icmp_seq=2 ttl=64 time=0.055 ms
    64 bytes from 10.1.1.2: icmp_seq=3 ttl=64 time=0.047 ms
    

    veth实现了两两通信,通信过程简单描述如下,左侧是刚刚演示的namespace与主机的通信模型,右侧是两个namespace之间的通信模型,但是如果需要多个接口互相通信,veth就无法胜任了。


    图6

    Bridge

    同样Bridge也是虚拟网络设备之一,具有网络设备的通性,普通虚拟设备只有两个端口,一进一出,而Bridge 由多个端口,数据从任一端口进来,再根据mac地址从指定端口出去。可以看出Bridge是一套虚拟交换设备,和物理交换机的功能相同。

    使用演示

    通过命令ip link add name bridge0 type bridge && ip link set bridge0 up 创建并启动一个网桥

    [root@2030-edu-01-no ~]# ifconfig bridge0
    bridge0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500
            inet6 fe80::3cb6:45ff:fe15:94f2  prefixlen 64  scopeid 0x20<link>
            ether 3e:b6:45:15:94:f2  txqueuelen 1000  (Ethernet)
            RX packets 0  bytes 0 (0.0 B)
            RX errors 0  dropped 0  overruns 0  frame 0
            TX packets 8  bytes 656 (656.0 B)
            TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0
    

    此时网桥一端连接协议栈,而另一端什么也没接。


    图7

    上文提到了veth设备只能实现两两连接,如果有多个接口需要连接,此时就可以借助Bridge来实现。我们假设现在有三个namespace 要互相通信,那么就需要创建三对veth设备,每对设备中的一个接到namespace中,另一个接入网桥中,具体的模型如下:


    图8
    具体步骤为
    • 创建namespace、veth、和网桥资源(这里直接使用上一步创建好的bridge0)。
    • 将veth分别接入namespace和网桥中;
    [root@2030-edu-01-no ~]# ip netns add ns1
    [root@2030-edu-01-no ~]# ip netns add ns2
    [root@2030-edu-01-no ~]# ip netns add ns3
    [root@2030-edu-01-no ~]# ip link add veth1 type veth peer name vethb1
    [root@2030-edu-01-no ~]# ip link add veth2 type veth peer name vethb2
    [root@2030-edu-01-no ~]# ip link add veth3 type veth peer name vethb3
    [root@2030-edu-01-no ~]# ip link set veth1 netns ns1
    [root@2030-edu-01-no ~]# ip link set veth2 netns ns2
    [root@2030-edu-01-no ~]# ip link set veth3 netns ns3
    [root@2030-edu-01-no ~]# ip link set vethb1 master bridge0
    [root@2030-edu-01-no ~]# ip link set vethb2 master bridge0
    [root@2030-edu-01-no ~]# ip link set vethb3 master bridge0
    [root@2030-edu-01-no ~]# ip link set vethb1  up
    [root@2030-edu-01-no ~]# ip link set vethb2  up
    [root@2030-edu-01-no ~]# ip link set vethb3  up
    [root@2030-edu-01-no ~]# ip netns exec ns1 ip addr add 10.1.1.2/24 dev veth1
    [root@2030-edu-01-no ~]# ip netns exec ns2 ip addr add 10.1.1.3/24 dev veth2
    [root@2030-edu-01-no ~]# ip netns exec ns3 ip addr add 10.1.1.4/24 dev veth3
    [root@2030-edu-01-no ~]# ip netns exec ns1 ip link set veth1 up
    [root@2030-edu-01-no ~]# ip netns exec ns2 ip link set veth2 up
    [root@2030-edu-01-no ~]# ip netns exec ns3 ip link set veth3 up
    
    • 测试是否已经联通,从ns3中分别ping ns1和ns2 ,从结果看已经成功啦!
    [root@2030-edu-01-no ~]# ip netns exec ns3 ping 10.1.1.2 -c 1
    PING 10.1.1.2 (10.1.1.2) 56(84) bytes of data.
    64 bytes from 10.1.1.2: icmp_seq=1 ttl=64 time=0.098 ms
    
    --- 10.1.1.2 ping statistics ---
    1 packets transmitted, 1 received, 0% packet loss, time 0ms
    rtt min/avg/max/mdev = 0.098/0.098/0.098/0.000 ms
    [root@2030-edu-01-no ~]# ip netns exec ns3 ping 10.1.1.3 -c 1
    PING 10.1.1.3 (10.1.1.3) 56(84) bytes of data.
    64 bytes from 10.1.1.3: icmp_seq=1 ttl=64 time=0.106 ms
    
    --- 10.1.1.3 ping statistics ---
    1 packets transmitted, 1 received, 0% packet loss, time 0ms
    rtt min/avg/max/mdev = 0.106/0.106/0.106/0.000 ms
    [root@2030-edu-01-no ~]# 
    

    通常使用brctl 工具来管理网桥,使用yum install bridge-utils 命令安装,比如查看网桥信息.

    [root@2030-edu-01-no ~]# brctl show bridge0
    bridge name bridge id       STP enabled interfaces
    bridge0     8000.127eaae73032   no      vethb1
                                vethb2
                                vethb3
    

    上面演示的案例中,已经实现了三个namespace的互相联通,此时如果在和主机联通,只需要给网桥分配一个同网段的ip地址即可,ip addr add 10.1.1.1/24 dev bridge0

    Bridge 应用

    Bridge目前主要用在虚拟机和Docker中,Docker网络基础后续会专门介绍,先来看看网桥在虚拟机的应用模型。虚拟机通过tun/tap或者其它类似的虚拟网络设备,将虚拟机内的网卡同网桥连接起来,以此达到和真实交换机一样的效果,虚拟机发出去的数据包先到达网桥,然后由网桥交给物理网卡发送出去,数据包不需要经过host机器的协议栈,效率也比较高。


    图9

    借助 Linux Bridge 功能,同主机或跨主机的虚拟机之间能够轻松实现通信,也能够让虚拟机访问到外网,这就是我们所熟知的桥接模式,一般在装 VMware 虚拟机或者 VirtualBox 虚拟机的时候,都会提示我们要选择哪种模式,其中常用的一种模式是桥接。

    OVS

    Bridge 充当虚拟交换机已经能满足网络的通信,但是仍有一些不足的地方,比如网络管理和监控的便利性、数据包寻路和转发的高效性、隧道协议支持类型等。OVS(Open vSwitch)是流行的虚拟交换机之一,扩展了很多高级特性。

    参考资料

    1. http://www.360doc.com/content/18/0829/07/44856983_782027344.shtml
    2. https://zhuanlan.zhihu.com/p/73248894
    3. https://www.jianshu.com/p/2a14fe583cdf
    4. https://blog.csdn.net/u012707739/article/details/78163354
    5. https://www.ibm.com/developerworks/cn/linux/l-tuntap/
    6. https://segmentfault.com/a/1190000009249039
    7. https://segmentfault.com/a/1190000009491002
    8. https://www.cnblogs.com/bakari/p/10613710.html
    9. https://www.cnblogs.com/bakari/p/8097439.html

    相关文章

      网友评论

        本文标题:Docker网络学习第一篇-Linux虚拟网络

        本文链接:https://www.haomeiwen.com/subject/ynujqktx.html