1 | # 查看cni网桥的接入 |
1 | # 网络插件 就是解决跨主机node上的pod通信 |
1 | # kubelet 创建 容器 |
1 | # cni 网络规范: |
交换和路由
1 | # 两个问题: |
交换技术
- 路由器:网络出口
- 核心层:主要完成数据高效转发、链路备份等
- 汇聚层:网络策略、安全、工作站交换机的接入、VLAN之间通信等功能
- 接入层:工作站的接入
- 交换机工作在OSI参考模型的第二层,即数据链路层。
- 交换机拥有一条高带宽的背部总线交换矩阵,在同一时间可进行多个端口对之间的数据传输。
交换技术分为2层和3层:
2层:主要用于小型局域网,仅支持在数据链路层转发数据,对工作站接入。
3层:三层交换技术诞生,最初是为了解决广播域的问题,多年发展,三层交换机书已经成为构建中大型网络的主要力量。
广播域
- 交换机在转发数据时会先进行广播,这个广播可以发送的区域就是一个广播域。
- 交换机之间对广播帧是透明的,所以交换机之间组成的网络是一个广播域。
- 路由器的一个接口下的网络是一个广播域,所以路由器可以隔离广播域。
ARP(地址解析协议,在IPV6中用NDP替代)
- 发送这个广播帧是由ARP协议实现,ARP是通过IP地址获取物理地址的一个TCP/IP协议。
三层交换机
- 前面讲的二层交换机只工作在数据链路层,路由器则工作在网络层。
- 而功能强大的三层交换机可同时工作在数据链路层和网络层,并根据 MAC地址或IP地址转发数据包。
VLAN(Virtual Local Area Network):虚拟局域网
- VLAN是一种将局域网设备从逻辑上划分成一个个网段。
- 一个VLAN就是一个广播域,VLAN之间的通信是通过第3层的路由器来完成的。
- VLAN应用非常广泛,基本上大部分网络项目都会划分vlan。
- VLAN的主要好处:
- 分割广播域,减少广播风暴影响范围。
- 提高网络安全性,根据不同的部门、用途、应用划分不同网段
重点1: 在一个2层交换机下的两台服务器的通信流程
1 | 主机A(10) |
- 在本机查找ARP缓存表,是否存在要发送的数据的目的地之 -> 主机B的MAC地址
- 如果没有,本机会发送ARP广播包,达到2层交换机,询问20(主机B)的MAC地址是多少
- 交换机也会查本地ARP缓存表
- 如果有 就直接响应主机A,主机A得到后重新封装数据包中的目的MAC
- 如果没有,会发送除主机A之外的所有主机,每个主机都判断目的IP是不是自己,主机B发现是自己将mac地址响应回给交换机,交换机返回给主机A,并保存到自己的缓存表,不同的则将包丢弃
- 主机A获取到主机B的MAC地址,将目的MAC封装在包内发送给交换机,交换机再转发给主机B。
ARP缓存表记录着经过二层传输的源IP和目的IP的MAC地址,以便下次传输能直接使用。
主机A发送的广播包 是否会被所有的交换机收到?
- 交换机A 和 交换机B 并不是直连的 无法收到
- 交换机A 可以通过 3层交换机 转发给 交换机B ,如果没有3层交换机,上面是路由器就无法收到,路由器隔离广播域。
- 3层交换机 既可以处理2层数据包也可以处理3层数据包
重点2: 不在一个局域网之内,主机A 和 主机B 通信流程
1 | 1. 主机A 和 主机B 不在一个Vlan |
- 目的地址与本机不在同一个子网,会走默认网关
1 | [root@k8s-master1 ~]# ip route |
网关会查询路由表
路由器的路由表得知接口是哪个,到达哪个交换机,最后到服务器上。此时交换机会记录mac地址
- 目的地址 192.168.32.0 网关 接口 B
Kubernetes 网络模型
Kubernetes 要求所有的网络插件实现必须满足如下要求:
- 一个Pod一个IP
- 所有的 Pod 可以与任何其他 Pod 直接通信,无需使用 NAT 映射
- 所有节点可以与所有 Pod 直接通信,无需使用 NAT 映射
- Pod 内部获取到的 IP 地址与其他 Pod 或节点与其通信时的 IP 地址是同一个。
Kubernetes 网络组件之 Flannel
- Flannel是CoreOS维护的一个网络组件,Flannel为每个Pod提供全局唯一的IP,Flannel使用ETCD来存储Pod子网与Node IP之间的关系。
- flanneld守护进程在每台主机上运行,并负责维护ETCD信息和路由数据包。
1 | 1. k8s中所有的pod的IP地址都必须是唯一的,如果不唯一,数据包就不知道发送给谁,因为是全联通。 |
Flannel 部署
1 | https://github.com/coreos/flannel |
1 | # 每个node节点都以 DaemonSet 形式部署 ,保证每个node节点上运行守护进程 |
1 | # 查看 yaml 文件 |
1 | # 1. 部署后,配置信息会在每个node上的 |
1 | 1. flannel 的网络配置在 |
1 | 3. 二进制文件,kubelet会调用这个二进制文件 为创建的每个pod 配置网络信息,从小子网网段里分配IP地址 |
Flannel 工作模式及原理
Flannel支持多种数据转发方式
- UDP:最早支持的一种方式,由于性能最差,目前已经弃用。
- VXLAN:Overlay Network方案 ? ,源数据包封装在另一种网络包里面进行路由转发和通信 - 隧道方案
- Host-GW:Flannel 通过在各个节点上的Agent进程,将容器网络的路由信息刷到主机的路由表上,这样一来所有的主机都有整个容器网络的路由数据了。 - 路由方案
- 大多数网络插件都具有这两种方案,比如 Calico
VXLAN
- 二进制部署 支持 cni
1 | [root@k8s-master1 net.d]# cat /opt/kubernetes/cfg/kube-controller-manager.conf |
- 为了能够在二层网络上打通“隧道”,VXLAN 会在宿主机上设置一个特殊的网络设备作为“隧道”的两端。
- 这个设备就叫作 VTEP,即:VXLAN Tunnel End Point(虚拟隧道端点)。解决跨网段的数据通信。
- 下图flannel.1的设备就是VXLAN所需的VTEP设备。示意图如下:
1 | # 查看 每个node上flannel分配的子网 |
VXLAN 数据包的传输流程
1.容器路由: 容器根据路由表从eth0发出,通过 veth 到达cni0也就到达了node服务器
1 | root@my-nginx-67f56d94f7-lwl9c:/# ip route |
veth pair(对)
1 | 1. veth 设备对 网线的一头在容器,一头在node上 |
1 | # 创建容器时, node上 会被创建一个新的网络接口 |
1 | # 进入容器,里面的 网卡 eth0@if10 和 node上的 veth6cdc3884 就是 一对 veth pair |
1 | # 不同节点 需要走 宿主机路由表 走默认网关 |
2.主机路由: 数据包进入到宿主机虚拟网卡cni0,根据路由表转发到flannel.1虚拟网卡,也就是,来到了隧道的入口。
1 | 1. 宿主机发现目的IP 10.244.2.10 本机是无法处理,需要走默认网关 |
3. VXLAN封装: 而这些VTEP设备(二层)之间组成二层网络必须要知道目的MAC地址。这个MAC地址从哪获取到呢?其实在flanneld进程启动后,就会自动添加其他节点ARP记录,可以通过ip命令查看,如下所示:
1 | [root@k8s-master1 ~]# ip neigh show dev flannel.1 |
1 | # 数据包解析: |
4. 二次封包: 知道了目的MAC地址,封装二层数据帧(容器源IP和目的IP)后,对于宿主机网络来说这个帧并没有什么实际意义。接下来,Linux内核还要把这个数据帧进一步封装成为宿主机网络的一个普通数据帧,好让它载着内部数据帧,通过宿主机的eth0网卡进行传输。
5. 封装到UDP包发出去: 现在能直接发UDP包嘛?到目前为止,我们只知道另一端的flannel.1设备的MAC地址,却不知道对应的宿主机地址是什么。
- flanneld进程也维护着一个叫做FDB的转发数据库,可以通过bridge fdb命令查看:
1 | [root@k8s-master1 ~]# bridge fdb show dev flannel.1 |
1 | # 根据图中 udp数据包解析 将原来的包上面再加一次层 : |
可以看到,上面用的对方flannel.1的MAC地址对应宿主机IP,也就是UDP要发往的目的地。使用这个目的IP进行封装。
6. 数据包到达目的宿主机 Node1的eth0网卡发出去,发现是VXLAN数据包,把它交给flannel.1设备。flannel.1设备则会进一步拆包,取出原始二层数据帧包,发送ARP请求,经由cni0网桥转发给container。
- 数据包头部里面会有 VXLAN 标记,加了编号VNI,flannel.1的1就是编号,交给flannel.1去处理
- 拿到源IP和目的IP,去判断,一看是cni0网桥的交给cni0
- cni0进行广播 拿着ip进行广播, 最后转发到容器里
- 最复杂的地方在于flannel.1的vtep 对数据的封包与解封包
- flannel.1 这种封包 会导致效率下降 ,VXLAN这种形式就是Overlay Network方案,源数据包封装到另一层网络里,叠加网络
- VXLAN 只是实现封包解封包的技术
- VXLAN 在大型网络架构中,效率并不高
Host-GW
- host-gw模式相比vxlan简单了许多,直接添加路由,将目的主机当做网关,直接路由原始封包。
- 没有 flannel.1 阶段了
- host-gw模式 就像把每一个node主机当做网关
设置flannel使用host-gw模式
1 | [root@k8s-master1 ~]# vim /tmp/k8s/kube-flannel.yaml |
1 | # 更新 flannel 部署 |
1 | 当你设置flannel使用host-gw模式,flanneld会在宿主机上创建节点的路由表: |
1 | 1. 目的 IP 地址属于 10.244.1.0/24 网段的 IP 包,应该经过本机的 eth0 设备发出去(即:dev eth0); |
小总结
- 如果追求性能 并且 node的二层网络能通 可以使用 Host-GW 工作模式
- 如果node不能通过二层通信,需要路由转发(可能不同Vlan),使用 VXLAN
- 用户访问 - svc(nodeport) -> iptables/ipvs -> node1\node2\node3 ,如果访问的pod正好是本机的node那么直接走本地的路由访问,
如果到了其他node,那么就是跨主机网络通信,需要走路由表,如果工作模式是vxlan,那么要走flannel.1进行封包解封包流程
重点是看哪种工作流程和路由规则,一定要搞清楚里面的概念很多,加油吧 - Host-GW 二层包转发
阿里云主机的 host-gw方案不通
- 阿里云不通,但是本地虚拟机是通的,测试环境迁移到虚拟机
- Calico BGP 也没通,测试下虚拟机 通不通
Kubernetes 网络方案之 Calico
- Calico是一个纯三层的数据中心网络方案,Calico支持广泛的平台,包括Kubernetes、OpenStack等。
- Calico 在每一个计算节点利用 Linux Kernel 实现了一个高效的虚拟路由器( vRouter) 来负责数据转发,而每个 vRouter 通过 BGP 协议负责把自己上运行的 workload 的路由信息向整个 Calico 网络内传播。
- 此外,Calico 项目还实现了 Kubernetes 网络策略,提供ACL功能。
- 访问控制列表(ACL)是一种基于包过滤的访问控制技术,它可以根据设定的条件对接口上的数据包进行过滤,允许其通过或丢弃。
BGP 概述
- 实际上,Calico项目提供的网络解决方案,与Flannel的host-gw模式几乎一样。
- 也就是说,Calico也是基于路由表实现容器数据包转发,但不同于Flannel使用flanneld进程来维护路由信息的做法,而Calico项目使用BGP协议来自动维护整个集群的路由信息。
- GP英文全称是Border Gateway Protocol,即边界网关协议,它是一种自治系统间的动态路由发现协议,与其他 BGP 系统交换网络可达信息。
1 | # 维护路由信息 |
为了能让你更清楚理解BGP,举个例子:
- 在这个图中,有两个自治系统(autonomous system,简称为AS):AS 1 和 AS 2。
1 | # 什么是自治系统 |
Calico BGP实现
- 在了解了 BGP 之后,Calico 项目的架构就非常容易理解了,Calico主要由三个部分组成:
1 | - Felix:以DaemonSet方式部署,运行在每一个Node节点上,主要负责维护宿主机上路由规则以及ACL规则。 |
Calico 部署
1. 删除Flannel
1 | # 切换网络别忘记停机维护 夜深人静 |
1 | # 1 添加证书 |
1 | # 2 指定读取位置 |
1 | # 3. 指定etcd连接字符串 |
1 | # 4. 修改 Pod CIDR |
1 | # 5. 修改工作模式,也有两种 IPIP 和 BGP(应用最多) |
1 | # 6. 清理网络 每台node都要做 |
1 | # 7. 部署 calico |
Calico 管理工具
1 | 下载工具: |
1 | # 向etcd读取数据 |
Calico BGP 原理剖析
- Pod 1 访问 Pod 2大致流程如下:
1 | 1. 数据包从容器1出 到达Veth Pair另一端(宿主机上,以cali前缀开头); |
1 | 1. 创建pod kubelet 调用 calico 二进制程序创建网络,配置IP地址 |
1 | 1. 到达cali之后就是到达了宿主机node服务器身上 |
- 其中,这里最核心的“下一跳”路由规则,就是由 Calico 的 Felix 进程负责维护的。
- 这些路由规则信息,则是通过 BGP Client 也就是 BIRD 组件,使用 BGP 协议传输而来的。
- 不难发现,Calico 项目实际上将集群里的所有节点,都当作是边界路由器来处理,它们一起组成了一个全连通的网络,互相之间通过 BGP 协议交换路由规则。
- 这些节点,我们称为 BGP Peer。
Route Reflector 模式(RR)
1 | 参考: |
1 | [root@k8s-master1 ~]# calicoctl node status |
- Node-to-Node 在100台以下
- Calico 维护的网络在默认是(Node-to-Node Mesh)全互联模式,Calico集群中的节点之间都会相互建立连接,用于路由交换。
- 但是随着集群规模的扩大,mesh模式将形成一个巨大服务网格,连接数成倍增加。
1 | # 默认连接 |
- 这时就需要使用 Route Reflector(路由器反射)模式解决这个问题。
- 确定一个或多个Calico节点充当路由反射器,让其他节点从这个RR节点获取路由信息。
1 | # 路由反射 至少2-3个 有备用 |
1. 关闭 node-to-node BGP网格
1 | [root@k8s-master1 demo]# vim bgp.yaml |
2. 配置指定节点充当路由反射器
- 为方便让BGP Peer轻松选择节点,通过标签选择器匹配。
- 给路由器反射器节点打标签:
1 | kubectl label node my-node route-reflector=true |
1 | # 然后配置路由器反射器节点 routeReflectorClusterID: |
1 | # 将非路由反射器节点 连接路由反射器 获取交换信息 |
1 | # 使用路由反射器解决当bgp越来越大时的路由消耗 |
IPIP 模式
- 在前面提到过,Flannel host-gw 模式最主要的限制,就是要求集群宿主机之间是二层连通的。
- 而这个限制对于 Calico 来说,也同样存在。
修改为IPIP模式:
1 | [root@k8s-master1 demo]# calicoctl get ipPool -o yaml > ipip.yaml |
1 | # ip route 变化 tunl0 隧道网卡 有自己的IP 10.244.159.131 |
IPIP示意图:
Pod 1 访问 Pod 2大致流程如下:
1 | 1. 数据包从容器1出,到达Veth Pair另一端(宿主机上,以cali前缀开头); |
1 | [root@k8s-master1 demo]# kubectl get pods -o wide |
1 | # 里面一层 容器到容器数据包 原始IP包 |
- 不难看到,当 Calico 使用 IPIP 模式的时候,集群的网络性能会因为额外的封包和解包工作而下降。
- 所以建议你将所有宿主机节点放在一个子网里,避免使用 IPIP。
CNI 网络方案优缺点及最终选择
1 | 先考虑几个问题: |
办公网络与K8S网络如何互通
- 测试环境 k8s 集群
- 开发人员 办公网络
- 微服务通过 podip 调用
- 办公网络 如何 访问 podip 打通网络
网络策略
为什么需要网络隔离?
- CNI插件插件解决了不同Node节点Pod互通问题,从而形成一个扁平化网络,默认情况下,Kubernetes 网络允许所有 Pod 到 Pod 的流量
在一些场景中,我们不希望Pod之间默认相互访问,例如:
- 应用程序间的访问控制。例如微服务A允许访问微服务B,微服务C不能访问微服务A
- 开发环境命名空间不能访问测试环境命名空间Pod
- 当Pod暴露到外部时,需要做Pod白名单
- 多租户网络环境隔离
所以,我们需要使用network policy对Pod网络进行隔离。支持对Pod级别和Namespace级别网络访问控制。
acl Pod网络入口方向隔离
- 基于Pod级网络隔离:只允许特定对象访问Pod(使用标签定义),允许白名单上的IP地址或者IP段访问Pod
- 基于Namespace级网络隔离:多个命名空间,A和B命名空间Pod完全隔离。
acl Pod网络出口方向隔离
- 拒绝某个Namespace上所有Pod访问外部
- 基于目的IP的网络隔离:只允许Pod访问白名单上的IP地址或者IP段
- 基于目标端口的网络隔离:只允许Pod访问白名单上的端口
1 | 1. 对 default 空间下 带标签 role: db 的pod 做网络策略 |
1 | 配置解析: |
入站、出站网络流量访问控制案例
Pod访问限制
- 准备测试环境,1组web pod,2个client pod
1 | [root@k8s-master1 demo]# kubectl get pods -o wide |
1 | [root@k8s-master1 demo]# vim ns.yaml |
1 | 隔离策略配置: |
1 | # 先在不做策略的时候 测试访问 run client1 和 run client2 |
1 | # 增加规则 |
1 | # 测试 |
命名空间隔离
- 两个开发团队,都有自己的命名空间,他们之间的pod都不可以相互访问
1 | [root@k8s-master1 demo]# kubectl run client3 --generator=run-pod/v1 --image=busybox -n kube-system --command -- sleep 36000 |
1 | [root@k8s-master1 demo]# kubectl get pods -o wide |
1 | # 需求: |
1 | # 执行测试 |
Flannel 实现网络隔离
1 | 1. 需要 canal 插件支持 |
1 | 1. 阿里云主机 |