什么是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。
希望小作文對你有些許幫助,如果內容有誤請指正。
您可以隨意轉載、修改、發布本文,無需經過本人同意。【公號:容器雲實踐】