Linux虚拟网络技术


近几年,Docker、Kubernetes等容器化技术和容器编排工具的兴起使技术人员从应用部署和维护的泥淖中解脱出来,同时也改变了很多很多互联网公司的技术架构。笔者近期也在学习Docker和Kubernetes,对这些新技术所带来的便捷性和安全性非常着迷,其中尤其对容器化技术的网络实现方式更为好奇。今天就把近期的对Linux虚拟网络技术的学习成果分享出来,希望能和大家一起交流学习。

Network Namespace

Network Namespace 是 Linux 内核提供的功能,是实现网络虚拟化的重要功能,它能创建多个隔离的网络空间,它们有独自网络栈信息。不管是虚拟机还是容器,运行的时候仿佛自己都在独立的网络中。而且不同Network Namespace的资源相互不可见,彼此之间无法通信。如下图所示:

  Network Namespace

ip netns命令

可以借助ip netns命令来完成对 Network Namespace 的各种操作。ip netns命令来自于iproute2安装包,一般系统会默认安装,如果没有的话,读者自行安装。

注意:ip netns命令修改网络配置时需要 sudo 权限。

可以通过ip netns命令完成对Network Namespace 的相关操作,可以通过ip netns help查看命令帮助信息:

$ ip netns add ns0
$ ip netns list
ns0

新创建的 Network Namespace 会出现在/var/run/netns/目录下。如果相同名字的 namespace 已经存在,命令会报Cannot create namespace file "/var/run/netns/ns0": File exists的错误。

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

操作Network Namespace

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

  1. 查看新创建 Network Namespace 的网卡信息
$ ip netns exec ns0 ping 127.0.0.1
connect: Network is unreachable

通过下面的命令启用lo回环网卡:

$ ip netns exec ns0 ping -c 3 127.0.0.1
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.048 ms
64 bytes from 127.0.0.1: icmp_seq=2 ttl=64 time=0.031 ms
64 bytes from 127.0.0.1: icmp_seq=3 ttl=64 time=0.029 ms

--- 127.0.0.1 ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 1999ms
rtt min/avg/max/mdev = 0.029/0.036/0.048/0.008 ms

转移设备

我们可以在不同的 Network Namespace 之间转移设备(如veth)。由于一个设备只能属于一个 Network Namespace ,所以转移后在这个 Network Namespace 内就看不到这个设备了。

其中,veth设备属于可转移设备,而很多其它设备(如lo、vxlan、ppp、bridge等)是不可以转移的。

veth pair

veth pair 全称是 Virtual Ethernet Pair,是一个成对的端口,所有从这对端口一 端进入的数据包都将从另一端出来,反之也是一样。
引入veth pair是为了在不同的 Network Namespace 直接进行通信,利用它可以直接将两个 Network Namespace 连接起来。
整个veth的实现非常简单,有兴趣的读者可以参考源代码drivers/net/veth.c的实现。

  veth pair

创建veth pair

ip link add vethfoo type veth peer name vethbar

实现Network Namespace间通信

下面我们利用veth pair实现两个不同的 Network Namespace 之间的通信。刚才我们已经创建了一个名为ns0的 Network Namespace,下面再创建一个信息Network Namespace,命名为ns1

$ ip link set veth0 netns ns0
$ ip link set veth1 netns ns1

然后我们分别为这对veth pair配置上ip地址,并启用它们:

$ ip netns exec ns0 ip addr
61: veth0@if62: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default qlen 1000
    link/ether e6:39:e1:e0:3a:a0 brd ff:ff:ff:ff:ff:ff link-netnsid 1
    inet 10.0.1.1/24 scope global veth0
       valid_lft forever preferred_lft forever
    inet6 fe80::e439:e1ff:fee0:3aa0/64 scope link
       valid_lft forever preferred_lft forever

$ ip netns exec ns1 ip addr
62: veth1@if61: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default qlen 1000
    link/ether be:41:49:42:23:6a brd ff:ff:ff:ff:ff:ff link-netnsid 0
    inet 10.0.1.2/24 scope global veth1
       valid_lft forever preferred_lft forever
    inet6 fe80::bc41:49ff:fe42:236a/64 scope link
       valid_lft forever preferred_lft forever

