1. 程式人生 > >深入了解kubernetes 中Service服務的內在邏輯

深入了解kubernetes 中Service服務的內在邏輯

端口 支持 選擇 地址轉換 由於 相同 多個service 默認 stat

背景
深入了解Service服務的內在邏輯

Service
k8s中service是一個面向微服務架構的設計,它從k8s本身解決了容器集群的負載均衡,並開放式地支持了用戶所需要的各種負載均衡方案和使用場景。

通常,一個service被創建後會在集群中創建相應的endpoints,隨後,controller-manager中的endpointsController會去檢查並向該endpoints填入關於這個service,符合下述所有條件的後端端點(即pod):

相同的namespace;
pod的labels能滿足service.Spec.selector(除非service.Spec.selector為空,這種情況下不會自動創建endpoints);

如果service開放了port,且是以字符串名字的形式(targetPort=[字符串]),則相應的pod的某個container中必須有配置同名的port;
當endpoints被更新後,kube-proxy會感知,並根據更新後的endpoints,在宿主機上做轉發規則的配置,kube-proxy目前支持iptables、ipvs兩種負載均衡方式,默認是iptables。

DNS
絕大部分使用k8s的人都會使用kubedns做服務發現和service domain的解析。kubedns會建立長連接即時檢查service的變化,一旦發現有service被創建,會根據service的類型,在數據庫中構建service domain 到指定的CNAME或IP(cluster-IP)的映射,作為對該service domain 的dns解析結果。

service 的類型
ClusterIP
最普遍的service類型,也是默認類型。ClusterIP類型的service創建時,k8s會通過etcd從可分配的IP池中分配一個IP,該IP全局唯一,且不可修改。所有訪問該IP的請求,都會被iptables轉發到後端的endpoints中。

該類型下,service的cluster-ip會作為kube-dns的解析結果,返回給客戶端。基本上這是私有雲中服務內部常用的方案,但是也只能集群內服務互相訪問。

NodePort
通過node的IP進行地址轉換實現服務的可訪問性,外部訪問集群中任意一個node:port即可以訪問服務。
舉個例子:一個單副本的deployment,其pod運行在node2上,通過nodePort方式開放服務,外部client訪問node1_ip:port即可訪問到容器服務。這個過程中原理是:

client訪問node1_ip:port
node1對數據包做SNAT,將來源地址改成node1_ip
node1對數據包做DNAT,將目的地址改成node2_ip
node2收到數據包,根據port查找自身的端口,這個端口在service創建時就會與自身運行的port做映射,所以這裏數據包會被轉發到真實的容器中
數據響應由容器發給node1,node1再返回給client
這種方案下,node上會產生端口的占用,所以要確保端口可用性。
另外,通過指定service.spec.externalTrafficPolicy=Local可以設置node上kube-proxy不轉發到其他node,參照上面的例子,由於node1沒有響應的pod在運行,所以node1上會直接drop數據包。

使用這種方案的原因,不外乎是:外部無法訪問容器服務。

LoadBalancer
需要外部支持(GCP and Azure),用戶訪問service.spec.external-ip,該IP對應到一個外部負載均衡的vip,外部服務對這個vip的請求,會被loadbalancer通過健康檢查和轉發,發送到一個運行著該服務pod的node上,並同樣通過nodePort裏的端口映射,發送給容器。

上述是幾種比較普遍的service,隨著社區發展,又出現了一些新的類型,可以做更靈活的適配。

ExternalName
用戶可以指定一個任意的名字,作為該service被解析的CNAME,這種類型的servcie不用指定clusterIP,因此kube-proxy不會管理這類service,這類service需要使用1.7版本以上的kubedns。比如用戶想創建一個service,但要讓所有容器訪問該service的請求都引導到用戶自己定義的外部域名服務,就可以通過這個方式實現。可以說這個是最自由的一個方式:你希望服務被如何解析,服務就能被如何解析。你甚至可以給多個service配置同一個externalName。

headless service
headless service是一個特殊的ClusterIP類service,這種service創建時不指定clusterIP(--cluster-ip=None),因為這點,kube-proxy不會管這種service,於是node上不會有相關的iptables規則。

當headless service有配置selector時,其對應的所有後端節點,會被記錄到dns中,在訪問service domain時kube-dns會將所有endpoints返回,選擇哪個進行訪問則是系統自己決定;

當selector設置為空時,headless service會去尋找相同namespace下與自己同名的pod作為endpoints。這一點被應用到statefulset中,當一個三副本的statefulset(mysql1,mysql2,mysql3)運行在不同節點時,我們可以通過域名的方式對他們分別訪問。

深入了解kubernetes 中Service服務的內在邏輯