네트워크 구성 요소의 사례는 Kubernetes의 옥양목 전략 (BGP, RR, IPIP)

옥양목의 전략 실제로는 Kubernetes 네트워크 솔루션

케이스 : K8S는 플란넬 네트워크 정책 배포 시나리오입니다 클러스터로하기 전에, 그래서 전략이 플란넬 것 옥양목 네트워크 정책으로 전환

Calico是一个纯三层的数据中心网络方案,Calico支持广泛的平台,包括Kubernetes、OpenStack等。
Calico 在每一个计算节点利用 Linux Kernel 实现了一个高效的虚拟路由器( vRouter) 来负责数据转发,而每个 vRouter 通过 BGP 协议负责把自己上运行的 workload 的路由信息向整个 Calico 网络内传播。
此外,Calico  项目还实现了 Kubernetes 网络策略,提供ACL功能。

옥양목은, 가상 라우터를 구현하기 위해 이러한 라우팅 테이블을 유지하기 위해, 다음 가상 라우터, BGP에 따라 서로 라우팅 정보를 교환 한 후 각 노드로, 이러한 데이터는이를 통해 클러스터 노드 사이에 인도 할 커널을 사용한다 교환 및 캘는 ACL 기능 네트워크 정책을 제공 K8S을 달성

1, BGP 개요

实际上,Calico项目提供的网络解决方案,与Flannel的host-gw模式几乎一样。也就是说,Calico也是基于路由表实现容器数据包转发,但不同于Flannel使用flanneld进程来维护路由信息的做法,而Calico项目使用BGP协议来自动维护整个集群的路由信息。
BGP英文全称是Border Gateway Protocol,即边界网关协议,它是一种自治系统间的动态路由发现协议,与其他 BGP 系统交换网络可达信息。 

다음은이 실제로 호스트-GW이며, 플란넬 패턴이 거의 동일 네트워크를 통해 데이터를 교환 BGP를 사용, 단지 그들을 유지하기 위해 자신의 플란넬, 데이터의 교환을 달성하기 위해 BGP를 사용하고, BGP는 대규모 네트워크입니다 동적 프로토콜 또한하므로 BGP 경로 교환기로 사용 캘 성능, 데이터 패킷, 데이터 테이블을 채용하고, 클러스터 크기가 일정량 도달뿐만 아니라, 좋은 성능 좋은 있도록

말할 것이다 일반 파이프 서버 룸 다음 BGP 다중 회선 우리 가족 룸. "이 말은 경계 경로 프로토콜이며, 경로는 정적 외출 그 라우팅 테이블 정보를 추가 할 수있는 인공이다, 라우팅 테이블은 동적 및 정적으로 나누어 져, 앞으로 라우팅 테이블을 기반으로 경로를 선택하는 것입니다, 동적 상호 에너지이다 인식이 때 네트워크 장비, VLAN을 많은 기업 간 통신의 많은이 매뉴얼은 라우팅 테이블 정보를 구성 할 경우, 분명히 인공 볼륨이 큰 것을 달성하기 위해, 그래서 손이 닿지 특정 크기는 의지는 기본적으로 동적를 사용하는 경우 라우팅 프로토콜, BGP는 OSPF는 RIP 라우팅 테이블을 인터넷에서 대규모 네트워크 인 네트워크의 동적 토폴로지를 인식하는 학습으로, 그 중 하나입니다, 그들은 각 라우팅 장치에서 배울 배울 동적 통해 , BGP 프로토콜 중 하나입니다
위해, 예를 들어, 당신은 더 명확하게 BGP를 이해 할 수 있도록 :
네트워크 구성 요소의 사례는 Kubernetes의 옥양목 전략 (BGP, RR, IPIP)
이 그림에서 (로 축약 자동 시스템)이 자율 시스템이 있습니다 : 1과 2로.

自治系统,可以想成公司的网络和其他公司的网络,两个就可以理解为两个自治系统,每个自治系统是由自己交换机,路由器来组成的,而这些交换机路由器单独去运行,它不依赖于别的公司,别的公司也不依赖你公司的网络,都可以去独立的单独的去运行,一个大学,一个企业都可以说成是一个自治系统,但是这个自治系统也没什么交际,你家的网络和邻居家的网络也没什么来往,但是如果他们想通信,他们本身就不在一个网络里面,你家的网络和它家的网络上层的网络出口的交换机必须相互的学习到,而且我们用到的电脑都是私网IP,邻居家也用的是私网IP,甚至这些IP都是冲突的,如果想实现两家的网络内网能够通信,首先保证用到的ip地址不能冲突,还要保证上层的路由出口之间能够相互学到自己的路由表,比如你家的路由器能够学习到当前路由表的信息.