从上面可以看出,我们已经成功启用了这个veth pair,并为每个veth设备分配了对应的ip地址。我们尝试在ns1中访问ns0中的ip地址:

$ ip netns exec ns1 ethtool -S veth1
NIC statistics:
    peer_ifindex: 5

得知另一端的接口设备序列号是5,我们再到另一个命名空间中查看序列号5代表什么设备:

# 创建网桥
brctl addbr br0 
  1. 删除网桥
# 让eth0 成为br0 的一个端口
brctl addif br0 eth0  
# 让eth1 成为br0 的一个端口
brctl addif br0 eth1
# 让eth2 成为br0 的一个端口
brctl addif br0 eth2

iptables/netfilter

iptables是Linux实现的软件防火墙,用户可以通过iptables设置请求准入和拒绝规则,从而保护系统的安全。
我们也可以把iptables理解成一个客户端代理,用户通过iptables这个代理,将用户安全设定执行到对应的安全框架中,这个“安全框架”才是真正的防火墙,这个框架的名字叫netfilter
iptables其实是一个命令行工具,位于用户空间。

iptables/netfilter(以下简称iptables)组成了Linux平台下的包过滤防火墙,可以完成封包过滤、封包重定向和网络地址转换(NAT)等功能。

消息处理链

iptables不仅要处理本机接收到的消息,也要处理本机发出的消息。这些消息需要经过一系列的”关卡“才能被本机应用层接收,或者从本机发出,每个”关卡“担负着不同的工作。这里的”关卡“被称为”链“。

  • INPUT:进来的数据包应用此规则链中的策规则;
  • OUTPUT:外出的数据包应用此规则链中的规则;
  • FORWARD:转发数据包时应用此规则链中的规则;
  • PREROUTING:对数据包作路由选择前应用此链中的规则(所有的数据包进来的时侯都先由这个链处理);
  • POSTROUTING:对数据包作路由选择后应用此链中的规则(所有的数据包出来的时侯都先由这个链处理);

数据包经过各个链的处理过程大致如下图所示:

  消息处理链

规则表

从上面我们知道,iptables是按照规则来办事的,这些规则就是网络管理员预定义的条件。规则一般的定义为:如果数据包头符合这样的条件,就这样处理“。这些规则并不是严格按照添加顺序排列在一张规则表中,而是按照功能进行分类,存储在不同的表中,每个表存储一类规则:

  • Filter
    主要用来过滤数据,用来控制让哪些数据可以通过,哪些数据不能通过,它是最常用的表。
  • NAT
    用来处理网络地址转换的,控制要不要进行地址转换,以及怎样修改源地址或目的地址,从而影响数据包的路由,达到连通的目的。
  • Mangle
    主要用来修改IP数据包头,比如修改TTL值,同时也用于给数据包添加一些标记,从而便于后续其它模块对数据包进行处理(这里的添加标记是指往内核skb结构中添加标记,而不是往真正的IP数据包上加东西)。
  • Raw
    在Netfilter里面有一个叫做链接跟踪的功能,主要用来追踪所有的连接,而raw表里的rule的功能是给数据包打标记,从而控制哪些数据包不做链接跟踪处理,从而提高性能;优先级最高

表和链的关系

表和链共同完成了iptables对数据包的处理。但并不是每个链都包含所有类型的表,所以,有些链是天生不具备某些功能的。就像我们去车站乘车的时候,”关卡A“只负责检查身份证,”B关卡”只负责检查行李,而“C关卡”功能比较齐全,即负责检查身份证,又负责检查行李。二者的关系如下图所示:

  表和链的关系

总结

今天我们共同学习了一些常见的Linux虚拟网络技术。其中,Linux通过Network Namespace实现了网络的隔离,使网络协议栈之间互不干扰;并通过veth pair和网桥实现了相同主机上多个Network Namespace之间的数据通信;iptables则可以帮助我们实现网络安全和数据包的路由转发功能,从而使主机和主机、容器与容器、容器和宿主机之间可以相互收发消息。在这些技术的共同协作下,才有了现在安全、稳定的虚拟网络。



作者:王勇1991
链接:https://www.jianshu.com/p/f86d4b88777d
来源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。