k8s 服務代理和發現

今天主要學習一下 k8s 的Service,之前的文章中有介紹過 k8s 內服務訪問的方式,但是自己也沒有特別的去了解具體的實現方式,今天就專門再來學習下Service這個非常重要的資源。今天主要是學習官方的文檔,文檔地址:Service。另外我還是覺得中文文檔翻譯之後不太好理解,所以最好我覺得還是看英文文檔。
k8s 中的Service是一個抽象的概念,它定義了一組邏輯的Pod以及訪問這些Pod的策略(有時這種模式被稱作微服務)。直白點講Service本身並不提供所謂的服務,它只是告訴你怎麼訪問你想要訪問的服務。通常情況下Service是通過Selector來定位到目標Pod的,當然你也可以通過創建相應的Endpoints定位到具體的服務。

1、虛擬 IP 和服務代理

在 k8s 集羣中的每個節點上都會運行着一個kube-proxy,由kube-proxy負責爲非ExternalName類型的Service實現一種虛擬 IP。
之所以 k8s 選擇代理模式將入站流量轉發到後端,而不是通過配置多個 A 值(ipv6 爲 AAAA)0 的DNS記錄,然後依賴輪詢名稱解析。主要有幾點原因:
1、一直以來DNS都不遵守數據的 TTL(Time to live),即使在數據過期後依然會進行緩存;
2、對於某些應用只進行一次DNS查找,但是結果卻會永久緩存;
3、即使應用和庫進行了適當的重新解析,DNS 記錄上的較低的 TTL 值低或零值可能會給DNS帶來高負載,從而使管理變得困難。

1、用戶空間代理模式

這種模式下kube-proxy會觀察 k8s 控制節點對Service 對象和 Endpoints對象的添加和刪除操作。對於每一個Servicekube-proxy都會在本地的節點上打開一個隨機端口,任何連接到這個 “代理端口” 的請求,都會被轉發到該Service對應的後端Pods中的某一個上面。具體使用哪一個Pod,是kube-proxy基於該Service的設置項SessionAffinity來確定的。
最後,kube-proxy會配置iptables規則,捕獲到達該ServiceclusterIP(是虛擬 IP)和 port的請求,並重定向到代理端口,然後由代理端口再代理請求到後端Pod
用戶空間模式下的kube-proxy默認通過輪轉算法選擇後端。
用戶空間模式見下圖:

圖 - 1.png

2、iptables 代理模式

和用戶空間代理模式,這種模式下kube-proxy也會觀察 k8s 控制節點對Service對象和Endpoints對象的添加和刪除操作。不同的是對每個Servicekube-proxy會通過配置iptables規則,從而捕獲到達該ServiceclusterIPport的請求,然後將請求重定向到該Service後端集合中的某個上面。對於每個Endpoints對象,kube-proxy也會配置 iptables規則,這個規則會選擇一個後端Pod
iptables模式下kube-proxy默認會隨機選擇一個後端。
使用iptables處理流量具有較低的系統開銷,因爲流量由 Linux netfilter 處理,而無需在用戶空間和內核空間之間切換,而且這種方法也可能更可靠。
如果kube-proxyiptables模式下運行,kube-proxy隨機選擇的Pod沒有響應,那麼此次連接失敗。這點與用戶空間模式不同。在用戶空間模式情況下,kube-proxy如果檢測到與第一個Pod的連接失敗,那麼它會自動使用其他後端Pod進行重試。
我們可以使用Pod readiness probes 驗證後端Pod是否就緒以便iptables模式下的kube-proxy僅看到狀態正常且就緒的後端。readiness probes在滾動發佈的時候也會用到。
iptables代理模式如下圖:

圖 - 2.png

3、IPVS 代理模式

ipvs模式下kube-proxy觀察 k8s 的ServicesEndpoints,調用netlink接口相應地創建IPVS規則,並定期將IPVS規則與 k8s 的ServicesEndpoints同步。該控制循環可確保IPVS狀態與所需狀態匹配。訪問Service時,IPVS將流量定向到後端Pod之一。
IPVS代理模式基於類似於iptables模式的netfilter鉤子函數, 但是使用哈希表作爲基礎數據結構,並且在內核空間中工作。這意味着與iptables模式下的kube-proxy相比,IPVS模式下的kube-proxy重定向通信的延遲要短,並且在同步代理規則時具有更好的性能。與其他代理模式相比IPVS模式還支持更高的網絡流量吞吐量。
IPVS提供了更多選項來平衡到達後端Pod的流量。這些是:
rr:輪替(Round-Robin)
lc:最少鏈接(Least Connection),即打開鏈接數量最少者優先
dh:目標地址哈希(Destination Hashing)
sh:源地址哈希(Source Hashing)
sed:最短預期延遲(Shortest Expected Delay)
nq:從不排隊(Never Queue)

