×

linux 网络虚拟化:network namespace 简介

hqy hqy 发表于2019-05-07 15:07:13 浏览2447 评论0

抢沙发发表评论

network namespace 是实现网络虚拟化的重要功能,它能创建多个隔离的网络空间,它们有独自的网络栈信息。不管是虚拟机还是容器,运行的时候仿佛自己就在独立的网络中。这篇文章介绍 network namespace 的基本概念和用法,network namespace 是 linux 内核提供的功能,这篇文章借助 ip 命令来完成各种操作。ip 命令来自于 iproute2 安装包,一般系统会默认安装,如果没有的话,请读者自行安装。

NOTE:ip 命令因为需要修改系统的网络配置,默认需要 sudo 权限。这篇文章使用 root 用户执行,请不要在生产环境或者重要的系统中用 root 直接执行,以防产生错误。

ip 命令管理的功能很多, 和 network namespace 有关的操作都是在子命令 ip netns 下进行的,可以通过 ip netns help 查看所有操作的帮助信息。

默认情况下,使用 ip netns 是没有网络 namespace 的,所以 ip netns ls 命令看不到任何输出。

[root@localhost ~]# ip netns helpUsage: ip netns list
       ip netns add NAME
       ip netns delete NAME
       ip netns identify PID
       ip netns pids NAME
       ip netns exec NAME cmd ...
       ip netns monitor
[root@localhost ~]# ip netns ls123456789

创建 network namespace 也非常简单,直接使用 ip netns add 后面跟着要创建的 namespace 名称。如果相同名字的 namespace 已经存在,命令会报 Cannot create namespace 的错误。

[root@localhost ~]# ip netns add net1[root@localhost ~]# ip netns lsnet1123

ip netns 命令创建的 network namespace 会出现在 /var/run/netns/ 目录下,如果需要管理其他不是 ip netns 创建的 network namespace,只要在这个目录下创建一个指向对应 network namespace 文件的链接就行。

有了自己创建的 network namespace,我们还需要看看它里面有哪些东西。对于每个 network namespace 来说,它会有自己独立的网卡、路由表、ARP 表、iptables 等和网络相关的资源。ip 命令提供了 ip netns exec 子命令可以在对应的 network namespace 中执行命令,比如我们要看一下这个 network namespace 中有哪些网卡。更棒的是,要执行的可以是任何命令,不只是和网络相关的(当然,和网络无关命令执行的结果和在外部执行没有区别)。比如下面例子中,执行 bash 命令了之后,后面所有的命令都是在这个 network namespace 中执行的,好处是不用每次执行命令都要把 ip netns exec NAME 补全,缺点是你无法清楚知道自己当前所在的 shell,容易混淆。

[root@localhost ~]# ip netns exec net1 ip addr1: lo: <LOOPBACK> mtu 65536 qdisc noop state DOWN    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00[root@localhost ~]# ip netns exec net1 bash[root@localhost ~]# ip addr1: lo: <LOOPBACK> mtu 65536 qdisc noop state DOWN    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00[root@localhost ~]# exitexit123456789

更新:通过修改 bash 的前缀信息可以区分不同 shell,操作如下:

$ ip netns exec ns1 /bin/bash --rcfile <(echo "PS1=\"namespace ns1> \"")

namespace ns1> ping www.google.comPING www.google.com (178.60.128.38) 56(84) bytes of data.64 bytes from cache.google.com (178.60.128.38): icmp_seq=1 ttl=58 time=17.6 ms12345

ip netns exec 后面跟着 namespace 的名字,比如这里的 net1,然后是要执行的命令,只要是合法的 shell 命令都能运行,比如上面的 ip addr 或者 bash

每个 namespace 在创建的时候会自动创建一个 lo 的 interface,它的作用和 linux 系统中默认看到的 lo 一样,都是为了实现 loopback 通信。如果希望 lo 能工作,不要忘记启用它:

