Kube-proxy的功能
我們知道POD的IP是動態分配的而且經常會變,所以為了可以通過一個不太容易變化的IP訪問POD就會使用一個叫做service的東西,通過標簽選擇器和POD進行關聯。
Service提供常用的類型有:
- ClusterIP,也是默認方式。Service會分配一個集群內部的固定虛擬IP,實現集群內通過該IP來對POD進行訪問。這個又有兩類,上面說到的最普通的Service,ClusterIP還有一種是Headless Service,這種形式不會分配IP也不會通過kube-proxy做反向代理或者負載均衡,而是通過DNS提供穩定的網絡ID來訪問,DNS會將headless service的后端直接解析為POD的IP列表,這種主要是共StatefulSet類型使用。
- NodePort,這種類型的Service是除了使用ClusterIP的功能外還會映射一個宿主機隨機端口到service上,這樣集群外部可以通過宿主機IP+隨機端口來訪問。
- LoadBalancer:和nodePort類似,不過除了使用ClusterIP和NodePort之外還會向使用的公有雲申請一個負載均衡器,從而實現集群外部通過LB來訪問服務
- ExternalName:是Service的一種特例,此模式主要面對運行在集群外部的服務,通過它可以將外部服務映射到k8s集群,具備k8s內服務的一些特性,來為集群內部提供服務。
我們要說一下ClusterIP這個東西,這是通過yaml安裝的一個coredns插件,它就的配置清單中就定義了service。
這個servic ip地址段是在部署API SERVER時API SERVER服務啟動配置文件中定義的地址段。而且在Flannel中都沒有這個地址段。相比之下POD的IP其實是實實在在配置在容器中的,最終要的是集群中任何節點上都沒有關於這個網段的路由信息,那么集群內部是如何通過這個完全虛擬的IP來訪問的呢?這就要說到kube-proxy了
你看在集群的任何機器上都可以PING通這個地址。我們來看看這個svc的詳情
這個10.254.0.2 serviceIP關聯了Endpoints(這里是2個endpoints因為POD開放了TCP和UDP的53號端口,所以是2個)。那么現在就有了一個大致的認識就是你訪問10.254.0.2就是訪問172.30.23.2,而這個172的IP就是POD的真實IP,這個IP段是在Flannel上配置過的。下面再來看一張圖:
在IPVS規則中定義了訪問10.254.0.2就會轉發到172.30.23.2,而172.30.23.2就是POD的IP。
所以通過上面我們就知道它其實是通過IPVS規則來轉發的根本不是通過路由來實現的。可是你想過沒有這個規則是誰生成的呢?其實就是kube-proxy來生成的,而且這樣的規則會同步到集群其他機器上,哪怕這個POD沒有運行在自己的機器上也要有這樣的規則,只有這樣才能保證集群任何一台主機都可以通過這個serviceIP來訪問到POD,當面臨跨主機的時候才會用到路由規則,由Flannel的隧道來進行轉發到真實POD所在主機,然后由該主機的kube-proxy來轉發到具體的POD上。
這時候我們就明白了kube-proxy的大致作用,當service有了IP、端口以及POD的IP和端口對應關系以及宿主機隨機端口到service的映射,就可以完成對內、外請求的轉發,而轉發就是,本地轉發還是用IPVS規則,而遠程則用了路由信息。
集群中每個NODE都運行一個kube-proxy進程,這個就是service的載體。它負責建立和刪除包括更新IPVS規則、通知API SERVER自己的更新,或者從API SERVER哪里獲取其他kube-proxy的IPVS規則變化來更新自己的。我們說了多次IPVS,其實kube-proxy支持3中模式
Userspace模式
userspace,這種模式時最早的,不過已經不推薦使用了,效率低,因為需要在內核空間和用戶空間轉換。
Iptables模式
這是默認方案,在內核中通過iptables的NAT實現本地轉發和負載均衡。在大規模試用下存在性能問題。
Ipvs模式
可選方案,如果內核支持且用戶指定那么kube-proxy將使用這種模式。在早期版本的K8S中不支持。
如果POD不在本機怎么辦
本機是srv01而這個coredns是運行在srv02上,你從srv01訪問會先進行ipvs找到目標POD的IP地址,發現不在本機就通過路由,訪問的網段172.30.23.0走flannel.1這個網絡接口,這個接口其實和其他主機的flannel.1通過物理鏈路做了隧道,所以就可以通。那么10.254.0.2是如何與這個172.30.23.2這個IP建立關系的呢?其實就是service,看下圖