BGP는 단순히 두 자치 시스템은 두 개의 상호 될 수 있습니다 연결 의사 소통이 BGP의 역할은, 두 사람은 지금 같은 네트워크 아닌 두 개의 AS가, 두 회사는이 개 학교처럼 될 수있다 172.17 및 192.168 AS가 AS 1 2 다음 통신 이동에서 수행하고자하는 경우, 어떻게 이동?

192.168.1.10 이동 제 1 스위치는 라우터에 라우터가 통신하고자하는 경우, 그들은 두 회사는 라우터가 통신이 설정되어 긴 후와 통신 할 수, 연결할 수 있어야 A의 입에서 갔다, 패킷은 노드 B에 도착 다음은 2 할 방법을 알고 라우터에 전달된다? 우리는 라우팅 테이블, 목적지 주소 1, 172.17.1.20이 세그먼트에 라우터 포워딩이 필요 그래서, 볼 수있는 로컬 라우팅 테이블, 그것은 로컬 라우팅 테이블이 있음을 발견 라우터 2에 전달, 포트 B에서 것 없다 그 자체가 아니라 다음 홉, 때문에 자신의 관할 구역에서, 같은 목적지 주소로 배웠지 만 어떻게 자신의 관할의 많은, 그것은 때문에 밖으로, 그리고 확실히,이 라우터 위의에 라우터 2 네트워크 노하우를 전달합니다 1.20, 이것은 상기 스위치에 연결하고 그 다음이, 목적지 어드레스에 대응하는 바닥에 상기 스위치로부터 송신 단지 포트 안에서, IP 후없는 다음 홉을 가지고, 그 내부의 인터페이스 인터페이스는 A로 포워딩 노드가 통신 할 수있다

이러한 환경에서, 다음 BGP는 어떤 역할을 출시?
这个路由表也可以手动的进行添加,然后指向下一跳就是这个route的路由器,它也会转发过来,只要他们的网络是通的,如果节点很多,vlan很多,这样的话添加路由表就很大,而且在互联网中还会有别的路由器,可能还会用到别的网络进行去通信,所以这里的BGP就是可以相互的去学习到相互的路由表信息,那么route1为什么有route2的信息,其实就是在route学到的,那么AS 2要访问AS 2的节点,首先它的路由表也能学到目标地址,那么就能之间转发到路由器中,然后转发到目的的服务器上

서버 1, 우리 K8S 컨테이너처럼 2 노드 K8S로서 그대로 호스트 GW 모드 우리 플란넬로 라우팅 프로토콜이 동적는 다소 유사하다
현재 노드가이 옥양목 이제 게이트웨이로 사용되며, 전 BGP의 도입, 그것은 그들이 BGP 통해 라우팅 정보를 상호 교환을 실현하고, 각 라우팅 테이블이 로컬 노드에 송신 데몬, 및 캘로 유지 플란넬 가상 라우터 만들어진 각 노드이며 BGP는 데이터 교환을 위해 사용하지만, 각 노드의 라우팅 테이블에 기록됩니다.

在互联网中,一个自治系统(AS)是一个有权自主地决定在本系统中应采用何种路由协议的小型单位。这个网络单位可以是一个简单的网络也可以是一个由一个或多个普通的网络管理员来控制的网络群体,它是一个单独的可管理的网络单元(例如一所大学,一个企业或者一个公司个体)。一个自治系统有时也被称为是一个路由选择域(routing domain)。一个自治系统将会分配一个全局的唯一的16位号码,有时我们把这个号码叫做自治系统号(ASN)。
在正常情况下,自治系统之间不会有任何来往。如果两个自治系统里的主机,要通过 IP 地址直接进行通信,我们就必须使用路由器把这两个自治系统连接起来。BGP协议就是让他们互联的一种方式。

2, 옥양목 BGP 실현
네트워크 구성 요소의 사례는 Kubernetes의 옥양목 전략 (BGP, RR, IPIP)
칼리 인터페이스가 될 것이다 1,10.0.0.2 용기는 용기 (2)이다 도면 공식 10.0.0.1이고, 이것이 패브릭 아키텍처도 두 장치 veth 장비는 여기 이 가상 라우터로 호스트입니다, 다음 가상 라우터를 가지고 두 가지 구성 요소, 하나는 펠릭스, 기계 테이블의 정보를 기록 할 책임이 주된 이유, 라우팅 테이블 전에 기록 된 정보를 클라이언트입니다을 포함 BGP 프로토콜입니다 데몬은 플란넬에 기록, 옥양목 쓰기에 펠릭스를 사용하는 것입니다.
또한 또한 BGP 클라이언트의 인이 가장 중요한에서, 또한 네트워크 정책 설정 및 구성 상태 옥양목을 유지하기 위해 etcd 사용 옥양목, 각 노드 daemonset 방법에 배포하여도 모든이며,이 프로토콜을 제공하기 위해 주로 노드는 그렇게하는 BGP 클라이언트가 그들 BGP 사이의 연결을 설정이 다음 그래서 전체 시스템 컨테이너의 각 노드는 전체 토폴로지 규칙을 형성하는 것으로, 자신의 정보를 교환하기 위해 BGP 라우팅 테이블의 연결을 그들은 BGP의 규칙을 사라, 플란넬 규칙이 할 간단한의 TCP입니다.