[root@localhost ~]# ip netns exec net1 ip link set lo up1

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

network namespace 之间通信

有了不同 network namespace 之后,也就有了网络的隔离,但是如果它们之间没有办法通信,也没有实际用处。要把两个网络连接起来,linux 提供了 veth pair 。可以把 veth pair 当做是双向的 pipe(管道),从一个方向发送的网络数据,可以直接被另外一端接收到;或者也可以想象成两个 namespace 直接通过一个特殊的虚拟网卡连接起来,可以直接通信。

使用上面提到的方法,我们再创建另外一个 network namespace,这里我们使用 net0 和 net1 两个名字。

我们可以使用 ip link add type veth 来创建一对 veth pair 出来,需要记住的是 veth pair 无法单独存在,删除其中一个,另一个也会自动消失。

[root@localhost ~]# ip link add type veth[root@localhost ~]# ip link4: veth0: <BROADCAST,MULTICAST> mtu 1500 qdisc noop state DOWN mode DEFAULT qlen 1000
    link/ether 36:88:73:83:c9:64 brd ff:ff:ff:ff:ff:ff5: veth1: <BROADCAST,MULTICAST> mtu 1500 qdisc noop state DOWN mode DEFAULT qlen 1000
    link/ether fe:7e:75:4d:79:2e brd ff:ff:ff:ff:ff:ff123456

小知识: 创建 veth pair 的时候可以自己指定它们的名字,比如 ip link add vethfoo type veth peer name vethbar 创建出来的两个名字就是 vethfoo 和 vethbar 。因为这里我们对名字没有特殊要求,所以就直接使用系统自动生成的名字。如果 pair 的一端接口处于 DOWN 状态,另一端能自动检测到这个信息,并把自己的状态设置为 NO-CARRIER

创建结束之后,我们能看到名字为 veth0 和 veth1 两个网络接口,名字后面的数字是系统自动生成的。接下来,要做的是把这对 veth pair 分别放到已经两个 namespace 里面,这个可以使用 ip link set DEV netns NAME 来实现:

[root@localhost ~]# ip link set veth0 netns net0[root@localhost ~]# ip link set veth1 netns net1[root@localhost ~]# ip netns exec net0 ip addr1: lo: <LOOPBACK> mtu 65536 qdisc noop state DOWN    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:004: veth0: <BROADCAST,MULTICAST> mtu 1500 qdisc noop state DOWN qlen 1000
    link/ether 36:88:73:83:c9:64 brd ff:ff:ff:ff:ff:ff
[root@localhost ~]# ip netns exec net1 ip addr1: lo: <LOOPBACK> mtu 65536 qdisc noop state DOWN    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:005: veth1: <BROADCAST,MULTICAST> mtu 1500 qdisc noop state DOWN qlen 1000
    link/ether fe:7e:75:4d:79:2e brd ff:ff:ff:ff:ff:ff123456789101112

最后,我们给这对 veth pair 配置上 ip 地址,并启用它们。

[root@localhost ~]# ip netns exec net0 ip link set veth0 up[root@localhost ~]# ip netns exec net0 ip addr add 10.0.1.1/24 dev veth0[root@localhost ~]# ip netns exec net0 ip route10.0.1.0/24 dev veth0  proto kernel  scope link  src 10.0.1.1[root@localhost ~]# ip netns exec net1 ip link set veth1 up[root@localhost ~]# ip netns exec net1 ip addr add 10.0.1.2/24 dev veth11234567

可以看到,最每个 namespace 中,在配置玩 ip 之后,还自动生成了对应的路由表信息,网络 10.0.1.0/24 数据报文都会通过 veth pair 进行传输。使用 ping 命令可以验证它们的连通性:

