很多人觉得,在Kubernetes中不是只要用 Service就可以做负载均衡了吗?SVC确实有负载均衡的作用。它是由Kube-proxy提供,但是由于Grpc的特性没有办法让它很好的处理负载均衡。
SVC负载调度原理
在k8s的文档中阐述了kube-proxy的特点:
The kube proxy:
runs on each node #运行在每个节点
proxies UDP, TCP and SCTP #可以代理UDP、TCP、SCTP协议
does not understand HTTP #但是不能识别HTTP协议
provides load balancing #提供负载均衡
is just used to reach services #只用于到达服务
其中阐述了kube-proxy的iptbles代理,是不能支持应用层协议识别的。所以存在一个消费者与生产者之间过载复用一条tcp链接的情况,导致后端pod副本的应用载荷不一致。
下面是查看svc的负载均衡实现
service 的集群ip实现负载均衡
创建service后,kubernetes会在kube-proxy里面设置iptables相应规则,从api-server获取到后端pod的ip以及端口情况。生产相应转发规则
当目标地址为service的地址10.97.211.12/32和目标端口为8000时,跳转到KUBE-SVC-EHPSUHUW2JIVMPQD链
-A KUBE-SERVICES -d 10.97.211.12/32 -p tcp -m comment
--comment "default/backend:grpc cluster IP" -m tcp --dport 8000 \
-j KUBE-SVC-EHPSUHUW2JIVMPQD
链KUBE-SVC-EHPSUHUW2JIVMPQD为3条规则,副本数是3
30.333%的几率去KUBE-SEP-MPFDKKCFY2PBGU4S链(pod1 的ip)
-A KUBE-SVC-EHPSUHUW2JIVMPQD -m statistic --mode random --probability 0.33332999982 \
-j KUBE-SEP-MPFDKKCFY2PBGU4S
50%的几率去KUBE-SEP-LMG22MVGXQA364F6链(pod2的ip地址)
-A KUBE-SVC-EHPSUHUW2JIVMPQD -m statistic --mode random --probability 0.50000000000 \
-j KUBE-SEP-LMG22MVGXQA364F6
其余的全部走KUBE-SEP-EX64MF3TTLO7J5EC链(pod3的ip地址)
-A KUBE-SVC-EHPSUHUW2JIVMPQD -j KUBE-SEP-EX64MF3TTLO7J5EC
pod3的ip地址
-A KUBE-SEP-EX64MF3TTLO7J5EC -s 10.80.167.226/32 -j KUBE-MARK-MASQ
-A KUBE-SEP-EX64MF3TTLO7J5EC -p tcp -m tcp -j DNAT --to-destination 10.80.167.226:8000
pod1的ip地址
-A KUBE-SEP-MPFDKKCFY2PBGU4S -s 10.80.128.247/32 -j KUBE-MARK-MASQ
-A KUBE-SEP-MPFDKKCFY2PBGU4S -p tcp -m tcp -j DNAT --to-destination 10.80.128.247:8000
pod2的ip地址
-A KUBE-SEP-LMG22MVGXQA364F6 -s 10.80.153.167/32 -j KUBE-MARK-MASQ
-A KUBE-SEP-LMG22MVGXQA364F6 -p tcp -m tcp -j DNAT --to-destination 10.80.153.167:8000
所以得到:
pod3:10.80.167.226
pod2:10.80.128.247
pod1:10.80.153.167
这些ip和上面的comment信息,均来自apiserver,apiserver最终是在etcd里面存储并及时更新并管理这些对象。
所以在GRPC的技术棧中,生产者和消费者之间的互相调用,并达到真正的负载均衡的目的。使用其他方法进行,这里介绍两种方法。
使用无头服务
Headless Service的负载均衡,是通过内部coredns进行dns解析pod所有地址列表进行的。
kind: Service
apiVersion: v1
metadata:
name: backendh
spec:
selector:
app: backend #对应后端生产者或者消费者
ports:
- port: 8000
protocol: TCP
targetPort: 8000
clusterIP: None
在grpc的生产者与者消费者之间的通信,通过在发起方预埋变量名方式获取dns解析后向具体服务进行发起请求。
使用 istio 服务网格
1.在集群中部署好istio基础设施
2.编排VirtualService和DestinationRule CRD
因为是内部互相调用,并不需要Gateway。
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
name: backend
spec:
hosts:
- backend
http:
- route:
- destination:
host: backend
---
apiVersion: networking.istio.io/v1alpha3
kind: DestinationRule
metadata:
name: backend
spec:
host: backend
trafficPolicy:
loadBalancer:
simple: RANDOM