什麼是service?
Service是將執行在一組 Pods 上的應用程式公開為網路服務的抽象方法。
簡單來說K8s提供了service物件來訪問pod。我們在《k8s網路模型與叢集通訊》中也說過k8s叢集中的每一個Pod(最小排程單位)都有自己的IP地址,都有IP了訪問起來還不簡單?
其實不然,一是k8s中pod不是永續性的,摧毀重建將獲得新的IP,客戶端通過變更IP來訪問顯然不合理。二是需要多個副本間的負載均衡。所以此時Service就冒出來了。
那麼今天我們就來學習一下service,看看它是如何工作的。
Service與endpoints、pod
當我們通過API建立/修改service物件時,endpoints控制器的informer機制 Listen到service物件,然後根據service的配置的選擇器建立一個endpoints
物件,此物件將pod的IP、容器埠做記錄並儲存到etcd,這樣service只要看一下自己名下的endpoints就可以知道所對應pod資訊了。
且看下圖:
我們在例項來看一下,先稀疏平常建立一個Deployment
#deployment.yml
apiVersion: apps/v1
kind: Deployment
metadata:
name: deployment-demo
spec:
selector:
matchLabels:
app: nginx
replicas: 3
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: mirrorgooglecontainers/serve_hostname
ports:
- containerPort: 9376
protocol: TCP
serve_hostname
是k8s官方提供的debug映象,一個返回hostname的web server。這樣我們建立出了標籤為app=nginx
的三個pod,當我們訪問pod的9376時會返回hostname。
接著是service清單,我們在service中指定了選擇器為app=nginx
#service.yaml
apiVersion: v1
kind: Service
metadata:
name: service-demo
spec:
selector:
app: nginx
ports:
- name: default
protocol: TCP
#service port
port: 80
#container port
targetPort: 9376
這樣我們獲得不變的CLUSTER-IP 10.96.148.206的service
如果pod啟動成功,則自動建立和service同名的endpoints記錄下了三個pod的資料
service中選擇器未指定標籤時endpoints需要手動建立對映到service的網路地址如下:
apiVersion: v1
kind: Endpoints
metadata:
name: service
subsets:
- addresses:
- ip: 10.96.148.206
ports:
- port: 9376
此時當我們不斷訪問service的CLUSTER-IP時:
# curl 10.96.148.206:80
deployment-demo-7d94cbb55f-8mmxb
# curl 10.96.148.206:80
deployment-demo-7d94cbb55f-674ns
# curl 10.96.148.206:80
deployment-demo-7d94cbb55f-lfrm8
# curl 10.96.148.206:80
deployment-demo-7d94cbb55f-8mmxb
可以看到此時請求已被路由到後端pod,返回hostname,並且負載均衡方式是Round Robin
即輪詢模式。
通過上面介紹我們好像摸到了Service
其中的門道,接下來是流量到底如何通過service進入pod的?
Service與kube-proxy
涉及到流量當然是kube-proxy登場了!
kube-proxy 是叢集中每個節點上執行的網路代理, 實現 Kubernetes 服務(Service) 概念的一部分。用於處理單個主機子網劃分並向外部世界公開服務。它跨叢集中的各種隔離網路將請求轉發到正確的 pod/容器。
kube-proxy 維護節點上的網路規則。這些網路規則允許從叢集內部或外部的網路會話與 Pod 進行網路通訊。
如下圖所示:
kube-proxy 通過 Informer知道了Service、endpoints物件的建立,然後把service身上的CLUSTER-IP 和埠已經端點資訊拿出來,建立iptable NAT規則做轉發或通過ipvs模組建立VS伺服器,這樣經過CLUSTER-IP的流量都被轉發到後端pod。
iptables模式
我們先檢視nat表的OUTPUT鏈,存在kube-proxy建立的KUBE-SERVICE鏈
iptables -nvL OUTPUT -t nat
在KUBE-SERVICES鏈中有一條目的地為10.96.148.206即CLUSTER-IP地址跳轉到KUBE-SVC-EJUV4ZBKPDWOZNF4
iptables -nvL KUBE-SERVICES -t nat |grep service-demo
接著是檢視這條鏈,以1/3的概率跳轉到其中一條
iptables -nvL KUBE-SVC-EJUV4ZBKPDWOZNF4 -t nat
最後KUBE-SEP-BTFJGISFGMEBGVUF鏈終於找到了DNAT規則
iptables -nvL KUBE-SEP-BTFJGISFGMEBGVUF -t nat
即將請求通過DNAT傳送到地址100.101.184.61:9376
也就是我們其中一個Pod。
IPVS模式
與iptalbes模式相比,IPVS模式工作在核心態,在同步代理規則時具有更好的效能,同時提高網路吞吐量為大型叢集提供了更好的可擴充套件性。
IPVS 模式在工作時,當我們建立了前面的 Service 之後,kube-proxy 首先會在宿主機上建立一個虛擬網路卡kube-ipvs0,併為它分配 Service VIP 作為 IP 地址,如圖
接著kube-proxy通過Linux的IPVS模組為這個 IP 地址新增三個 IPVS 虛擬主機,並設定這三個虛擬主機之間使用輪詢模式 來作為負載均衡策略。
通過ipvsadm檢視
ipvsadm -ln |grep -C 5 10.96.148.206
可以看到虛擬server的IP即是Pod的地址,這樣流量即向了目的地Pod。
以上我們先認識了Service這個API物件,接著講到了service與endpoints和pod的關聯,然後是service與kube-proxy的關係,以及kube-proxy的兩種模式如何通過service的IP建立iptables、IPVS規則將流量轉發到Pod。
希望小作文對你有些許幫助,如果內容有誤請指正。
您可以隨意轉載、修改、釋出本文,無需經過本人同意。【公號:容器雲實踐】