[root@localhost ~]# ip netns exec net0 ping -c 3 10.0.1.2PING 10.0.1.2 (10.0.1.2) 56(84) bytes of data.64 bytes from 10.0.1.2: icmp_seq=1 ttl=64 time=0.039 ms64 bytes from 10.0.1.2: icmp_seq=2 ttl=64 time=0.039 ms64 bytes from 10.0.1.2: icmp_seq=3 ttl=64 time=0.139 ms--- 10.0.1.2 ping statistics ---3 packets transmitted, 3 received, 0% packet loss, time 2004ms
rtt min/avg/max/mdev = 0.039/0.072/0.139/0.047 ms123456789

完成这些,我们创建的网络拓扑结构如下所示: 
这里写图片描述

使用 bridge 连接不同的 namespace

虽然 veth pair 可以实现两个 network namespace 之间的通信,但是当多个 namespace 需要通信的时候,就无能为力了。 讲到多个网络设备通信,我们首先想到的交换机和路由器。因为这里要考虑的只是同个网络,所以只用到交换机的功能。linux 当然也提供了虚拟交换机的功能,我们还是用 ip 命令来完成所有的操作。

NOTE:和 bridge 有关的操作也可以使用命令 brctl,这个命令来自 bridge-utils 这个包,读者可以根据自己的发行版进行安装,使用方法请查阅 man 页面或者相关文档。

首先我们来创建需要的 bridge,简单起见名字就叫做 br0

[root@localhost ~]# ip link add br0 type bridge[root@localhost ~]# ip link set dev br0 up12

下面只演示一个 namespace 的操作,其他 namespace 要做的事情和这个类似。创建 veth pair:

[root@localhost ~]# ip link add type veth1

把其中一个 veth(veth1) 放到 net0 里面,设置它的 ip 地址并启用它:

[root@localhost ~]# ip link set dev veth1 netns net0[root@localhost ~]# ip netns exec net0 ip link set dev veth1 name eth0[root@localhost ~]# ip netns exec net0 ip addr add 10.0.1.1/24 dev eth0[root@localhost ~]# ip netns exec net0 ip link set dev eth0 up1234

最后,把另一个 veth(veth0)连接到创建的 bridge 上,并启用它:

[root@localhost ~]# ip link set dev veth0 master br0[root@localhost ~]# ip link set dev veth0 up12

可以通过 bridge 命令(也是 iproute2 包自带的命令)来查看 bridge 管理的 link 信息:

[root@localhost ~]# bridge link17: veth0 state UP : <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 master br0 state forwarding priority 32 cost 212

最后通过 ping 命令来测试网络的连通性:

[root@localhost ~]# ip netns exec net0 ping -c 3 10.0.1.3PING 10.0.1.3 (10.0.1.3) 56(84) bytes of data.64 bytes from 10.0.1.3: icmp_seq=1 ttl=64 time=0.251 ms64 bytes from 10.0.1.3: icmp_seq=2 ttl=64 time=0.047 ms64 bytes from 10.0.1.3: icmp_seq=3 ttl=64 time=0.046 ms--- 10.0.1.3 ping statistics ---3 packets transmitted, 3 received, 0% packet loss, time 2008ms12345678

下图是这部分网络的拓扑结构,如果对 docker 网络熟悉的话,其实这和 docker 默认的 bridge 网络模型非常相似。当然要实现每个 namespace 对外网的访问还需要额外的配置(设置默认网关,开启 ip_forward,为网络添加 NAT 规则等)。 
这里写图片描述


转自:http://cizixs.com/2017/02/10/network-virtualization-network-namespace




https://www.cnblogs.com/bakari/p/10443484.html


本文通过 IP 命令操作来简单介绍 network namespace 的基本概念和用法。深入了解可以看看我之前写的两篇文章 Docker 基础技术之 Linux namespace 详解 和 Docker 基础技术之 Linux namespace 源码分析。

和 network namespace 相关的操作的子命令是 ip netns 。

1. ip netns add xx 创建一个 namespace#

Copy# ip netns add net1# ip netns lsnet1

2. ip netns exec xx yy 在新 namespace xx 中执行 yy 命令#

