Kubernetes(K8s)Service提供了一種機制,使得集群內的應用程序可以從集群外部進行訪問。通過使用不同類型的Service(如NodePort、LoadBalancer和Ingress),Kubernetes實現了不同級別的集群外訪問。NodePort為每個節點分配了一個靜態端口,使得外部流量可以通過節點訪問服務。LoadBalancer通過云服務商提供的負載均衡器將流量分配到集群中的服務。Ingress則提供了更高級的路由功能,允許基于域名和路徑將流量轉發到不同的服務。K8s Service為集群外部用戶提供了可靠且靈活的訪問方式,使得應用程序可以無縫地與外部系統進行交互。
NodePort類型Service如何定義?

nodePort->servicePort->podPort
apiVersion: v1
kind: Service
metadata:
name: service-nodeport
namespace: dev
spec:
selector:
app: nginx-pod
type: NodePort # service類型
ports:
- port: 80
nodePort: 30002 # 指定綁定的node的端口(默認的取值范圍是:30000-32767), 如果不指定,會默認分配
targetPort: 80
nodeport和clusterip的service端口展示對比
nodeport:
[root@master k8sYamlForCSDN]# kubectl get svc service-nodeport -n dev
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
service-nodeport NodePort 10.99.95.186 <none> 80:30002/TCP 11s
clusterIp:
[root@master k8sYamlForCSDN]# kubectl get svc -n dev -o wide
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE SELECTOR
service-clusterip ClusterIP 10.97.97.97 <none> 80/TCP 17s app=nginx-pod
NodePort類型Service實現原理?
NodePort 模式也就非常容易理解了。顯然,kube-proxy 要做的,就是在每臺宿主機上生成這樣一條 iptables 規則
apiVersion: v1
kind: Service
metadata:
name: my-nginx
labels:
run: my-nginx
spec:
type: NodePort
ports:
- nodePort: 8080
targetPort: 80
protocol: TCP
name: http
selector:
run: my-nginx
Iptables規則:nodePort(8080)->service
- 控制面:
- 添加Iptables規則:讓nodeport轉給svc
- -A KUBE-NODEPORTS -p tcp -m comment --comment "default/my-nginx: nodePort" -m tcp --dport 8080 -j KUBE-SVC-67RL4FN6JRUPOJYM
- 表示:訪問nodeport80端口,則跳轉給service規則:KUBE-SVC-67RL4FN6JRUPOJYM
- 所以接下來的流程,就跟 ClusterIP 模式完全一樣了
- 添加Iptables規則:NodePort 方式下,Kubernetes 會在 IP 包離開宿主機發往目的 Pod 時,對這個 IP 包做一次 SNAT 操作
- -A KUBE-POSTROUTING -m comment --comment "kubernetes service traffic requiring SNAT" -m mark --mark 0x4000/0x4000 -j MASQUERADE
- 只對service轉發的做SNAT, IP 包是否有一個“0x4000”的“標志”,認為是service轉發的
- IP 包的源地址替換成了這臺宿主機上的 CNI 網橋地址,或者宿主機本身的 IP 地址(如果 CNI 網橋不存在的話)

- 數據面:
- 請求進入node節點機器,被Iptable攔截,如果是訪問指定nodeport端口,則轉給service
- service如果將請求轉給其他node上的pod,會對該請求包做SNAT
NodePort類型Service,如何拿到client真實源IP?
- 問題:
- node2暴露nodeport,請求轉發到node1上的pod時候,會做SNAT,源IP改成node2的ip

- 但有些場景,我們的應用需要拿到client真實來源IP,如何做?
- 解決方案:
- 將 Service 的 spec.externalTrafficPolicy 字段設置為 local(默認值是cluster)
- 原理:
- ocal表示service只把請求轉給當前節點的pod(即不存在SNAT),這樣就可以拿到client真實來源IP
- 缺點:
- 轉到node2上沒有pod,則直接報錯

LoadBalance類型Service如何定義?

---
kind: Service
apiVersion: v1
metadata:
name: example-service
spec:
ports:
- port: 8765
targetPort: 9376
selector:
app: example
type: LoadBalancer
在公有云提供的 Kubernetes 服務里,都使用了一個叫作 CloudProvider 的轉接層,來跟公有云本身的 API 進行對接
LoadBalance類型Service實現原理?
Loadbalancer 的實現方面各個廠家的方案不盡一致,從大類來做下對比:
- 控制面實現:

- 實現方式一:
- External Cloud Provider 直接與 Kubernetes 集群對接,監聽 Kubernetes 資源對象變更事件:這種方式要求 Cloud Provider 本身支持與 Kubernetes 環境對接,兼容性和靈活性上略差;
- 實現方式二:
- 使用 Operator 在集群內監聽 Kubernetes 事件:Kubernetes 原生建議的對接方式,集群內 Operator 可以理解為 Kubernetes 與外部負載均衡器的橋梁,可以做 API 轉換等工作,靈活性很強。
- 數據面實現:

- 實現方式一:
- 在集群內部署 Pod 實現負載均衡服務:將 LB 數據面直接放在 Pod 中運行,這樣的好處是 LB Pod 到業務 Pod 流量路徑短,理論上也支持 ClusterIP 類型服務的實現。但缺點是復雜,需要考慮 Kubernetes 兼容性、網絡如何暴露、資源爭奪等諸多問題;
- 實現方式二:
- 外部負載均衡器+NodePort:一種云廠商使用較多的方式,這種實現可以理解為 NodePort 的優化,但繼承了 NodePort 的諸多缺點;
- 實現方式三:
- 外部負載均衡器直連 Pod:外部負載均衡器直接通過 Pod IP 訪問 Pod,中間不會經過 NodePort,這種實現性能更優,而且流量路徑更簡化,便于排錯,Avi 通常使用這種方式




