Kubernetes網絡組件之Calico策略實踐(BGP、RR、IPIP)

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,舉個例子:
Kubernetes網絡組件之Calico策略實踐(BGP、RR、IPIP)
在這個圖中,有兩個自治系統(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實現
Kubernetes網絡組件之Calico策略實踐(BGP、RR、IPIP)
這是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 原理剖析
Kubernetes網絡組件之Calico策略實踐(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的設備上的,經過路由表信息下一跳地址到宿主機而後轉發到對應的容器中

六、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示意圖:
Kubernetes網絡組件之Calico策略實踐(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。
八、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:

  • 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端口。
      三、入站、出站網絡流量訪問控制案例
      如今作對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
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: {} : 若是未配置具體的規則,默認不容許

相關文章
相關標籤/搜索