想象一下,如果您日常使用的研發測試Kubernetes叢集,能夠有以下效果:
- 在辦公網路下直接訪問Pod IP
- 在辦公網路下直接訪問Service Cluster IP
- 在辦公網路下直接訪問叢集內部域名,類似 service.namespace.svc.cluster.local
會不會很方便,很優雅?
筆者近期就給內部的一個新叢集做過類似的調整,特此分享一些心得。
PS: 這裡的 直接訪問/直連 指的是不借助Ingress/hostnetwork:true/NodePort等常規方式,直接訪問k8s內部IP or DNS,起到 網路拉平 的效果。
先決條件 - 三層路由方案
辦公網段跟Kubernetes叢集大機率是不同的網段,所以要想打通最自然的想法是依賴路由。相應的,Kubernetes 跨主機網路方案,我們最好也選擇三層路由方案或者Host-GW,而非Overlay,不然資料包在封包解包過程中可能會失去路由方向。
我們的叢集選用的是Calico,且關閉了 IPIP 模式。具體的IPPool配置如下:
-> calicoctl get IPPool -o yaml
apiVersion: projectcalico.org/v3
items:
- apiVersion: projectcalico.org/v3
kind: IPPool
metadata:
name: default-pool
spec:
blockSize: 24
cidr: 10.233.64.0/18
# 關閉IPIP模式
ipipMode: Never
natOutgoing: true
nodeSelector: all()
vxlanMode: Never
kind: IPPoolList
Calico RR(Route Reflectors)or Full-Mesh 模式?
網上的很多類似教程,上來都會引導大家先把叢集改為RR模式,其實這不是必須的。大家可以思考下,RR模式解決的問題是什麼?是為了防止所有節點間都做BGP連線交換,浪費資源。但如果你的叢集很小, 且已經是按Full Mesh模式部署了,到也沒必要非得改為RR模式。Full Mesh下所有的節點都是類似RR節點的效果,所以如果我們想選擇作為 BGPPeer交換的節點,選擇任意節點就行。 比如,筆者的叢集就選擇了Ingress所在的節點,作為BGPPeer。
~ calicoctl get BGPPeer -o yaml
apiVersion: projectcalico.org/v3
items:
- apiVersion: projectcalico.org/v3
kind: BGPPeer
metadata:
name: peer-switch
spec:
# 交換機配置
asNumber: 65200
peerIP: 10.200.20.254
# 這個label是Ingress節點特有的
nodeSelector: node-role.kubernetes.io/ingress == 'ingress'
kind: BGPPeerList
從叢集外部訪問Pod IP vs 從叢集內部訪問?
這個問題很關鍵,如果我們想從外部直接訪問到叢集內部的Pod IP,那麼首先需要搞清楚叢集內的節點是如何暢通訪問的。
以下面的節點為例,我們來看它的路由資訊:
~ ip r
# 預設路由
default via 10.200.20.21 dev bond0 onlink
# 宿主機資料包路由
10.200.20.0/24 dev bond0 proto kernel scope link src 10.200.20.105
# 黑洞,防止成環
blackhole 10.233.98.0/24 proto bird
# 目的地址是10.233.98.3的資料包,走cali9832424c93e網路卡
10.233.98.3 dev cali9832424c93e scope link
# 目的地址是10.233.98.4的資料包,走cali4f5c6d27f17網路卡
10.233.98.4 dev cali4f5c6d27f17 scope link
# 目的地址是10.233.98.8的資料包,走cali8f10abc672f網路卡
10.233.98.8 dev cali8f10abc672f scope link
# 目的地址是10.233.110.0/24網段的資料包,從bond0網路卡出到下一跳10.200.20.107上
10.233.110.0/24 via 10.200.20.107 dev bond0 proto bird
# 目的地址是10.233.112.0/24網段的資料包,從bond0網路卡出到下一跳10.200.20.106上
10.233.112.0/24 via 10.200.20.106 dev bond0 proto bird
# 目的地址是10.233.115.0/24網段的資料包,從bond0網路卡出到下一跳10.200.20.108上
10.233.115.0/24 via 10.200.20.108 dev bond0 proto bird
相信看了筆者的註釋,大家應該很容易瞭解到以下資訊:
- 這臺宿主機IP是10.200.20.105,叢集內其他的宿主機還有10.200.20.106, 10.200.20.107, 10.200.20.108等
- 主機10.200.20.105上的Pod IP段是10.233.98.0/24, 10.200.20.106上是10.233.112.0/24,10.200.20.107上是10.233.110.0/24
- 目的地址是10.233.98.3的資料包走cali9832424c93e網路卡,目的地址10.233.98.4的資料包走cali4f5c6d27f17網路卡等
而這些資訊實際解答了,容器資料包的 出和入 這個關鍵問題:
- 比如想訪問Pod IP為10.233.110.7的容器,宿主機自然知道下一跳是10.200.20.107上
- 比如接收到了目的地址是10.233.98.8的資料包,宿主機自然也知道要把這個包交給cali8f10abc672f網路卡。而這個網路卡是veth pair裝置的一端,另一端必然在目標Pod裡
那這些路由資訊是哪裡來的呢?自然是Calico藉助BGP的能力實現的。我們進一步想,如果外部節點也有這些資訊,是不是也就自然知道了Pod IP在哪裡了? 答案確實如此,其實總結基於Calico的網路打平方案,核心原理就是 透過BGP能力,將叢集路由資訊廣播給外部。
而在具體的配置上,就比較簡單了,只需要在兩端配置好BGP Peer即可。
-
先是叢集這一側,前面筆者已給出:
~ calicoctl get BGPPeer -o yaml apiVersion: projectcalico.org/v3 items: - apiVersion: projectcalico.org/v3 kind: BGPPeer metadata: name: peer-switch spec: # 交換機配置 asNumber: 65200 peerIP: 10.200.20.254 # 這個label就是Ingress節點特有的 nodeSelector: node-role.kubernetes.io/ingress == 'ingress' kind: BGPPeerList
-
再就是外部,一般是交換機,使用類似下面的命令:
[SwitchC] bgp 64513 # 這是k8s叢集的ASN [SwitchC-bgp] peer 10.200.20.107 as-number 64513 [SwitchC-bgp] peer 10.200.20.108 as-number 64513
PS: 具體的交換機操作方式可以參考各品牌交換機官方文件
到這裡,基本上我們已經打通了外部直接訪問Pod IP的能力。當然,如果您的辦公網路到交換機這一側還有多個閘道器,您還需要在這些閘道器上設定合適的路由才行。
為什麼 Service Cluster IP 還不能訪問?
也許這時候您會發現,可以直連Pod IP,但 Cluster IP不可以,這是為什麼呢?原來,預設情況Calico並沒有廣播Service IP,您可以在交換機這一側透過檢視交換過來的IP段來確認這一點。
PS: 您是否注意到,k8s主機節點上也沒有service的ip路由,但為啥在叢集內部訪問service沒有問題呢?
解決方案也簡單,只要開啟相關的設定即可, 類似如下:
~ calicoctl get bgpconfig default -o yaml
apiVersion: projectcalico.org/v3
kind: BGPConfiguration
metadata:
name: default
spec:
asNumber: 64513
listenPort: 179
logSeverityScreen: Info
nodeToNodeMeshEnabled: true
# 這就是需要廣播的service cluster IP 段
serviceClusterIPs:
- cidr: 10.233.0.0/18
打通內網DNS,直接訪問Service域名
直連IP雖然方便,但有時若想記住某服務的具體IP卻不是那麼容易。所以,我們將K8s內部的DNS域名也暴漏出來了,類似下面:
<service>.<namespaces>.svc.cluster.local
而這塊的設定也相對簡單,一般企業都有內網DNS,只需要新增相應解析到K8s內部DNS Server即可。
總結
其實若想打造一個好用的研發測試叢集,有很多的細節需要處理,筆者後續也會繼續分享類似的經驗,希望對大家有用。