GKE 内部负载均衡器不会在 gRPC 服务器之间分配负载

Joh*_*röm 7 load-balancing kubernetes google-kubernetes-engine grpc kubernetes-ingress

我有一个 API 最近开始接收更多流量,大约是 1.5 倍。这也导致延迟加倍:

潜伏

这让我感到惊讶,因为我设置了节点和 Pod 的自动缩放以及 GKE 内部负载平衡。

我的外部 API 将请求传递到使用大量 CPU 的内部服务器。查看我的 VM 实例,似乎所有流量都发送到我的两个 VM 实例之一(也称为 Kubernetes 节点):

每个节点的 CPU 利用率

通过负载平衡,我预计 CPU 使用率会在节点之间更加均匀地分配。

查看我的部署,第一个节点上有一个 pod:

第一个节点上的第一个 pod

第二个节点上有两个 pod:

在此输入图像描述

在此输入图像描述

我的服务配置:

$ kubectl describe service model-service
Name:                     model-service
Namespace:                default
Labels:                   app=model-server
Annotations:              networking.gke.io/load-balancer-type: Internal
Selector:                 app=model-server
Type:                     LoadBalancer
IP Families:              <none>
IP:                       10.3.249.180
IPs:                      10.3.249.180
LoadBalancer Ingress:     10.128.0.18
Port:                     rest-api  8501/TCP
TargetPort:               8501/TCP
NodePort:                 rest-api  30406/TCP
Endpoints:                10.0.0.145:8501,10.0.0.152:8501,10.0.1.135:8501
Port:                     grpc-api  8500/TCP
TargetPort:               8500/TCP
NodePort:                 grpc-api  31336/TCP
Endpoints:                10.0.0.145:8500,10.0.0.152:8500,10.0.1.135:8500
Session Affinity:         None
External Traffic Policy:  Cluster
Events:
  Type    Reason               Age                  From                Message
  ----    ------               ----                 ----                -------
  Normal  UpdatedLoadBalancer  6m30s (x2 over 28m)  service-controller  Updated load balancer with new hosts
Run Code Online (Sandbox Code Playgroud)

Kubernetes 启动了一个新的 pod,这一事实似乎表明 Kubernetes 自动缩放功能正在发挥作用。但第二个虚拟机上的 Pod 不会收到任何流量。如何让 GKE 更均匀地平衡负载?

11 月 2 日更新:

Goli的回答让我认为这与模型服务的设置有关。该服务公开 REST API 和 GRPC API,但 GRPC API 是接收流量的 API。

我的服务有对应的转发规则:

$ gcloud compute forwarding-rules list --filter="loadBalancingScheme=INTERNAL"
NAME                              REGION       IP_ADDRESS   IP_PROTOCOL  TARGET
aab8065908ed4474fb1212c7bd01d1c1  us-central1  10.128.0.18  TCP          us-central1/backendServices/aab8065908ed4474fb1212c7bd01d1c1
Run Code Online (Sandbox Code Playgroud)

其中指向后端服务:

$ gcloud compute backend-services describe aab8065908ed4474fb1212c7bd01d1c1
backends:
- balancingMode: CONNECTION
  group: https://www.googleapis.com/compute/v1/projects/questions-279902/zones/us-central1-a/instanceGroups/k8s-ig--42ce3e0a56e1558c
connectionDraining:
  drainingTimeoutSec: 0
creationTimestamp: '2021-02-21T20:45:33.505-08:00'
description: '{"kubernetes.io/service-name":"default/model-service"}'
fingerprint: lA2-fz1kYug=
healthChecks:
- https://www.googleapis.com/compute/v1/projects/questions-279902/global/healthChecks/k8s-42ce3e0a56e1558c-node
id: '2651722917806508034'
kind: compute#backendService
loadBalancingScheme: INTERNAL
name: aab8065908ed4474fb1212c7bd01d1c1
protocol: TCP
region: https://www.googleapis.com/compute/v1/projects/questions-279902/regions/us-central1
selfLink: https://www.googleapis.com/compute/v1/projects/questions-279902/regions/us-central1/backendServices/aab8065908ed4474fb1212c7bd01d1c1
sessionAffinity: NONE
timeoutSec: 30
Run Code Online (Sandbox Code Playgroud)

其中有健康检查:

