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

背景
深入瞭解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)運行在不同節點時,我們可以通過域名的方式對他們分別訪問。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章