
Kubernetes 网络名称空间与网桥(Bridge)
网络名称空间(namespace)简介
linux名称空间是一种linux内核功能,用于隔离虚拟化系统资源。仅限于命名空间的进程只能与属于同一命名空间的资源或进程进行交互。名称空间是Docker孤岛模型的重要组成部分。每种类型的资源都有名称空间,包括net
(网络)、mnt
(存储)、uts
(主机名控制)和user
(UID映射)
每个进程都有一个/proc/[pid]/ns/
子目录,其中每个名称空间都包含一个条目,该名称空间支持由setns操作(2)。
(可通过如下命令进行查看)
# ls -l /proc/$$/ns
total 0
lrwxrwxrwx. 1 root root 0 Apr 27 11:20 ipc -> ipc:[4026531839]
lrwxrwxrwx. 1 root root 0 Apr 27 11:20 mnt -> mnt:[4026531840]
lrwxrwxrwx. 1 root root 0 Apr 27 11:20 net -> net:[4026531956]
lrwxrwxrwx. 1 root root 0 Apr 27 11:20 pid -> pid:[4026531836]
lrwxrwxrwx. 1 root root 0 Apr 27 11:20 user -> user:[4026531837]
lrwxrwxrwx. 1 root root 0 Apr 27 11:20 uts -> uts:[4026531838]
在(net)网络名称空间中呢,network namespace主要提供了关于网络资源的隔离,包括网络设备、IPv4和IPv6协议栈、IP路由表、防火墙、/proc/net目录、/sys/class/net目录、套接字等。一个物理的网络设备最多存在于一个network namespace中,可以通过创建veth pair对在不同的network namespace间创建通道,以达到通信目的。
当然一般只会在用户空间隔离,包括docker也是通过用户空间进行隔离,并且它们所使用的是同一个内核。
# 主机是Ubuntu系统
uname -u
# 运行centos容器
docker run -it --rm centos /bin/bash
uname -u
Namespace | 系统调用参数 | 隔离内容 | 内核版本 |
---|---|---|---|
UTS | CLONE_NEWUTS | 主机名域名 | 2.6.19 |
IPC | CLONE_NEWIPC | 信号量、消息队列和共享内存 | 2.6.19 |
PID | CLONE_NEWPID | 进程编号 | 2.6.24 |
Network | CLONE_NEWNET | 网络设备、网络栈、端口 | 2.6.29 |
Mount | CLONE_NEWNS | 挂载点(文件系统) | 2.3.19 |
User | CLONE_NEWUSER | 用户和用户组 | 3.8 |
查看网络名称空间的命令为:
ip netns list
Veth虚拟网卡对
简单来讲它可以连接不同的网络名称空间进行网络通信,也可以连接网桥设备等。
由于它的创建是一对一对的所以其中的一个会分别插入需要通信的不同网络名称空间的两端。
我们可以通过如下命令来进行创建:
ip link add <p1-name> type veth peer name <p2-name>
p1-name
和p2-name
是分配给两个连接端点的名称。在这对设备中的一个设备上传输的数据包会立即在另一个设备上接收。当任一设备关闭时,pairis
的链接状态将关闭。
# 添加网卡虚拟对
ip link add p11 type veth peer name p22
# 添加net1网络名称空间
ip netns add net1
# 查看网卡
ip a
# 列出所有网络名称空间
ip netns list
# 绑定p11链接到net1网络上
ip link set p11 netns net1
# 查看net1网络名称空间下拥有的网卡
ip netns exec net1 ip a
# 通过ethtool -S命令查找网卡对的另一半
ip netns exec net1 ethtool -S p11
# 再次查看所有的网卡信息
ip a
从上图得知在没绑定之前,通过ip a
可以查看到虚拟网卡对p11
和p12
,在p11
绑定了net1
网络名称空间后,本地的p11
虚拟网卡被net1
网络名称空间拿走了。所以在net1
下找到了p11
虚拟网卡,接着我们通过ethtool
查看p11
链接到peer的索引为4
的地方,也就ip a 下标第4的一个也是我们的p22
。
如果想要一步到位,一个虚拟网卡对应一个网络名称空间,可以通过下面的命令进行创建:ip link add <p1-name> netns <p1-ns> type veth peer <p2-name> netns <p2-ns>
TAP与TUN设备
tap/tun 提供了一台主机内用户空间的数据传输机制。它虚拟了一套网络接口,这套接口和无力的接口无任何区别,可以配置IP,可以路由流量,不同的是,他的流量只在主机内流通。
作为网络设备,tap/tun也需要配套相应的驱动程序才能工作。tap/tun 驱动程序分两个部分,一个是字符设备驱动,一个是网卡驱动。这两部分驱动程序分工不太一样,字符驱动负责数据包和用户空间的传送,网卡驱动负责数据包在TCP/IP网络协议栈上的传输和处理。
tap/tun有些许的不同,tun只操作三层的IP包,而tap操作二层的以太网帧。
在Linux中,用户空间和内核空间的数据传输有多种方式,字符设备就是其中的一种。tap/tun通过驱动程序和一个与在Linux内核2.6.x之后的版本中,tap/tun对应的设备文件分别为:
- tap: /dev/tap0
- tun: /dev/net/tun
设备文件即充当了用户空间和内核空间通信的接口。当应用程序打开设备文件时,驱动程序就会创建并注册相应的虚拟设备接口,一般以tunX
或tapX
命名。当应用程序关闭文件时,驱动也会自动删除 tunX
和tapX
设备,还会删除已经建立起来的路由等信息。
tap/tun 设备文件就像一个管道,一端连接着用户空间,一端连接着内核空间。当用户程序向 /dev/net/tun
或 /dev/tap0
写数据时,内核就可以从对应的 tunX
或 tapX
接口读到数据,反之,内核可以通过相反的方式向用户程序发送数据。
tap/tun是Linux内核2.4.x版本之后实现的虚拟网络设备,不同于无力网卡靠硬件板卡实现,tap/tun虚拟网卡完全由软件实现,功能和硬件实现完全没差别,它们都属于网络设备,都可配置IP,都归Linux网络设备管理模块统一管理。
简单使用案例
程序A希望构造数据包发往192.168.1.0/24
网段的主机192.168.1.1
。
1.程序A构造数据包,目的地址上 192.168.1.1
,通过socket A将这个数据包发给协议栈。
2.协议栈更具数据包的目的IP地址,匹配路由规则,发现要从tun0出去。
3.tun0
发现自己的另一端应用程序B打开了,于是将数据发给程序B
4.程序B收到数据后,做一些跟业务相关的操作,然后构造一个新的数据包,源IP是eth0
的IP,目的IP是10.1.1.0/24
的网关10.1.1.1
,封装原来的数据协议包,重新发给协议栈。
5.协议栈再根据本地路由,将这个数据包从eth0发出。
Veth Demo
veth pair(虚拟网卡对)试用于不同的network namespace 间进行通信,veth pair将一个network namespace数据发往另一个network namespace 的veth。
清理网络空间
我们先清理刚刚创建的network namespace。
ip netns list
ip netns delete net1
ip netns list
开始
# 首先我们创建`ns1`和`ns2`名称空间
ip netns add ns1
ip netns add ns2
# 创建两个虚拟网络对
ip link add veth0 type veth peer name veth1
# 插入两端
ip link set veth0 netns ns1
ip link set veth1 netns ns2
ip netns exec ns1 ip a
# 创建ip地址
ip netns exec ns1 ip add a 10.1.1.1/24 dev veth0
ip netns exec ns2 ip add a 10.1.1.2/24 dev veth1
# 查看配置IP情况
ip netns exec ns1 ifconfig -a
ip netns exec ns2 ifconfig -a
# 激活网卡
ip netns exec ns1 ip link set veth0 up
ip netns exec ns2 ip link set veth1 up
# ping测试
ip netns exec ns1 ping 10.1.1.2
抓包工具rpcapd
官网项目地址:https://github.com/rpcapd-linux/rpcapd-linux
可通过如下命令在Ubuntu中安装,如果找不到build-dep
和libpcap
了,请更新或换源。
sudo apt-get build-dep libpcap
git clone https://github.com/rpcapd-linux/rpcapd-linux.git
cd /Home
cd rpcapd/libpcap
./configure && make
cd ../
make
./rpcapd -h
我们可以通过运行./rpcapd -p 2002
来进行抓包并进行远程获取包的端口为2002
。
注意:需要注意防火墙的规则,可以通过如下操作来进行清理防火墙的规则。
$ iptables -F
$ systemctl stop firewalld.service
然后我们可以打开我们的Wireshark进行连接。操作如下:
捕获—>选项—>管理接口—>远程接口—> +就可以进行添加与连接了。
如果添加不了,请重启Wireshark或检测ip与port是否正确。
如果你是想去抓我们刚刚案例的包这样说抓不到的,因为你抓的说root网络空间下的包.
本地抓包
一般是在请求的过程中进行监听并打包成抓包文件,例如veth.cap
。可通过如下命令进行抓包:
tcpdump -i any -w veth.cap
按Ctrl+C
之后会生成veth.cap
的包,然后可以打开Wireshark进行分析。
如果要收集刚我们案例下的包,可以通过在指定的网络名称空间下进行抓包。
ip netns exec ns1 tcpdump -i any -w veth1.cap
Linux Bridge 与网络名称空间的连接
接下来我们模拟docker的bridge网络模式,它大致的网络如下图所示:
我们接下来要模拟ns2
通过Bridge模式向ns1
发包,要做的如下图所示:
# 查看bridge
brctl show
# 如果没有可以通过如下命令进行安装
apt install -y bridge-utils
# 清理网络名称空间
ip netns list
ip netns delete ps1
ip netns delete ps2
# 创建一个Bridge
ip link add br1 type bridge
ip link set br1 up
# 简写也可以:ip l a br1 type bridge
# 列出所有的bridge网桥
brctl show
# 然后激活一下
ip a s br1 up
# 创建网络名称空间
ip netns add ns1
ip netns add ns2
# 创建虚拟网卡对 veth pair
ip link add veth0 type veth peer name br1-veth0
ip link add veth1 type veth peer name br1-veth1
# 接着我们需要将网络名称空间与网桥通过veth连接起来
# 先插入一头到网络名称空间里面去
ip l set veth0 netns ns1
ip link set veth1 netns ns2
# 再插入一头到网桥里面去
ip link set br1-veth0 master br1
ip link set br1-veth1 master br1
# 都要激活
ip l s br1-veth0 up
ip l s br1-veth1 up
# 配置IP
ip netns exec ns1 ip add a 10.1.1.2/24 dev veth0
ip netns exec ns2 ip a a 10.1.1.3/24 dev veth1
ip netns exec ns1 ip link set veth0 up
ip netns exec ns2 ip link set veth1 up
# 查看IP配置情况
ip netns exec ns1 ip a
ip netns exec ns2 ip a
# 解下来我们通过ns2来ping ns1
ip netns exec ns2 ping 10.1.1.2
如果出ping不通的情况,有可能是你的防火墙出现了一些问题,请加上下面的命令再次尝试。
ip -n ns1 link set lo up
ip -n ns2 link set lo up
基于Linux Route路由转发
路由器与交换机的区别在于,交换机只能通过二层转发和在同一个ip网段下转发。而Route路由器是可以通过三层进行转发的,也就是通过ip进行转发,并且可以通过跨网段进行转发。
接下来我们模拟Linux Route路由器跨网段的转发请求。
# 设置转发
echo 1 >/proc/sys/net/ipv4/ip_forward
# 添加命名空间
ip netns add ns1
ip netns add ns2
# 添加虚拟网卡对
ip link add v1 type veth peer name v1_r
ip link add v2 type veth peer name v2_r
# 设置路由端的虚拟网卡的网段
ip a a 10.1.1.1/24 dev v1_r
ip a a 10.1.2.1/24 dev v2_r
# 激活网卡
ip l s v1_r up
ip l s v2_r up
# 绑定网络名称空间端的网卡
ip l s v1 netns ns1
ip l s v2 netns ns2
# 设置ip
ip netns exec ns1 ip a a 10.1.1.2/24 dev v1
ip netns exec ns2 ip a a 10.1.2.2/24 dev v2
# 激活一下
ip netns exec ns1 ifconfig v1 up
ip netns exec ns2 ifconfig v2 up
# 查看ns1与ns2网卡设置情况
ip netns exec ns1 ip a
ip netns exec ns2 ip a
# 设置包的出口
# ns1 的包从10.1.2.1出去
# ns2 的包从10.1.1.1出去
ip netns exec ns1 route add -net 10.1.2.0 netmask 255.255.255.0 gw 10.1.1.1
ip netns exec ns2 route add -net 10.1.1.0 netmask 255.255.255.0 gw 10.1.2.1
# 也可以这样
# ip netns exec ns1 route add -net 10.1.2.0 netmask 255.255.255.0 dev v1
# ip netns exec ns2 route add -net 10.1.1.0 netmask 255.255.255.0 dev v2
# 进行ping测试
ip netns exec ns1 ping 10.1.2.2
容器与容器跨主机通信
我们在第二台主机可以通过docker创建IP为172.18.0.1
名为net_18
网络,并且在net_18
网络下运行一个容器,然后我们在第一台主机中默认的bridge下创建一个容器,通过添加相关的路由规则实现互联互通。
前提是需要这两台机子有一个口是通的。(大致示例图如下)
主机一ip是:10.211.55.11
主机二ip是:10.211.55.12
主机二运行如下命令。
# 创建网络
docker network create net_18 --subnet=172.18.0.0/16
# 查看网络
docker network list
# 运行容器
docker run --name c2 --network=net_18 -td ikubernetes/myapp:v1
# 查看容器信息
docker inspect c2
主机一运行如下命令。
# 运行容器
docker run --name c1 -td ikubernetes/myapp:v1
# 查看容器IP信息
docker inspect c1
我们通过ping测试,发现并不能测试得通。
docker exec c1 ping 172.17.0.2
我们通过第一台查看路由,发现并没有找到172.18.0.0
的出处,所以我们只需要在主机上添加好相关路由即可。
route -n
route add -net 172.18.0.0 netmask 255.255.0.0 gateway 10.211.55.12
第二台主机上,我们也添加相关的路由规则
route -n
route add -net 172.17.0.0 netmask 255.255.0.0 gateway 10.211.55.11
再次ping就可以了
注意:如果还ping不通的话,请在清理防火墙规则后再次尝试。(iptables -F
)
因为有可能是由于iptables规则引起的。
IPIP模式
是路由器把一种网络层协议封装到另一个协议中以跨过网络传送到另一个路由器的处理过程。(像VPN)
如下图所示:
在包中封装两层IP从而可以达到tun1与tun2的通信,还不懂的话我们待会可以通过抓包来查看。
接下来我们可以通过如下命令进行Demo演示。
# 添加网络名称空间ns1和ns2
ip netns add ns1
ip netns add ns2
# 创建虚拟网卡对
ip l a v1 type veth peer name v1_r
ip l a v2 type veth peer name v2_r
# 绑定给路由器端网口绑定IP
ip a a 10.10.10.1/24 dev v1_r
ip a a 10.10.20.1/24 dev v2_r
# 激活路由端的网卡
ip l s v1_r up
ip l s v2_r up
# 虚拟网卡对绑定到网络名称空间
ip l s v1 netns ns1
ip l s v2 netns ns2
# 创建虚拟网卡对的ip绑定到名称空间中
ip netns exec ns1 ip a a 10.10.10.2/24 dev v1
ip netns exec ns2 ip a a 10.10.20.2/24 dev v2
# 激活名称空间网卡
ip netns exec ns1 ifconfig v1 up
ip netns exec ns2 ifconfig v2 up
# 添加路由项
ip netns exec ns1 route add -net 10.10.20.0 netmask 255.255.255.0 gw 10.10.10.1
ip netns exec ns2 route add -net 10.10.10.0 netmask 255.255.255.0 gw 10.10.20.1
# 创建tun1和tun2
ip netns exec ns1 ip tunnel add tun1 mode ipip remote 10.10.20.2 local 10.10.10.2
ip netns exec ns2 ip tunnel add tun2 mode ipip remote 10.10.10.2 local 10.10.20.2
# 激活网卡
ip netns exec ns1 ifconfig tun1 up
ip netns exec ns2 ifconfig tun2 up
# 设置Peer
ip netns exec ns1 ip a a 10.10.100.10 peer 10.10.200.10 dev tun1
ip netns exec ns2 ip a a 10.10.200.10 peer 10.10.100.10 dev tun2
# 开启内核转发
echo 1 >/proc/sys/net/ipv4/ip_forward
# ping测试
ip netns exec ns1 ping 10.10.200.10
抓包查看,是多封装了一层ip
ip netns exec ns1 tcpdump -i any -w ipip.cap
OVS网络
在Openstack上,我们用得最多的是Linux+OVS Bridge的形式。
Ubuntu安装ovs工具
sudo apt-get install openvswitch-switch openvswitch-common
Demo案例
# 创建ovs bridge
ovs-vsctl add-br ovs-br
# 显示ovs
ovs-vsctl show
# 添加网络名称空间
ip netns a ns1
ip netns a ns2
# 添加虚拟网卡对
ip l a veth0 type veth peer name ovs-veth0
ip l a veth1 type veth peer name ovs-veth1
# 绑定到网络名称空间
ip l s veth0 netns ns1
ip l s veth1 netns ns2
# ovs绑定虚拟网卡对
ovs-vsctl add-port ovs-br ovs-veth0
ovs-vsctl add-port ovs-br ovs-veth1
ovs-vsctl show
# 激活网卡
ip netns exec ns1 ip l s veth0 up
ip netns exec ns2 ip l s veth1 up
ip l s ovs-veth0 up
ip l s ovs-veth1 up
# 设置ip
ip netns exec ns1 ip a a 10.1.1.2/24 dev veth0
ip netns exec ns2 ip a a 10.1.1.5/24 dev veth1
# 查看配置情况
ip netns exec ns1 ifconfig
ip netns exec ns2 ifconfig
# 测试
ip netns exec ns1 ping 10.1.1.5
欢迎加群讨论技术,1群:677373950(满了,可以加,但通过不了),2群:656732739