Copy# ip netns exec net1 ip addr 1: lo: <LOOPBACK> mtu 65536 qdisc noop state DOWN group default qlen 1
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00# ip netns exec net1 bash // 在 net1 中打开一个shell终端# ip addr // 在net1中的shell终端1: lo: <LOOPBACK> mtu 65536 qdisc noop state DOWN group default qlen 1
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00# exit // 退出net1

上面 bash 不好区分是当前是在哪个 shell,可以采用下面的方法解决:

Copy# ip netns exec net1 /bin/bash --rcfile <(echo "PS1=\"namespace net1> \"")namespace net1> ping www.baidu.com

每个 namespace 在创建的时候会自动创建一个回环接口 lo ,默认不启用,可以通过 ip link set lo up 启用。

3. network namespace 之间的通信#

新创建的 namespace 默认不能和主机网络,以及其他 namespace 通信。

可以使用 Linux 提供的 veth pair 来完成通信。下面显示两个 namespace 之间通信的网络拓扑:

3.1 ip link add type veth 创建 veth pair#

Copy# ip link add type veth# ip link3: veth0@veth1: <BROADCAST,MULTICAST,M-DOWN> mtu 1500 qdisc noop state DOWN mode DEFAULT group default qlen 1000
    link/ether 1a:53:39:5a:26:12 brd ff:ff:ff:ff:ff:ff4: veth1@veth0: <BROADCAST,MULTICAST,M-DOWN> mtu 1500 qdisc noop state DOWN mode DEFAULT group default qlen 1000
    link/ether 46:df:46:1f:bf:d6 brd ff:ff:ff:ff:ff:ff

使用命令 ip link add xxx type veth peer name yyy 指定 veth pair 的名字。

3.2 ip link set xx netns yy 将 veth xx 加入到 namespace yy 中#

Copy# ip link set veth0 netns net0# ip link set veth1 netns net1## ip netns exec net0 ip addr1: lo: <LOOPBACK> mtu 65536 qdisc noop state DOWN group default qlen 1
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
10: veth0@if11: <BROADCAST,MULTICAST> mtu 1500 qdisc noop state DOWN group default qlen 1000
    link/ether 1a:53:39:5a:26:12 brd ff:ff:ff:ff:ff:ff link-netnsid 1

3.3 给 veth pair 配上 ip 地址#

Copy# ip netns exec net0 ip link set veth0 up# ip netns exec net0 ip addr1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1
    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
10: veth0@if11: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc noqueue state LOWERLAYERDOWN group default qlen 1000
    link/ether 1a:53:39:5a:26:12 brd ff:ff:ff:ff:ff:ff link-netnsid 1# ip netns exec net0 ip addr add 10.1.1.1/24 dev veth0# ip netns exec net0 ip route10.1.1.0/24 dev veth0  proto kernel  scope link  src 10.1.1.1 linkdown## ip netns exec net1 ip link set veth1 up# ip netns exec net1 ip addr add 10.1.1.2/24 dev veth1

可以看到,在配完 ip 之后,还自动生成了对应的路由表信息。

3.4. ping 测试两个 namespace 的连通性#

Copy# ip netns exec net0 ping 10.1.1.2PING 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.069 ms64 bytes from 10.1.1.2: icmp_seq=2 ttl=64 time=0.054 ms64 bytes from 10.1.1.2: icmp_seq=3 ttl=64 time=0.053 ms64 bytes from 10.1.1.2: icmp_seq=4 ttl=64 time=0.053 ms

Done!

4. 多个不同 namespace 之间的通信#

2 个 namespace 之间通信可以借助 veth pair ,多个 namespace 之间的通信则可以使用 bridge 来转接,不然每两个 namespace 都去配 veth pair 将会是一件麻烦的事。下面就看看如何使用 bridge 来转接。

拓扑图如下:

4.1 使用 ip link 和 brctl 创建 bridge#