$ gcloud compute health-checks describe k8s-42ce3e0a56e1558c-node                                          
checkIntervalSec: 8
creationTimestamp: '2021-02-21T20:45:18.913-08:00'
description: ''
healthyThreshold: 1
httpHealthCheck:
  host: ''
  port: 10256
  proxyHeader: NONE
  requestPath: /healthz
id: '7949377052344223793'
kind: compute#healthCheck
logConfig:
  enable: true
name: k8s-42ce3e0a56e1558c-node
selfLink: https://www.googleapis.com/compute/v1/projects/questions-279902/global/healthChecks/k8s-42ce3e0a56e1558c-node
timeoutSec: 1
type: HTTP
unhealthyThreshold: 3
Run Code Online (Sandbox Code Playgroud)

我的 Pod 列表:

kubectl get pods
NAME                                       READY   STATUS    RESTARTS   AGE
api-server-deployment-6747f9c484-6srjb     2/2     Running   3          3d22h
label-server-deployment-6f8494cb6f-79g9w   2/2     Running   4          38d
model-server-deployment-55c947cf5f-nvcpw   0/1     Evicted   0          22d
model-server-deployment-55c947cf5f-q8tl7   0/1     Evicted   0          18d
model-server-deployment-766946bc4f-8q298   1/1     Running   0          4d5h
model-server-deployment-766946bc4f-hvwc9   0/1     Evicted   0          6d15h
model-server-deployment-766946bc4f-k4ktk   1/1     Running   0          7h3m
model-server-deployment-766946bc4f-kk7hs   1/1     Running   0          9h
model-server-deployment-766946bc4f-tw2wn   0/1     Evicted   0          7d15h
model-server-deployment-7f579d459d-52j5f   0/1     Evicted   0          35d
model-server-deployment-7f579d459d-bpk77   0/1     Evicted   0          29d
model-server-deployment-7f579d459d-cs8rg   0/1     Evicted   0          37d
Run Code Online (Sandbox Code Playgroud)

IA) 如何确认此健康检查实际上显示 2/3 后端不健康?B) 配置运行状况检查以将流量发送到我的所有后端?

11 月 5 日更新:

在发现过去有几个 Pod 由于 RAM 太少而被驱逐后,我将这些 Pod 迁移到了新的节点池。旧的节点池虚拟机有 4 个 CPU 和 4GB 内存,新的节点池虚拟机有 2 个 CPU 和 8GB 内存。这似乎已经解决了逐出/内存问题,但负载均衡器仍然一次只向一个 pod 发送流量。

节点 1 上的 Pod 1:

节点 1 上的 Pod 1

节点 2 上的 Pod 2:

在此输入图像描述

看起来负载均衡器根本没有分割流量,而只是随机选择一个 GRPC 模型服务器并向那里发送 100% 的流量。是否有一些我错过的配置导致了这种行为?这和我使用GRPC有关吗?

Joh*_*röm 4

事实证明,答案是您无法使用 GKE 负载平衡器对 gRPC 请求进行负载平衡

每次形成新的 TCP 连接时,GKE 负载均衡器(以及 Kubernetes 的默认负载均衡器)都会选择一个新的后端。对于常规 HTTP 1.1 请求,每个请求都会获得一个新的 TCP 连接,并且负载均衡器工作正常。对于 gRPC(基于 HTTP 2),TCP 连接仅建立一次,所有请求都在同一连接上复用。

这篇博文中有更多详细信息。

要启用 gRPC 负载平衡,我必须:

  1. 安装Linkerd
curl -fsL https://run.linkerd.io/install | sh
linkerd install | kubectl apply -f -
Run Code Online (Sandbox Code Playgroud)
  1. 接收和发送 pod中注入 Linkerd 代理:

在此输入图像描述

kubectl apply -f api_server_deployment.yaml
kubectl apply -f model_server_deployment.yaml
Run Code Online (Sandbox Code Playgroud)
  1. 在意识到 Linkerd 无法与 GKE 负载均衡器一起工作后,我将接收部署公开为 ClusterIP 服务。
kubectl expose deployment/model-server-deployment
Run Code Online (Sandbox Code Playgroud)
  1. 将 gRPC 客户端指向我刚刚创建的 ClusterIP 服务 IP 地址,并重新部署客户端。 在此输入图像描述
kubectl apply -f api_server_deployment.yaml
Run Code Online (Sandbox Code Playgroud)