Kubernetes网络方案之 Calico策略实践node
案例:因为k8s集群部署以前的方案是flannel网络策略,因此这里将flannel策略切换成calico网络策略linux
Calico是一个纯三层的数据中心网络方案,Calico支持普遍的平台,包括Kubernetes、OpenStack等。 Calico 在每个计算节点利用 Linux Kernel 实现了一个高效的虚拟路由器( vRouter) 来负责数据转发,而每一个 vRouter 经过 BGP 协议负责把本身上运行的 workload 的路由信息向整个 Calico 网络内传播。 此外,Calico 项目还实现了 Kubernetes 网络策略,提供ACL功能。
Calico会使用kernel来实现一个虚拟的路由器,来维护这些路由表,而后每一个节点做为虚拟的路由器以后,再根据BGP来相互交换这些路由信息,致使能让它在整个集群节点这些数据之间的交换,并且calico来实现k8s网络策略提供ACL功能nginx
一、BGP概述git
实际上,Calico项目提供的网络解决方案,与Flannel的host-gw模式几乎同样。也就是说,Calico也是基于路由表实现容器数据包转发,但不一样于Flannel使用flanneld进程来维护路由信息的作法,而Calico项目使用BGP协议来自动维护整个集群的路由信息。 BGP英文全称是Border Gateway Protocol,即边界网关协议,它是一种自治系统间的动态路由发现协议,与其余 BGP 系统交换网络可达信息。
这里使用BGP来实现整个网络的数据交换,这个其实和flannel的host-gw的模式几乎同样,只不过是使用BGP来实现数据的交换的,flannel是本身来维护这些,而BGP是一个在大型网络中采用的一个动态协议,因此calico使用这个BGP做为一个路由的交换,它也是性能和数据包,数据表和集群规模到达必定量时,也能保证好一个好的性能github
通常管机房服务器会说:咱们家机房时采用BGP多线的。" 这个说的也就是边界网关协议,路由也就是选择一个路径,转发出去时根据路由表的,路由表又分为了,动态和静态,静态就是人工去添加那些路由表信息,动态就是相互的能感知,当网络中设备不少,vlan不少,跨公司实现通讯,那若是手动的去配置这些路由表信息,显然人工量是很大的,因此当这个到达必定规模的时候,基本上都会采用动态的路由协议,BGP就是其中一个,好比OSPF、RIP都是路由表的学习,动态的去感知网络的拓扑的,也就是在这个互联网大型网络中,各个路由设备他们之间学习都是经过动态去学习的,那BGP就是其中一个协议
为了能让你更清楚理解BGP,举个例子:
在这个图中,有两个自治系统(autonomous system,简称为AS):AS 1 和 AS 2。web
自治系统,能够想成公司的网络和其余公司的网络,两个就能够理解为两个自治系统,每一个自治系统是由本身交换机,路由器来组成的,而这些交换机路由器单独去运行,它不依赖于别的公司,别的公司也不依赖你公司的网络,均可以去独立的单独的去运行,一个大学,一个企业均可以说成是一个自治系统,可是这个自治系统也没什么交际,你家的网络和邻居家的网络也没什么来往,可是若是他们想通讯,他们自己就不在一个网络里面,你家的网络和它家的网络上层的网络出口的交换机必须相互的学习到,并且咱们用到的电脑都是私网IP,邻居家也用的是私网IP,甚至这些IP都是冲突的,若是想实现两家的网络内网可以通讯,首先保证用到的ip地址不能冲突,还要保证上层的路由出口之间可以相互学到本身的路由表,好比你家的路由器可以学习到当前路由表的信息
。docker
BGP简单来讲就是将两个自治系统进行链接起来,两个可以相互的通讯,这就是BGP的做用,这里有两个AS,能够比如两个学校两个公司,两个都不是同网络,如今要是想AS 1下的192.168与AS 2下的172.17进行去通讯,应该怎么走?数据库
192.168.1.10先走交换机,再到路由器,从路由器的A口进去,他们两个公司要是想通讯,路由器必须是可达的,是能够通讯的,只要创建了通讯以后,数据包到达了B节点,而后它怎么知道转发到路由器2呢?因此就须要路由表的存在,路由器1根据转发的目的地址,172.17.1.20这个网段,看本地有没有路由表,它会发现本地有个路由表,是转发到路由器2的,是从B口出去的,而后就会转发到路由器2上面,路由器2确定知道它本身管辖的网络是多少,由于它自己就学习到了本身的目的地址,不过没有下一跳,由于在本身的管辖以内,好比1.20,有这个IP,那么就不须要下一跳,而后之间根据接口转发到A口里面,A口里面正好接的是交换机,而后交换机从二层传输到对应的目的地址上了,那么这样节点就能够通讯了vim
那么BGP在这个环境中启动了什么做用?这个路由表也能够手动的进行添加,而后指向下一跳就是这个route的路由器,它也会转发过来,只要他们的网络是通的,若是节点不少,vlan不少,这样的话添加路由表就很大,并且在互联网中还会有别的路由器,可能还会用到别的网络进行去通讯,因此这里的BGP就是能够相互的去学习到相互的路由表信息,那么route1为何有route2的信息,其实就是在route学到的,那么AS 2要访问AS 2的节点,首先它的路由表也能学到目标地址,那么就能之间转发到路由器中,而后转发到目的的服务器上
api
这种动态路由协议跟咱们的flannel中host-gw模式有点相似,比如服务器就是咱们k8s中的容器,AS 1,AS 2都当成k8s的node,
以前是把当前节点看成一个网关,如今这个Calico引入这个BGP,它是将每一个node,都作成一个虚拟路由器,他们经过BGP实现相互路由信息的交换,而flannel是由一个守护进程维护的每一个路由表,发送到本地的节点,而calico采用的是BGP来实现数据交换,但路由表也要写到每一个节点上。
在互联网中,一个自治系统(AS)是一个有权自主地决定在本系统中应采用何种路由协议的小型单位。这个网络单位能够是一个简单的网络也能够是一个由一个或多个普通的网络管理员来控制的网络群体,它是一个单独的可管理的网络单元(例如一所大学,一个企业或者一个公司个体)。一个自治系统有时也被称为是一个路由选择域(routing domain)。一个自治系统将会分配一个全局的惟一的16位号码,有时咱们把这个号码叫作自治系统号(ASN)。 在正常状况下,自治系统之间不会有任何来往。若是两个自治系统里的主机,要经过 IP 地址直接进行通讯,咱们就必须使用路由器把这两个自治系统链接起来。BGP协议就是让他们互联的一种方式。
二、Calico BGP实现
这是calico的架构图,也是官方的一张图10.0.0.1是容器1,10.0.0.2是容器2,这里会有一个cali的接口,这两个设备也是veth的设备,这里它把宿主机看成一个虚拟路由器,而后这个虚拟路由器走的就是BGP协议,里面涉及到两个组件一个是Client,一个是Felix,主要负责写入机器的理由表信息,以前写入路由表信息的是flannel的守护进程去写的,calico是使用Felix去写的。
另外也是使用daemonset方式部署到每一个节点上的,calico也是使用etcd来保持calico设置的网络策略以及配置状态,在这里面最关键的是BGP的client,它主要来提供这个协议的,也就是每一个节点都有一个BGP client它们之间创建一个BGP的链接,而后走BGP的链接,把各自的路由表信息交换一下,这样整个机器的每一个节点容器就造成了一个完整的拓扑规则了,因此他们是走了一个BGP的规则,flannel就是一个简单的TCP的规则去作的。
在了解了 BGP 以后,Calico 项目的架构就很是容易理解了,Calico主要由三个部分组成:
Felix:以DaemonSet方式部署,运行在每个Node节点上,主要负责维护宿主机上路由规则以及ACL规则。
BGP Client(BIRD):主要负责把 Felix 写入 Kernel 的路由信息分发到集群 Calico 网络。
Etcd:分布式键值存储,保存Calico的策略和网络配置状态。
calicoctl:容许您从简单的命令行界面实现高级策略和网络。
三、Calico 部署git clone git@gitee.com:zhaocheng172/calico.git
这里须要将你的公钥给我,才能拉下来,否则没有权限
下载完后还须要修改里面配置项:
由于Calico使用的etcd一些策略一些网络配置信息的,还有一些calico的属性信息都是保存在etcd中的,而etcd也在k8s集群中部署,因此咱们之间使用现有的k8s的etcd就能够了,若是使用https还要配置一下证书,而后选择一些pod的网络,还有工做模式
具体步骤以下:
配置链接etcd地址,若是使用https,还须要配置证书。
(ConfigMap,Secret) 根据实际网络规划修改Pod CIDR(CALICO_IPV4POOL_CIDR) 选择工做模式(CALICO_IPV4POOL_IPIP),支持BGP,IPIP
calico也是使用configmap保存配置文件的,secret是存储etcd它的https的证书的,分为3项
etcd-key: null etcd-cert: null etcd-ca: null
指定etcd链接的地址: etcd_endpoints: "http://<ETCD_IP>:<ETCD_PORT>"
当启动secret挂载到容器中时,它的文件是挂载哪一个文件下,在这里指定好
etcd_ca: "" # "/calico-secrets/etcd-ca" etcd_cert: "" # "/calico-secrets/etcd-cert" etcd_key: "" # "/calico-secrets/etcd-key"
如今进行一下切换网络到calico
1、因此修改etcd一共修改3个位置
一、etcd的证书
我放证书的位置是在/opt/etcd/ssl下,可是咱们须要放到secret里面,须要要转换成base64编码才能去存储,而这样执行也是由换行的,必须将它拼接成一个总体的字符串[root@k8s-master1 ~]# cat /opt/etcd/ssl/ca.pem |base64 -w 0
将对应的都添进去,将注释去掉
# etcd-key: null 将对应ssl下的证书转换成base64编码放进来,并去掉注释 # etcd-cert: null # etcd-ca: null
二、要读取secret落地到容器中位置,直接将注释去掉就能够了
etcd_ca: "/calico-secrets/etcd-ca" etcd_cert: "/calico-secrets/etcd-cert" etcd_key: "/calico-secrets/etcd-key"
三、链接etcd的字符串,这与k8s链接API的字符串是同样的
这个是在[root@k8s-master1 ~]# cat /opt/kubernetes/cfg/kube-apiserver.conf 这个目录下,由于每一个集群都是本身部署的,位置可能不同etcd_endpoints: "https://10.4.7.11:2379,https://10.4.7.12:2379,https://10.4.7.21:2379"
将这个证书放进放进calico配置中
2、根据实际网络规划修改Pod CIDR
这个位置在这个是默认的,须要改为本身的
- name: CALICO_IPV4POOL_CIDR value: "192.168.0.0/16"
能够在控制器配置的默认的也就是这个10.244.0.0.16这个地址
[root@k8s-master1 ~]# cat /opt/kubernetes/cfg/kube-controller-manager.conf --cluster-cidr=10.244.0.0/16 \ 在配置中改为这个 - name: CALICO_IPV4POOL_CIDR value: "10.244.0.0/16"
3、选择工做模式
IPIP # Enable IPIP - name: CALICO_IPV4POOL_IPIP value: "Always"
这个变量问你要不要开启IPIP,由于有两种模式,第一种就是IPIP,第二种就是BGP
其实它应用最多就是BGP,将这个Always改为Never,就是将它关闭的意思
如今就能够删除flannel网络[root@k8s-master1 k8s]# kubectl delete -f kube-flannel.yaml
而后删除一下flannel生成的虚拟网卡还有网桥cni,这个最好删除掉,由于咱们使用的子网和flannel的子网同样,也会出现冲突
[root@k8s-master1 calico]# ip route default via 10.4.7.1 dev eth0 proto static metric 100 10.4.7.0/24 dev eth0 proto kernel scope link src 10.4.7.11 metric 100 10.244.0.0/24 via 10.4.7.21 dev eth0 10.244.1.0/24 dev cni0 proto kernel scope link src 10.244.1.1 10.244.2.0/24 via 10.4.7.12 dev eth0 172.17.0.0/16 dev docker0 proto kernel scope link src 172.17.0.1
开始删除,这个每一个节点都要删除路由表和网桥,这是以前部署flannel留下的,也是避免和calico冲突
[root@k8s-node1 ~]# ip link delete cni0 [root@k8s-node1 ~]# ip link delete flannel.1 [root@k8s-node1 ~]# ip route delete 10.244.0.0/24 via 10.4.7.21 dev eth0 [root@k8s-node1 ~]# ip route delete 10.244.1.0/24 via 10.4.7.11 dev eth0
开始部署calico,这里会帮你启动两个角色,calico-node其实就是calico的client和felix,这个会在每一个节点都启动一个
calico-kube-controllers这个是calico主要在etcd中动态的获取一些网络规则,处理一些策略都是由这个控制器去完成的,
[root@k8s-master1 calico]# kubectl create -f calico.yaml [root@k8s-master1 calico]# kubectl get pod -o wide -n kube-system NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES calico-kube-controllers-f68c55884-q7bsl 1/1 Running 0 2m17s 10.4.7.21 k8s-node2 <none> <none> calico-node-g9tfw 1/1 Running 0 2m17s 10.4.7.21 k8s-node2 <none> <none> calico-node-tskkw 1/1 Running 0 2m17s 10.4.7.11 k8s-master1 <none> <none> calico-node-vldl8 1/1 Running 0 2m17s 10.4.7.12 k8s-node1 <none> <none>
目前为止去查看网络,会发现不到calico的路由表,由于目前的pod没有使用当前的calico的网络,须要重建才会应用到,因此这也是会受到一些影响的,这个须要提早作好准备
重建这些pod以后,网络就会根据calico的规则生成路由表,你会发现以前的pod,使用flannel部署的pod已经没法互通了,因此切换网络也是一个比较大的事情,须要注意安排时间去作这件事
[root@k8s-master1 ~]# ip route default via 10.4.7.1 dev eth0 proto static metric 100 10.4.7.0/24 dev eth0 proto kernel scope link src 10.4.7.11 metric 100 10.244.113.128 dev calibe9d0ccbf7b scope link blackhole 10.244.113.128/26 proto bird 10.244.203.64/26 via 10.4.7.21 dev eth0 proto bird 10.244.245.0/26 via 10.4.7.12 dev eth0 proto bird 172.17.0.0/16 dev docker0 proto kernel scope link src 172.17.0.1
四、Calico 管理工具
这里会用到calico的管理工具,用它管理一些calico的配置,好比切换成ipip模式,
下载工具:https://github.com/projectcalico/calicoctl/releases # wget -O /usr/local/bin/calicoctl https://github.com/projectcalico/calicoctl/releases/download/v3.9.1/calicoctl # chmod +x /usr/local/bin/calicoctl
安装好这个管理工具以后就能够查看当前节点BGP的节点状态
[root@k8s-master1 ~]# calicoctl node status Calico process is running. IPv4 BGP status +--------------+-------------------+-------+------------+-------------+ | PEER ADDRESS | PEER TYPE | STATE | SINCE | INFO | +--------------+-------------------+-------+------------+-------------+ | 10.4.7.12 | node-to-node mesh | up | 2019-12-27 | Established | | 10.4.7.21 | node-to-node mesh | up | 2019-12-27 | Established | +--------------+-------------------+-------+------------+-------------+ IPv6 BGP status No IPv6 peers found.
这个工具主要也是往etcd里去操做,主要再etcd去得到,这个表只不过是帮你从etcd中拿出来格式化输出
能够经过这个命令能够看出,进行长连接输出出来
[root@k8s-master1 ~]# netstat -anpt |grep bird tcp 0 0 0.0.0.0:179 0.0.0.0:* LISTEN 221854/bird tcp 0 0 10.4.7.11:179 10.4.7.12:28396 ESTABLISHED 221854/bird tcp 0 0 10.4.7.11:179 10.4.7.21:51460 ESTABLISHED 221854/bird
若是想使用calicoctl get node,就须要指定配置文件了,默认在/etc/calico/calicoctl.cfg下
主要修改etcd的路径,还有它链接的证书,它主要操做etcd
# mkdir /etc/calico # vim /etc/calico/calicoctl.cfg apiVersion: projectcalico.org/v3 kind: CalicoAPIConfig metadata: spec: datastoreType: "etcdv3" etcdEndpoints: "https://10.4.7.11:2379,https://10.4.7.12:2379,https://10.4.7.21:2379" etcdKeyFile: "/opt/etcd/ssl/server-key.pem" etcdCertFile: "/opt/etcd/ssl/server.pem" etcdCACertFile: "/opt/etcd/ssl/ca.pem"
这样的话就能使用calicocatl get node了,这样的话就是在etcd中去拿的数据了
# calicoctl get nodes NAME k8s-master k8s-node1 k8s-node2
查看 IPAM的IP地址池:
[root@k8s-master1 ~]# calicoctl get ippool -o wide NAME CIDR NAT IPIPMODE VXLANMODE DISABLED SELECTOR default-ipv4-ippool 10.244.0.0/16 true Never Never false all()
五、Calico BGP 原理剖析
看一下默认的BGP是怎么工做的?
这个也是跨节点之间的通讯,相比于flannel相似,其实这张图相比于flannel,经过一个路由器的图标,将flannel的cni,将flannel.1就相比于vxlan模式去掉了,因此会发现这里是没有网桥存在的,彻底就是经过路由来实现的,这个数据包也是先从veth的设备对的另外一口发出,到达宿主机上的cali开头的虚拟网卡上,到达这一头也就达到了宿主机上的网络协议栈,另外就是当建立一个pod时帮你先起一个infra containers的容器,而后调用calico的二进制帮你去配置容器的网络,而后会根据路由表决定这个数据包到底发送到哪里去,能够从ip route看到路由表信息,这里显示是目的cni分配的子网络和目的宿主机的网络,也就是当进行跨主机通讯的时候之间转发到下一跳地址走宿主机的eth0网卡出去,也就是一个直接的静态路由,这个下一跳就跟host-gw的形式同样了,这个和host-gw最大的区别calico使用的BGP路由的交换,而host-gw是使用的本身的路由交换,而BGP这个方案比较成熟,在大型网络中用的也比较多,因此要比flannel的方式好不少,而这些路由信息都是由BGP client传输过来,使用BGP协议传输而来的。
这个为何叫边界网关协议呢?
这个跟host-gw工做模式基本上是同样的,只不过BGP交换路由规则,BGP就成了一个边界路由器,主要是在每一个自治系统的最边界与其余自治系统传输规则,这个由来也是这么来的,而这些节点之间组成的BGP网络它是一个全网通的网络,这个网络就称为一个BGP Peer
能够看到启动文件也是在/opt/cni/bin,这个目录是yaml文件有两个镜像专门去写进去的
这是子网的相关配置信息[root@k8s-master1 ~]# cat /etc/cni/net.d/10-calico.conflist
Pod 1 访问 Pod 2大体流程以下:
数据包从容器1出到达Veth Pair另外一端(宿主机上,以cali前缀开头);
宿主机根据路由规则,将数据包转发给下一跳(网关);
到达Node2,根据路由规则将数据包转发给cali设备,从而到达容器2。
其中,这里最核心的“下一跳”路由规则,就是由 Calico 的 Felix 进程负责维护的。这些路由规则信息,则是经过 BGP Client 也就是 BIRD 组件,使用 BGP 协议传输而来的。
不难发现,Calico 项目实际上将集群里的全部节点,都看成是边界路由器来处理,它们一块儿组成了一个全连通的网络,互相之间经过 BGP 协议交换路由规则。这些节点,咱们称为 BGP Peer。
而host-gw和calico的惟一不同的地方就是当数据包下一跳到达node2节点的容器的时候发生变化了,而且出数据包也发生变化了,咱们知道它是从veth的设备对让容器里面的数据包到达宿主机上数据包,这个数据包到达node2以后,它又根据一个特殊的路由规则,这个会记录目的通讯地址的cni网络,而后经过cali的设备进去容器,这个就跟网线同样,数据包经过这个网线发到容器中,这也是一个二层的网络互通才能实现,若是二层不通的就可使用IPIP模式了,
calico没有网桥数据包是怎么出去的?
pod1的数据包从veth的设备对到到宿主机的一段eth0上,以前的数据包实际上是走的默认宿主机的网关将流量转发到calico的cali的设备上的,经过路由表信息下一跳地址到宿主机而后转发到对应的容器中
六、Route Reflector 模式(RR)(路由反射)
https://docs.projectcalico.org/master/networking/bgp
Calico 维护的网络在默认是(Node-to-Node Mesh)全互联模式,Calico集群中的节点之间都会相互创建链接,用于路由交换。可是随着集群规模的扩大,mesh模式将造成一个巨大服务网格,链接数成倍增长。
这时就须要使用 Route Reflector(路由器反射)模式解决这个问题。
肯定一个或多个Calico节点充当路由反射器,让其余节点从这个RR节点获取路由信息。
在BGP中能够经过calicoctl node status看到启动是node-to-node mesh网格的形式,这种形式是一个全互联的模式,默认的BGP在k8s的每一个节点担任了一个BGP的一个喇叭,一直吆喝着扩散到其余节点,随着集群节点的数量的增长,那么上百台节点就要构建上百台连接,就是全互联的方式,都要来回创建链接来保证网络的互通性,那么增长一个节点就要成倍的增长这种连接保证网络的互通性,这样的话就会使用大量的网络消耗,因此这时就须要使用Route reflector,也就是找几个大的节点,让他们去这个大的节点创建链接,也叫RR,也就是公司的员工没有微信群的时候,找每一个人沟通都很麻烦,那么建个群,里面的人都能收到,因此要找节点或着多个节点充当路由反射器,建议至少是2到3个,一个作备用,一个在维护的时候不影响其余的使用。
具体步骤以下:
一、关闭 node-to-node BGP网格
添加 default BGP配置,调整 nodeToNodeMeshEnabled和asNumber:
[root@k8s-master1 calico]# cat bgp.yaml apiVersion: projectcalico.org/v3 kind: BGPConfiguration metadata: name: default spec: logSeverityScreen: Info nodeToNodeMeshEnabled: false asNumber: 63400
直接应用一下,当咱们禁用node-to-node mesh的时候,网络立马就会断,因此断的话要提早作好影响的范围,也就是切换这个网络是须要断网的,使用node-to-node BGP这种也是建议100个节点如下,当超过100台节点必定要使用路由反射RR模式
[root@k8s-master1 calico]# calicoctl apply -f bgp.yaml Successfully applied 1 'BGPConfiguration' resource(s)
查看bgp网络配置状况,false为关闭
[root@k8s-master1 calico]# calicoctl get bgpconfig NAME LOGSEVERITY MESHENABLED ASNUMBER default Info false 63400
去查看pod的网络测试已经断开了,这里是由于咱们使用caclico的配置禁用了node-to-node mesh了
[root@k8s-master1 calico]# ping 10.244.245.2 PING 10.244.245.2 (10.244.245.2) 56(84) bytes of data.
ASN号能够经过获取 # calicoctl get nodes --output=wide
这里有个编号,ASN64300,一个编号就是一个自治系统
[root@k8s-master1 calico]# calicoctl get nodes --output=wide NAME ASN IPV4 IPV6 k8s-master1 (63400) 10.4.7.11/24 k8s-node1 (63400) 10.4.7.12/24 k8s-node2 (63400) 10.4.7.21/24
二、配置指定节点充当路由反射器
为方便让BGPPeer轻松选择节点,经过标签选择器匹配,也就是能够去调用k8s里面的标签进行关联,咱们能够给哪一个节点做为路由发射器打个标签
给路由器反射器节点打标签,我这将node1打上标签[root@k8s-master1 calico]# kubectl label node k8s-node1 route-reflector=true
查看node BJP的节点状态,由于禁用了网格,因此这里都关闭了,因此也就不通了。
[root@k8s-master1 calico]# calicoctl node status Calico process is running. IPv4 BGP status No IPv4 peers found. IPv6 BGP status No IPv6 peers found.
而后配置路由器反射器节点routeReflectorClusterID,增长一个集群节点的ID
下面的能够经过-o yaml输出出来
[root@k8s-master1 calico]# calicoctl get node k8s-node2 -o yaml > node.yaml apiVersion: projectcalico.org/v3 kind: Node metadata: annotations: projectcalico.org/kube-labels: '{"beta.kubernetes.io/arch":"amd64","beta.kubernetes.io/os":"linux","kubernetes.io/arch":"amd64","kubernetes.io/hostname":"k8s-node2","kubernetes.io/os":"linux"}' creationTimestamp: null labels: beta.kubernetes.io/arch: amd64 beta.kubernetes.io/os: linux kubernetes.io/arch: amd64 kubernetes.io/hostname: k8s-node2 kubernetes.io/os: linux name: k8s-node2 spec: bgp: ipv4Address: 10.4.7.12/24 routeReflectorClusterID: 244.0.0.1 # 集群ID orchRefs: - nodeName: k8s-node2 orchestrator: k8s
应用一下[root@k8s-master1 calico]# calicoctl apply -f node.yaml
如今,很容易使用标签选择器将路由反射器节点与其余非路由反射器节点配置为对等:如今也就是将其余的节点去链接这个k8s-node1打标签的路由发射器
[root@k8s-master1 calico]# cat bgp1.yaml apiVersion: projectcalico.org/v3 kind: BGPPeer metadata: name: peer-with-route-reflectors spec: nodeSelector: all() #因此的节点 peerSelector: route-reflector == 'true'
#就是带route-reflector的都去链接匹配这个,刚才咱们不是打上标签了嘛,因此须要咱们去链接这个路由反射器
查看节点的BGP规则与链接状态,这样的话就显示一个路由反射器的节点
[root@k8s-master1 calico]# calicoctl apply -f bgp1.yaml Successfully applied 1 'BGPPeer' resource(s) [root@k8s-master1 calico]# calicoctl get bgppeer NAME PEERIP NODE ASN peer-with-route-reflectors all() 0 [root@k8s-master1 calico]# calicoctl node status Calico process is running. IPv4 BGP status +--------------+---------------+-------+----------+-------------+ | PEER ADDRESS | PEER TYPE | STATE | SINCE | INFO | +--------------+---------------+-------+----------+-------------+ | 10.4.7.12 | node specific | up | 08:22:22 | Established | +--------------+---------------+-------+----------+-------------+ IPv6 BGP status No IPv6 peers found.
查看容器网络联通性
[root@k8s-master1 calico]# ping 10.244.203.80 PING 10.244.203.80 (10.244.203.80) 56(84) bytes of data. 64 bytes from 10.244.203.80: icmp_seq=1 ttl=63 time=1.71 ms
添加多个路由反射器
如今进行对路由反射器添加多个,100个节点之内建议2-3个路由反射器
1)进行对集群节点打标签
[root@k8s-master1 calico]# kubectl label node k8s-node2 route-reflector=true node/k8s-node2 labeled
2)对k8s-node2添加而后配置路由器反射器节点
[root@k8s-master1 calico]# calicoctl get node k8s-node2 -o yaml
3)查看节点状态
[root@k8s-master1 calico]# calicoctl node status Calico process is running. IPv4 BGP status +--------------+---------------+-------+----------+-------------+ | PEER ADDRESS | PEER TYPE | STATE | SINCE | INFO | +--------------+---------------+-------+----------+-------------+ | 10.4.7.12 | node specific | up | 08:22:22 | Established | | 10.4.7.21 | node specific | up | 08:44:44 | Established | +--------------+---------------+-------+----------+-------------+ IPv6 BGP status No IPv6 peers found.
4)测试网络连通性
[root@k8s-master1 calico]# ping 10.244.203.81 PING 10.244.203.81 (10.244.203.81) 56(84) bytes of data. 64 bytes from 10.244.203.81: icmp_seq=1 ttl=63 time=12.7 ms 64 bytes from 10.244.203.81: icmp_seq=2 ttl=63 time=1.40 ms
因此这是使用路由反射器来解决节点增多BGP带来的消耗
七、IPIP模式
ipip模式与flannel的vxlan模式相似,这个也是对数据包的一个封装
在前面提到过,Flannel host-gw 模式最主要的限制,就是要求集群宿主机之间是二层连通的。而这个限制对于 Calico 来讲,也一样存在,也是不能跨vlan的,主要局限就是数据包主要封装的是容器,源IP和目的IP,由于它们工做都是使用的二层,因此二层它不会考虑容器之间进行数据包转发的,但若是添加路由表,将目的的IP经过静态路由的方式也能实现,不一样vlan的数据的通讯,不过这种方式目前没有测试。
另外还有一个弊端,会发现calico的路由表比flannel的多一些,由于它里面还要加一些传入过来的到设备的路由表信息,就是每一个pod都加一个路由表,因此它的路由表的量也比flannel大很多。
修改成IPIP模式:
calicoctl get ippool -o yaml > ipip.yaml vi ipip.yaml apiVersion: projectcalico.org/v3 kind: IPPool metadata: name: default-ipv4-ippool spec: blockSize: 26 cidr: 10.244.0.0/16 ipipMode: Always natOutgoing: true calicoctl apply -f ipip.yaml
建立好以后查看详细信息,已经开启ippool
[root@k8s-master1 calico]# calicoctl get ippool -o wide NAME CIDR NAT IPIPMODE VXLANMODE DISABLED SELECTOR default-ipv4-ippool 10.244.0.0/16 true Always Never false all()
查看网络设备会增长一个tunl0,增长了一个隧道的网卡
tunl0@NONE: <NOARP,UP,LOWER_UP> mtu 1440 qdisc noqueue state UNKNOWN group default qlen 1000 link/ipip 0.0.0.0 brd 0.0.0.0 inet 10.244.113.131/32 brd 10.244.113.131 scope global tunl0 valid_lft forever preferred_lft forever
IPIP示意图:
那么有这么一个需求,有两个不一样vlan,由于须要突破2层,如今有两个vlan,这两个vlan它自己是三层可达的,三层可达就必需要借助路由器,也就是这两个节点,部署k8s集群多是两个vlan里面的,可是它们网络是通的,你也能够组建成一个集群,可是要使用calico的BGP,若是只让它们三层可达的话,与BJP也是不能够用的,由于BJP使用的二层,由于数据源IP和目的IP都是pod的IP,而这个路由器并不知道这个源IP和目的IP应该发给谁,由于这没有写这个路由表信息,若是写了,理论上来说节点不一样网段也是能够通讯的,那么不添加这个路由表的话,这个BGP是过不去的,那么这种状况下就得去启用ipip模式了,IPIP是linux内核的驱动程序,能够对数据包进行隧道,那么它看到两个不一样的网络vlan1和vlan2,启动ipip模式也有个前提,它是属于4层的,由于它是基于现有的以太网的网络将你原来包里的原始IP,也是进行一次封装,由于现有的网络已经通了,三层的路由现实不一样的vlan进行通讯,因此经过tunl0解包,这个tunl0相似于ipip模块,这个就跟vxlan的veth相似,因此这个模式跟vxlan的模式大体是同样的
Pod 1 访问 Pod 2大体流程以下:
数据包从容器1出到达Veth Pair另外一端(宿主机上,以cali前缀开头);
进入IP隧道设备(tunl0),由Linux内核IPIP驱动封装在宿主机网络的IP包中(新的IP包目的地之是原IP包的下一跳地址,即192.168.31.63),这样,就成了Node1 到Node2的数据包;
此时包的类型: 原始IP包: 源IP:10.244.1.10 目的IP:10.244.2.10 TCP: 源IP: 192.168.31.62 目的iP:192.168.32.63
那么这个IPIP自己和vxlan同样,工做在三层的,由于它用如今的以太网进行传输的,如今物理机传输的,路由器这个方面达到另外一个vlan2,这个网络之间确定是能够访问的,那么IPIP之间就能和三层的路由到目的另外一个vlan中
数据包通过路由器三层转发到Node2;
Node2收到数据包后,网络协议栈会使用IPIP驱动进行解包,从中拿到原始IP包;
而后根据路由规则,根据路由规则将数据包转发给cali设备,从而到达容器2。
路由表:
[root@k8s-node1 ~]# ip route default via 10.4.7.1 dev eth0 proto static metric 100 10.4.7.0/24 dev eth0 proto kernel scope link src 10.4.7.12 metric 100 10.244.113.128/26 via 10.4.7.11 dev tunl0 proto bird onlink 10.244.203.64/26 via 10.4.7.21 dev tunl0 proto bird onlink blackhole 10.244.245.0/26 proto bird 10.244.245.1 dev calie1d6cd79d22 scope link 10.244.245.2 dev calid6a1fb2294e scope link 172.17.0.0/16 dev docker0 proto kernel scope link src 172.17.0.1
不难看到,当 Calico 使用 IPIP 模式的时候,集群的网络性能会由于额外的封包和解包工做而降低。因此建议你将全部宿主机节点放在一个子网里,避免使用 IPIP。
八、CNI 网络方案优缺点及最终选择
先考虑几个问题:
须要细粒度网络访问控制?这个flannel是不支持的,calico支持,因此作多租户网络方面的控制ACL,那么要选择calico
追求网络性能?这两个方案无疑是flannel和calico的路由方案是最高的,也就是flannel的host-gw和calico的BGP。
服务器以前是否能够跑BGP协议?不少的公有云是不支持跑BGP协议的,那么使用calico的BGP模式天然是不行的。
集群规模多大?若是规模不大,100节点如下维护起来比较方面之间可使用flannel就能够
是否有维护能力?calico的路由表不少,并且走BGP协议,一旦出现问题排查起来也比较困难,上百台的,路由表去排查也是很麻烦,这个具体的需求也是跟本身饿的状况而定。
小话题:办公网络与k8s网络如何互通
如今架构也是微服务也比较流行,测试环境是在k8s集群中,开发人员是在办公网络中,网络显然是不一样的,微服务是使用pod去通讯的,办公网络访问pod天然是不一样的,那么就须要将这个网络打通。
好比开发开发了一个微服务,不想启动整套,注册中心,服务发现了等等,可是它只想跑一个模块,直接调用测试环境中注册中心数据库了直接去用,那么就要考虑将这个网络打通了
好比一块是办公网络,而后开发人家使用的开发电脑,另外一块是k8s集群跑着不少的微服务,那么pod的ip是10.244.0.0/16这个网段,那么service是10.0.0.10/24,宿主机是172.17.0.0/24,那么办公网络是192.168.1.0/24
---------------------------------- | 「pc」 「pc」 | | 「pc」 「pc」 | 办公网络:192.168.1.0/24 | 办公 | ---------------------------------- ---------------------------------- | 「pod」 「pod」 | pod IP 10.244.0.0/16 | 「pod」 「pod」 | service IP 10.0.0.10/24 | k8s集群 | 宿主机 IP 172.17.0.0/24 ----------------------------------
那么办公网络去访问pod的IP确定是不通的,除非作了路由,即便办公网络和k8s的宿主机网络能通,可是也须要作一些特殊的转发策略,如今解决的问题是办公网络能够访问pod的IP,访问service的IP,也就是让k8s内部的网络暴露出来,办公网络就像之间访问虚拟机同样去访问,因此去解决这个问题,分为两种状况。
第一种状况,k8s集群测试环境在办公网络的子网里面,那么这个实现就比较简单了,只须要在子网中上层的路由器添加一个规则就好了,ip route add,目的IP为10.244.0.0/16,到达的下一跳via为其中的k8s节点,好比k8s-node1 dev 接口A
ip route add 10.244.0.0/16 via <k8s-node1> dev A
添加这么一个规则,办公网络就能直接访问k8s的节点,直接就能访问pod IP,也就是下一跳地址以后,就能直接访问都podIP了。
第二种状况,k8s集群与办公网络在不一样VLAN中,不一样机房
前提是k8s集群与办公网络是互通的,三层可达
有两种方案1)在路由器上添加路由表,10.244.0.0/16 <k8s-node1>
2) 采用BGP,若是三层的路由支持BGP协议的话,直接就可让路由器BGP与路由反射器BGP创建链接,这样的话路由器上的BGP就能获取到了k8s上的路由表信息了,而后通过下一跳来转发到目的的node的pod中。
总结:只要是不一样vlan,必须是三层可达,能在上层的路由器上,访问集群的网段,pod网段仍是service网段,必定要告知它,帮它转发到下一跳是谁,下一跳若是是目的的节点,那就直接转发数据包。
4.5 网络策略
一、为何须要网络隔离?
CNI插件插件解决了不一样Node节点Pod互通问题,从而造成一个扁平化网络,默认状况下,Kubernetes 网络容许全部 Pod 到 Pod 的流量,也就是在k8s网络中都是相互ping通,都是能够访问传输数据包的,在一些场景中,咱们不但愿Pod之间默认相互访问,例如:
应用程序间的访问控制。例如微服务A容许访问微服务B,微服务C不能访问微服务A
开发环境命名空间不能访问测试环境命名空间Pod
当Pod暴露到外部时,须要作Pod白名单
多租户网络环境隔离
好比这个命名空间与其余的命名空间进行互通,将你的pod暴露在外面了暴露在办公网络中了,为了方便,可是提升一些安全性,那么谁能访问,谁不能访问,直接作白名单也能够,而后微服务部署的也比较多,也但愿作一些隔离,那么也可使用网络隔离,那么就可使用network policy进行pod网络的隔离。
既然说到了网络的限制也就是ACP访问控制,天然是有两个方向,一个是入口方向,一个是出口方向
一个用户去访问虚拟机,客户端访问这是入方向,对于客户端来讲这是出方向,虚拟机访问外网天然是出方向,作ACL一个是入方向,一个是出方向,咱们针对pod作谁能访问pod,pod能访问谁。
因此,咱们须要使用network policy对Pod网络进行隔离。支持对Pod级别和Namespace级别网络访问控制。
Pod网络入口方向隔离
基于Pod级网络隔离:只容许特定对象访问Pod(使用标签订义),容许白名单上的IP地址或者IP段访问Pod
基于Namespace级网络隔离:多个命名空间,A和B命名空间Pod彻底隔离。
Pod网络出口方向隔离
拒绝某个Namespace上全部Pod访问外部
基于目的IP的网络隔离:只容许Pod访问白名单上的IP地址或者IP段
基于目标端口的网络隔离:只容许Pod访问白名单上的端口
二、网络策略概述
一个NetworkPolicy例子:
apiVersion: networking.k8s.io/v1 kind: NetworkPolicy metadata: name: test-network-policy namespace: default spec: podSelector: matchLabels: role: db policyTypes: - Ingress - Egress ingress: - from: - ipBlock: cidr: 172.17.0.0/16 except: - 172.17.1.0/24 - namespaceSelector: matchLabels: project: myproject - podSelector: matchLabels: role: frontend ports: - protocol: TCP port: 6379 egress: - to: - ipBlock: cidr: 10.0.0.0/24 ports: - protocol: TCP port: 5978
配置解析:
podSelector:用于选择策略应用到的Pod组,也就是对哪一个pod进行网络隔离
policyTypes:其能够包括任一Ingress,Egress或二者。该policyTypes字段指示给定的策略用于Pod的入站流量、仍是出站流量,或者二者都应用。若是未指定任何值,则默认值为Ingress,若是网络策略有出口规则,则设置egress,这个也就是入口方向和出口方向,限制入口或者限制出口方向
Ingress:from是能够访问的白名单,能够来自于IP段、命名空间、Pod标签等,ports是能够访问的端口。入口的策略是什么,入口的策略是这么限制的,
Egress:这个Pod组能够访问外部的IP段和端口。出的策略是这么限制的,pod出去是这么限制的,是访问百度的ip,仍是访问其余的ip,是哪一个ip段仍是哪一个命名空间。
在172.17.0.0/16这个大子网里面除了这个172.17.1.0/24这个不能访问,其余的都能访问,命名空间也是能够哪一个能够访问,哪一个不能够访问。
cidr: 172.17.0.0/16
except:
根据上面的yaml的规则来讲,结构是这样的,对pod命名空间的携带标签role:db的pod进行网络隔离,只有172.17.0.0/16子网下除了172.17.1.0/24其余的均可以访问我,
kubectl create deployment nginx --image=nginx kubectl run client1 --generator=run-pod/v1 --image=busybox --command -- sleep 36000 kubectl run client2 --generator=run-pod/v1 --image=busybox --command -- sleep 36000 kubectl get pods --show-labels
需求:将default命名空间携带run=nginx标签的Pod隔离,只容许default命名空间携带run=client1标签的Pod访问80端口
apiVersion: networking.k8s.io/v1 kind: NetworkPolicy metadata: name: test-network-policy namespace: default spec: podSelector: matchLabels: app: nginx policyTypes: - Ingress ingress: - from: - namespaceSelector: matchLabels: project: default - podSelector: matchLabels: run: client1 ports: - protocol: TCP port: 80
隔离策略配置:
Pod对象:default命名空间携带run=nginx标签的Pod
容许访问端口:80
容许访问对象:default命名空间携带run=client1标签的Pod
拒绝访问对象:除容许访问对象外的全部对象
测试查看如今client的网络是不能通讯的,而这个组件calico支持,像其余的flannel组件是不支持的
命名空间隔离
需求:default命名空间下全部pod能够互相访问,但不能访问其余命名空间Pod,其余命名空间也不能访问default命名空间Pod。
[root@k8s-master1 ~]# kubectl run client3 --generator=run-pod/v1 --image=busybox -n kube-system --command -- sleep 36000
default的pod都能互通,kube-system的pod不能与default的pod互通,default也不能访问kube-system的pod,也就是本身隔离到了本身网络命名空间下了
如今咱们实现一下这个需求,建立好以后由于咱们没有限制网络的隔离,因此默认状况下不一样命名空间的网络也是互通的
建立一个default下的pod名字为nginx的pod
[root@k8s-master1 ~]# kubectl get pod -o wide NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES nginx-86c57db685-cv627 1/1 Running 0 5m57s 10.244.113.132 k8s-master1 <none> <none [root@k8s-master1 ~]# kubectl exec -it client3 -n kube-system /bin/sh / # ping 10.244.113.132 PING 10.244.113.132 (10.244.113.132): 56 data bytes 64 bytes from 10.244.113.132: seq=0 ttl=62 time=3.105 ms 64 bytes from 10.244.113.132: seq=1 ttl=62 time=13.029 ms
建立网络隔离yaml,实现default和kube-system下的pod不能互通
[root@k8s-master1 ~]# cat ns.yaml apiVersion: networking.k8s.io/v1 kind: NetworkPolicy metadata: name: deny-from-other-namespaces namespace: default spec: podSelector: {} policyTypes: - Ingress ingress: - from: - podSelector: {} [root@k8s-master1 ~]# kubectl apply -f ns.yaml networkpolicy.networking.k8s.io/deny-from-other-namespaces created
测试以后如今已经没法访问default下的pod了,这也就实现的网络隔离,相反反过来也不通
[root@k8s-master1 ~]# kubectl exec -it client3 -n kube-system /bin/sh / # ping 10.244.113.132 PING 10.244.113.132 (10.244.113.132): 56 data bytes
podSelector: {}:default命名空间下全部Podfrom.podSelector: {} : 若是未配置具体的规则,默认不容许