通常 Linux 中和 bridge 有关的操作是使用命令 brctl (yum install -y bridge-utils ) 。但为了前后照应,这里都用 ip 相关的命令来操作。

Copy// 建立一个 bridge# ip link add br0 type bridge# ip link set dev br0 up9: br0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UNKNOWN group default qlen 1000
    link/ether 42:55:ed:eb:a0:07 brd ff:ff:ff:ff:ff:ff
    inet6 fe80::4055:edff:feeb:a007/64 scope link
       valid_lft forever preferred_lft forever

4.2 创建 veth pair#

Copy//(1)创建 3 个 veth pair# ip link add type veth# ip link add type veth# ip link add type veth

4.3 将 veth pair 的一头挂到 namespace 中,一头挂到 bridge 上,并设 IP 地址#

Copy// (1)配置第 1 个 net0# ip link set dev veth1 netns net0# ip netns exec net0 ip link set dev veth1 name eth0# ip netns exec net0 ip addr add 10.0.1.1/24 dev eth0# ip netns exec net0 ip link set dev eth0 up## ip link set dev veth0 master br0# ip link set dev veth0 up// (2)配置第 2 个 net1# ip link set dev veth3 netns net1# ip netns exec net1 ip link set dev veth3 name eth0# ip netns exec net1 ip addr add 10.0.1.2/24 dev eth0# ip netns exec net1 ip link set dev eth0 up## ip link set dev veth2 master br0# ip link set dev veth2 up// (3)配置第 3 个 net2# ip link set dev veth5 netns net2# ip netns exec net2 ip link set dev veth5 name eth0# ip netns exec net2 ip addr add 10.0.1.3/24 dev eth0# ip netns exec net2 ip link set dev eth0 up# # ip link set dev veth4 master br0# ip link set dev veth4 up

这样之后,竟然通不了,经查阅 参见 ,是因为

原因是因为系统为bridge开启了iptables功能,导致所有经过br0的数据包都要受iptables里面规则的限制,而docker为了安全性(我的系统安装了 docker),将iptables里面filter表的FORWARD链的默认策略设置成了drop,于是所有不符合docker规则的数据包都不会被forward,导致你这种情况ping不通。

解决办法有两个,二选一:

  1. 关闭系统bridge的iptables功能,这样数据包转发就不受iptables影响了:echo 0 > /proc/sys/net/bridge/bridge-nf-call-iptables

  2. 为br0添加一条iptables规则,让经过br0的包能被forward:iptables -A FORWARD -i br0 -j ACCEPT

第一种方法不确定会不会影响docker,建议用第二种方法。

我采用以下方法解决:

Copyiptables -A FORWARD -i br0 -j ACCEPT

结果:

Copy# ip netns exec net0 ping -c 2 10.0.1.2PING 10.0.1.2 (10.0.1.2) 56(84) bytes of data.64 bytes from 10.0.1.2: icmp_seq=1 ttl=64 time=0.071 ms64 bytes from 10.0.1.2: icmp_seq=2 ttl=64 time=0.072 ms--- 10.0.1.2 ping statistics ---2 packets transmitted, 2 received, 0% packet loss, time 999msrtt min/avg/max/mdev = 0.071/0.071/0.072/0.008 ms# ip netns exec net0 ping -c 2 10.0.1.3PING 10.0.1.3 (10.0.1.3) 56(84) bytes of data.64 bytes from 10.0.1.3: icmp_seq=1 ttl=64 time=0.071 ms64 bytes from 10.0.1.3: icmp_seq=2 ttl=64 time=0.087 ms--- 10.0.1.3 ping statistics ---2 packets transmitted, 2 received, 0% packet loss, time 1000msrtt min/avg/max/mdev = 0.071/0.079/0.087/0.008 ms

Done!


打赏

本文链接:https://www.kinber.cn/post/643.html 转载需授权!

分享到:


推荐本站淘宝优惠价购买喜欢的宝贝:

image.png

 您阅读本篇文章共花了: 

群贤毕至

访客