說明:
要想kube-proxy使用IPVS模式,必須在啓動kube-proxy之前保證IPVS在節點上可用。因爲當kube-proxyIPVS代理模式啓動時,它將驗證IPVS內核模塊是否可用, 如果未檢測到IPVS內核模塊,則kube-proxy將會使用iptables代理模式運行。

IPVS代理模式如下圖:

圖 - 3.png


在這些代理模式中,在客戶端不瞭解 k8s 或ServicePod的任何信息的情況下,請求到Serviceip:port的流量最終被代理到具體的後端。
如果要確保每次都將來自特定客戶端的連接傳遞到同一Pod, 則可以通過將service.spec.sessionAffinity設置爲ClientIP(默認值是None),來基於客戶端的 IP 地址選擇會話關聯。你還可以通過適當設置 service.spec.sessionAffinityConfig.clientIP.timeoutSeconds來設置最大會話停留時間(默認值爲 10800 秒,即 3 小時)。

2、服務發現

k8s 支持兩種基本的服務發現模式——環境變量和DNS

1、環境變量

Pod運行在Node上,kubelet會爲每個存活的Service添加一組環境變量。它同時支持 Docker links compatible 變量 (見 makeLinkVariables) 和簡單的 {SVCNAME}_SERVICE_HOST{SVCNAME}_SERVICE_PORT 變量。這裏Service的名稱需大寫,橫線被轉換成下劃線。

舉個例子,一個名稱爲 redis-masterService暴露了TCP端口6379, 同時給它分配了ClusterIP地址 10.0.0.11,這個Service生成了如下環境變量:

1REDIS_MASTER_SERVICE_HOST=10.0.0.11
2REDIS_MASTER_SERVICE_PORT=6379
3REDIS_MASTER_PORT=tcp://10.0.0.11:6379
4REDIS_MASTER_PORT_6379_TCP=tcp://10.0.0.11:6379
5REDIS_MASTER_PORT_6379_TCP_PROTO=tcp
6REDIS_MASTER_PORT_6379_TCP_PORT=6379
7REDIS_MASTER_PORT_6379_TCP_ADDR=10.0.0.11

但是這個環境變量只針對同Namespace下的Service,比如我在namespace A下的Pod的環境變量中是查看到namespace B下的Service的相關信息的。

2、DNS

通常來講我們的 k8s 集羣都會安裝相關的網絡插件,比如Calico、Flannel等來設置 DNS 服務。
支持集羣的DNS服務器(例如CoreDNS)會觀察 k8s API 中的新Service,併爲每個服務創建一組DNS記錄。如果在整個集羣中都啓用了DNS,則所有Pod都應該能夠通過其DNS名稱自動解析服務。
例如,如果你在命名空間 my-ns中有一個名爲my-service的服務,則 k8s 控制面板和DNS服務共同爲my-service.my-ns創建DNS記錄。my-ns命名空間中的Pod能夠通過名稱my-service來找到服務(這裏可以直接通過環境變量獲取my-serviceclusterIpport等),當然也可以通過my-service.my-ns來定位到具體的cluserIp
其他命名空間中的Pod必須將名稱限定爲my-service.my-ns。這個名稱將解析爲服務的clusterIP
k8s 還支持命名端口的DNS SRV記錄。如果my-service.my-ns服務具有名爲http的端口,且協議設置爲 TCP,則可以對_http._tcp.my-service.my-ns執行DNS SRV查詢,以發現該服務http的端口號以及IP地址。
k8s DNS服務器是唯一的一種能夠訪問ExternalName類型的Service的方式。更多關於ExternalName信息可以查看 DNS Pod 和 Service。

本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源https://mp.weixin.qq.com/s/oJxoB3cORf0Swi6vTYEdvg