이해 BGP 후, 옥양목 프로젝트의 아키텍처는 매우 쉽게 이해하는 것입니다, 옥양목 주로 세 부분으로 구성 :
펠릭스 : 배포에 DaemonSet 방법, 각 노드의 노드에서 실행 호스트 ACL 규칙 규정에 의한 도로의 유지 보수에 대한 책임이며, .
BGP 클라이언트 (새가) : 라우팅 정보가 클러스터 옥양목 커널 유통망에 기록을 위해 펠릭스 책임이있다.
Etcd : 옥양목의 분산 키 - 값 저장, 보존 정책과 네트워크 구성 상태입니다.
calicoctl : 당신은 간단한 명령 줄 인터페이스에서 고급 전략과 네트워크를 구현할 수 있습니다.
3, 옥양목는 배치
git clone [email protected]: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
一、所以修改etcd一共修改3个位置
1、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

2、要读取secret落地到容器中位置,直接将注释去掉就可以了

  etcd_ca: "/calico-secrets/etcd-ca"
  etcd_cert: "/calico-secrets/etcd-cert"
  etcd_key: "/calico-secrets/etcd-key"

3、连接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配置中

二、根据实际网络规划修改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"

三、选择工作模式

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 

4、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() 

5、Calico BGP 原理剖析
네트워크 구성 요소의 사례는 Kubernetes의 옥양목 전략 (BGP, RR, IPIP)
看一下默认的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的设备上的,通过路由表信息下一跳地址到宿主机然后转发到对应的容器中

6、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个,一个做备用,一个在维护的时候不影响其他的使用。

具体步骤如下:
1、关闭 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 

2、配置指定节点充当路由反射器
为方便让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带来的消耗
7、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示意图:
네트워크 구성 요소의 사례는 Kubernetes의 옥양목 전략 (BGP, RR, 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。
8、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 网络策略
1、为什么需要网络隔离?
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访问白名单上的端口
2、网络策略概述
一个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:

  • 172.17.1.0/24
    pod还能定义你可以访问我哪个端口,这是出口策略的定义,就是出去可以访问谁,访问哪个ip段的端口
    ports:
    • protocol: TCP
      port: 6379

根据上面的yaml的规则来说,结构是这样的,对pod命名空间的携带标签role:db的pod进行网络隔离,只有172.17.0.0/16子网下除了172.17.1.0/24其他的都可以访问我,

  • namespaceSelector:
    matchLabels:
    project: myproject
    • podSelector:
      matchLabels:
      role: frontend
      这个命名空间可以访问我,携带role:frontend的也可以访问我,这些只能访问我6379的端口,本身自己只能访问10.0.0.0/24IP段的ip的5978端口。
      3、入站、出站网络流量访问控制案例
      现在做对pod的访问限制
      Pod访问限制
      准备测试环境,一个web pod,两个client pod
      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
포드의 기본값은 포드되지 포드 상호 운용성 기본의 KUBE 시스템, 통신 할 수있는 포드 KUBE 시스템에 액세스 할 수있는 기본은 자신의 아래로 네트워크 네임 스페이스에 격리
와 지금 우리는이 수요는, 이후에 생성 된 것을 실현 우리는 네트워크의 분리를 제한하지 않기 때문에 기본 네임 스페이스로 네트워크는 다른 연동 그래서
의 nginx에 포드의 이름으로 기본을 만들 포드

[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 만들기 통신 할 수있는 기본 및 KUBE 시스템에서 포드를 구현

[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

테스트가 포드 접근 할 수없는 기본 지금 후, 네트워크는이 격리를 달성 할 것이 아니라 차례로 통과하지 않습니다

[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 : {} : 기본 네임 스페이스의 모든 포드
from.podSelector : {} : 특정 규칙이 구성되지 않은 경우, 그것은 허용하지 않습니다

추천

출처blog.51cto.com/14143894/2463392