作者: 耳朵里有风
通过前三篇对网络基本概念的理解,本篇来具体实战如何搭建比较的完整的Namespace的网络环境,搭建过程主要是解决下列问题:
- namespace在主机上的网络:包括和主机通信,namespace之间的通信;
- namespace访问主机所在的局域网内其他机器;
- namespace访问互联网;
- 不同主机之间的namespace通信;
另外还有如namespace 端口映射等问题,再后续篇章中遇到了再展开说明
本地通信
这里的本地通信是指在同一个主机内的不同namespace之间的通信,以及namespace和主机的通信,也就是要解决的第一个问题;其实此问题在Docker网络学习第一篇-Linux虚拟网络 有演示过,这里再回顾一遍。
要解决第一个问题,需要借助网桥和veth-pair 实现。
具体演示步骤如下:
- 创建命名空间
ip netns add ns1
ip netns add ns2
- 创建网桥
ip link add name bridge0 type bridge
- 创建veth-pair
ip link add veth1 type veth peer name vethb1
ip link add veth2 type veth peer name vethb2
- 将veth的一端放在网桥上
ip link set vethb1 master bridge0
ip link set vethb2 master bridge0
- 将veth另一端放在namespace
ip link set veth1 netns ns1
ip link set veth2 netns ns2
- 给网络设备配置ip地址
ip netns exec ns1 ip addr add 10.1.1.2/24 dev veth1
ip netns exec ns2 ip addr add 10.1.1.3/24 dev veth2
ip addr add 10.1.1.1/24 dev bridge0
- 启动网络设备
ip link set bridge0 up
ip link set vethb1 up
ip link set vethb2 up
ip netns exec ns1 ip link set veth1 up
ip netns exec ns2 ip link set veth2 up
图1的网络模型到此搭建完成,因为给bridge0 和 veth1 veth2 设置了同一网段网络地址,所以它们之间网络已经是互通的了。
局域网内访问
在ns1 中现在已经能够ping 通 10.1.1.1(host)和 10.1.1.3(ns2)。假设当前主机的ip是172.31.131.149, 另外一台机器主机ip为172.31.131.150, 那么在ns1或ns2中如何能够访问172.31.131.150 呢?
ns1 无法直接访问150, 但是在ns1所在host 149 是可以访问150的,当ns1请求150时,先将请求从veth1设备通过vethb1发送到bridge0, bridge0再将请求转发给eth0(149的物理网卡),最终发送到150。
依据上述文字描述和图示,将过程分为3步
- 第一步,将ns1中的请求发送到10.1.1.1
回顾系列第三篇所讲的路由知识(Docker网络学习第三篇-路由),要指定请求发给谁,就需要在ns1中添加一条路由规则(默认网关)
ip netns exec ns1 ip route add default via 10.1.1.1
将bridge0 作为ns1 的默认网关。
- 第二步,将bridge0上的请求转给eth0
学习了iptables(Docker网络学习第二篇-认识iptables)之后会了解请求在进出网络设备时会先经过iptables的各种链,请求从bridge0发出时,先经过prerouting, 因为目标地址不是本机所以不会进入INPUT链,此处需要加条规则,允许请求走FORWARD链。
iptables -A FORWARD -i bridge0 ! -o bridge0 -j ACCEPT
这句命令的意思就是允许从设备bridge0进非bridge0出的请求通过;同时不要忘了加一条
iptables -A FORWARD -o bridge0 -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
加上这句话的意思是允许回包,假设请求成功到达150,那么150给出的响应会原路返回,eth0拿到响应后,同样会经过prerouting链,因为响应的目标地址不是本机(是ns1),所以需要走FORWARD链转给bridge0;
从Linux2.6.15的内核版本后,iptables开始支持状态跟踪(conntrack),该功能依赖于netfilter的内核模块nf_conntrack。iptables可以根据包的状态进行二次的过滤拦截和状态跟踪。它也是state/ctstate和nat的主要依赖模块。conntrack共可以为连接标记五种状态,分别如下:
- NEW 新建连接请求的数据包
- ESTABLISHED 该连接是某NEW状态连接的回包
- RELATED ESTABLISHED连接的衍生连接
- INVALID 无法识别或没有任何状态的数据包
- UNTRACKED 它是管理员在raw表中,为连接设置NOTRACK规则后的状态
再回到请求,当请求从bridge0到prerouting再到forward, 然后到postrouting链处理,这一步需要将请求转发给设备eth0,进而访问150。具体命令如下:
iptables -A POSTROUTING -s 10.1.1.0/24 ! -o bridge0 -j MASQUERADE
把所有来自10.1.2.0/24网段的请求且出口不是bridge0的请求进行转发。(试了下不加! -o bridge0,暂时没看出影响,只是规则更加具体。 ) MASQUERADE,地址伪装,算是SNAT中的一种特例,可以实现自动化的SNAT。也可以手动指定snat的地址:
iptables -t nat -A POSTROUTING -s 10.1.1.0/24 -j SNAT --to 172.31.131.149
- 第三步,转出
eth0 因为已经和150 是联通的,这一步不需要在做任何配置。此时在ns1中ping 下150,ip netns exec ns1 ping 172.31.131.150
, 测试一下,如果有问题,检查下上述操作是否都正确,正常应该是OK的。
namespace访问互联网
当解决了第二个问题后,这个问题其实已经随之解决了。尝试运行ip netns exec ns1 ping www.baidu.com
发现确实是可以ping通的,这是因为在解决第二个问题时,已经将请求交给了eth0,不管是访问局域网还是访问互联网都已经是eth0的事情了。在我实验的环境中172.31.131.149 这台机器是可以连上互联网的,所以ns1自然也可以。(主机联网和解决第二个问题的过程是类似的)
不同主机之间的namespace通信;
要模拟不同主机之间的namespace通信,先在150上也新建个ns1、网桥bridge0以及veth1-vethb1、不同的是bridge0的ip 设为10.1.2.1, veth1 的ip设为10.1.2.2, 和149上的10.1.1.0/24作区分。
现在红色线就是待实现的部分。下面演示下用vxlan实现,本文再原理上不作深究,后续篇章中还会具体学习。具体操作如下:
-
首先创建简单的点对点 VxLAN 环境, 在149和150建一条虚拟通道。
在149上执行
ip link add vxlan1 type vxlan id 1 remote 172.31.131.150 dstport 4789 dev eth0
ip link set vxlan1 up
ip addr add 10.0.0.2/24 dev vxlan1
在150上执行
ip link add vxlan1 type vxlan id 1 remote 172.31.131.149 dstport 4789 dev eth0
ip link set vxlan1 up
ip addr add 10.0.0.3/24 dev vxlan1
在149上执行ping 10.0.0.3
, 显示结果已经可以ping通,说明vxlan虚拟通道已建立。
- 将vxlan1 添加到网桥中,ns1 请求到bridge0后,再将请求转到vxlan虚拟通道上。先把vxlan1的ip地址给移除(第一步添加ip只是为了确认通道是否建立,因为加到bridge0, ip地址也没用了。)
ip addr del 10.0.0.2/24 dev vxlan1 (149上执行)
ip addr del 10.0.0.3/24 dev vxlan1 (150上执行)
将vxlan1添加到网桥,分别在149和150上执行
ip link set vxlan1 master bridge0
为了方便测试,在150上添加一个ns2, ip设置为10.1.1.5(和149中的ns同网段)
进入10.1.1.2 去访问10.1.1.5, 成功!
总结
本篇演示了namespace通信的一些简单场景,在实际的使用中还有很多方面需要考虑。