K8s提供兩種內建的雲端負載均衡機制(cloud load balacing), 一個是工作在傳輸層的TCP負載均衡器:Service
; 另一個是HTTP(S)負載均衡器:Ingress
。這邊會分為兩天來介紹!
在K8s中負責處理實際邏輯的是Pod, 每個Pod都有自已的Pod IP, 透過Deployment部署擴縮容或是任何原因造成Pod變動時會異動Pod IP, 此時client端如果不重新綁定IP將無法有效利用新的Pod, Service就是為了解決這個狀況而設計的; Service是K8s核心之一, 主要用於為同一組Pod集合提供統一的代理接口, 讓所有請求Pod服務的連線可以透過Service分別打到不同的Pod上面, 做到LoadBalance, Service透過LabelSelector來關聯Pod, 一個label對應一組Pod集合, client端對Service 發起請求時, Service 會調度請求到關聯的Pod集合中的其中一個Pod, client端看起來只有對Service操作請求, Service 代理Pod並調度流量的部分以及Pod上面的處理細節並不會暴露出去。實際觀察, Service 本身是不提供服務的, 提供服務的是Pod, Service需要和Pod Controller 合作才能完成請求的任務。
ClusterIP
Service 的 IP也稱為 ClusterIP
, 是Sevice創建時由K8s cluster 配置在專用區段的虛擬IP, 且創建後就不會再改變。ClusterIP能夠被同一cluster的Pod訪問。
Port Proxy
Service 端口用於接收client請求再轉發至Pod上面對應的端口, 這種代理機制稱為Port Proxy
或四層代理, 它運作在TCP/IP protocal 的傳輸層。
Service
-Endpoints
-Pod
Service與Pod之間透過LabelSelector以鬆耦合的方式建立關係, Service可以比Pod早建立, 也可以由不同的client建立, 每個Node上的kube-proxy
會透過API Server watch 隨時監控Service或LabelSelector匹配的Pod對象是否有變動, 例如: IP 改變, Pod 增加或減少, 然後再將這些異動隨時更新Endpoints。
在Service創建時, 預設也會建立一份Endpoints, Endpoints是由LabelSelector匹配出來的Pod的IP和端口組成的列表, 實際上Service是透過Endpoints連結到Pod, 它是Service和Pod的中間層。
可以把Service視為Node上的iptables或ipvs規則的定義, client端的請求送到Service之後會透過Endpoints 中的IP和端口轉發到對應的Pod上面, 在工作節點上的kube-proxy
對隨時透過API Server watch Service跟Pod的異動, 再把異動反映到iptables或ipvs規則。
kube-proxy
將請求代理到對應端口的代理模式有三種:
userspace
:
由於這個模式中用戶的請求會在內核空間和用戶空間之間來回轉發, 使得效率變得很差, 所以已經不建議使用了!
userspace
指的是Linux用戶空間的意思, userspace
代理的方式是透過 kube-proxy 監控API Server上的Service和Endpoints的變化後, 再去調整Service的定義。 它會隨機打開一個Service本地端口, 把接到的請求代理轉接到後面的Pod,而Service預設是使用輪詢(round-robin)的方式調度Pod, 這類的Service會創建iptables規則來捕捉ClusterIP和Port的流量。
iptables
:
iptables
代理方式是透過 kube-proxy 監控API Server上的Service和Endpoints的變化後, 再去調整Service的定義(和userspace一樣)。
它在每一個Service創建時都會把iptables資訊送到kube-proxy做為Service的定義, 每個Service上都有一份iptables , 所以可以直接捕捉ClusterIP 和Port的流量, 再把請求代理轉接到後面的Pod, 採用的調度方式是隨機(random), 由於不需要在內核空間和用戶空間之間轉發, 所以效率比userspace
好很多, 但缺點是當後面的Pod無回應的時候他不會重新定向, userspace
可以重新定向!
ipvs
:
ipvs
代理方式是透過 kube-proxy 監控API Server上的Service和Endpoints的異動後, 再根據異動調用netlink接口創建ipvs規則並確保與API Server的異動保持同步。和iptables
不同的地方在於調度流量是透過ipvs
, ipvs
建構在netlink的鉤子函數上, 他使用hash table做為底層數據結構, 並支援多種調度算法,且是在內核空間運作的, 所以她轉發流量的速度快, 規則同步也快。
Service spec 常用字段為selector
和 ports
, 分別定義使用的LabelSelector和要暴露的Port。
創建YAML
apiVersion: v1
kind: Service
metadata:
name: svc-demo
spec:
selector:
app: svc-demo
ports:
- protocol: TCP
port: 80
targetPort: 80
Service 會透過標籤選擇器關聯到標籤為 svc-demo 的對象,並會自動創建名稱爲 svc-demo 的Endpoint。
查看Service
-> % kubectl get svc svc-demo
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
svc-demo ClusterIP 10.103.226.230 <none> 80/TCP 10m
K8s Service 有四種類型:ClusterIP
,NodePort
,LoadBalancer
,ExternalName
。
透過內部IP地址暴露服務, 此IP只有內部可以使用, 無法被cluster外部的client訪問。
在工作節點的IP地址上選擇一個端口來將外部請求轉發到目標Service的clusterIP 和Port, 所以這個類型的Service可以向ClusterIP一樣收到內部Pod訪問, 也可以收到外部Client :的請求。
LoadBalancer 類型的Service會指向k8s cluster外部的一個實際存在的負載均衡設備, 該設備會透過工作節點上面的NodePort向K8s內部發送請求
NodePort類型的Service雖然能在cluster外部被訪問, 但是client還是需要事先知道至少一個Node的IP地址, 且選定的Node故障時, client端也需要同時更換對應的IP地址, 或者NodeIP可能是雲端讓使用的私有IP, 這類型的IP無法在網路上訪問到, 因為上述兩種原因, 所以雲端商的IaaS環境通常都會提供一個LBaaS(Load Balancer as a Service)服務, 允許用戶可以動態的在自己的網路中創建一個負載均衡的設備。
在IaaS環境手動指定IP地址時, 可以使用spec.loadBalancerIP
指定負載均衡設備的IP地址, 也可使用spec.oadBalancerSourceRanges
指定負載均衡設備允許的client IP地址範圍。
ExternalName
不是K8s cluster提供的服務, 而是把cluster外部的服務以DNS CNAME的方式映射到cluster中, 讓cluster內部的Pod能夠訪問到外部Service的實現方式。
ExternalName
不需要使用LabelSelector關聯任何Pod對象, 但必須使用spec.externalName
屬性定義一個CNAME
用於返回外部真正提供服務的主機別名, 然後再透過CNAME
的紀錄取得相關主機的IP地址
ExternalName
實現於 DNS級別, client直接接入外部的服務而不需要服務代理, 所以也不需要配置ClusterIP, 此類型的服務也稱為Headless Service
client有需求需要直接訪問Service後面的額Pod時, 就需要暴露每個Pod的IP地址,Client不會使用Service的ClusterIP, 這種類型的Service稱為Headless Service
。
如果要為這類型的Service配置IP地址, 要看有沒有定義LabelSelector:
有標籤選擇器:
Endpoints Controller 會在API中建立Endpoints紀錄, 再把ClusterDNS服務中的紀錄解析到Pod的IP地址上
沒有標籤選擇器:
Endpoints Controller 不會在API中建立Endpoints紀錄, ClusterDNS的配置會分成兩種:
創建YAML
-> % cat headless-svc.yaml
apiVersion: v1
kind: Service
metadata:
name: headless-demo
spec:
clusterIP: None
selector:
app: deploy-demo
ports:
- port: 80
targetPort: 80
name: httpport
查看結果
-> % kubectl get service
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
headless-demo ClusterIP None <none> 80/TCP 3s
查看Endpoints 狀況
Private IP
Private IP
Public IP
明天來讀Ingress。