GCP区域实例组与全局外部应用负载均衡流量分配异常排查
我有一个包含2个实例的区域性实例组,实例在8000端口提供服务,未启用自动扩缩容,该实例组作为**全局外部应用负载均衡(Global external application load balancer)**的后端。
实例上的Web服务器运行Flask,每个请求处理耗时1秒,代码如下:
@app.route("/", methods=["GET"]) def main() -> Response: sleep(1) response = jsonify( success=True, hostname=gethostname(), ) response.status_code = 200 response.headers["Access-Control-Allow-Origin"] = "*" return response
测试全局负载均衡公网IP时,单个请求响应耗时1秒符合预期。但同时发送2个请求时,本应分别分配到2个实例,却全部流向同一实例导致排队:一个请求耗时1秒,另一个耗时2秒。
我已尝试将Locality load balancing policy修改为Round-Robin、Least-Request和Random,但现象始终一致。
请问Locality load balancing policy是否仅用于选择后端?若是,如何配置后端(即实例组)内部的负载均衡策略?
详细配置
实例组(Instance group)
- 类型:区域性(Regional)
- 目标分布形态:均匀(Even)
- ✅ 允许实例重分配(Allow instance redistribution)
- 自动扩缩容:最小2,最大2
- 自动扩缩容信号:HTTP负载均衡100%
- 初始化周期:60s
- 健康检查:
- 路径:/health
- 协议:HTTP
- 端口:8000
- 间隔:30秒
- 超时:30秒
- 健康阈值:1
- 不健康阈值:10
负载均衡器(Load balancer)
- 前端(Frontend):
- 协议:HTTP
- IP:x.x.x.x
- 网络层级:Premium
- HTTP长连接超时:610秒
- 路由规则:全部未匹配请求
- 后端服务(Backend services):
- 端点协议:HTTP
- 命名端口:web
- 超时:300秒
- Cloud CDN:禁用
- 日志:启用(采样率1)
- 会话亲和性:无
- 连接排空超时:300秒
- 流量策略:
- 本地负载均衡策略:轮询(Round robin)
- 异常实例检测:禁用
- 后端安全策略:无
- 边缘安全策略:无
- 身份感知代理:禁用
- 均衡模式:最大RPS:1(每实例)
- 容量:100%
测试数据
使用siege工具测试,命令示例:
siege \ --concurrent 1 \ --time 60s \ "http://x.x.x.x"
2个节点时
concurrent=1: 平均1.02秒concurrent=2: 平均1.66秒concurrent=4: 平均3.35秒concurrent=8: 平均5.54秒
4个节点时
concurrent=1: 平均1.02秒concurrent=2: 平均1.18秒concurrent=4: 平均2.70秒concurrent=8: 平均3.83秒concurrent=16: 平均7.26秒
8个节点时
concurrent=2: 平均1.20秒concurrent=4: 平均1.85秒concurrent=16: 平均4.40秒concurrent=64: 平均14.06秒concurrent=128: 平均19.04秒
预期行为
- 2个节点:
concurrent=1: 1秒concurrent=2: 1秒concurrent=4: ~2秒concurrent=8: ~4秒
- 4个节点:
concurrent=1: 1秒concurrent=2: 1秒concurrent=4: 1秒concurrent=8: ~2秒concurrent=16: ~4秒
更新1
切换为Classic proxy网络负载均衡器发送100个请求时:
- 56个请求流向vm0
- 44个请求流向vm1
而使用HTTP负载均衡器时:
- 99个请求流向vm0
- 1个请求流向vm1
问题解答
Locality load balancing policy的作用
Locality load balancing policy确实仅用于在不同区域/可用区的后端服务之间选择流量目标,不会直接控制同一实例组内的实例负载分配逻辑。
实例组内部负载分配异常的解决方法
禁用长连接复用
负载均衡器设置了HTTP keepalive timeout: 610 sec,而siege默认会复用TCP连接,同一连接上的所有请求都会被转发到同一个后端实例,这是请求集中到单个实例的核心原因。- 测试时添加
--no-keepalive参数禁用连接复用:siege \ --concurrent 2 \ --time 60s \ --no-keepalive \ "http://x.x.x.x"
- 测试时添加
理解均衡模式配置
你设置的Balancing mode: Max. RPS: 1 (per instance)是自动扩缩容的触发阈值,并非实时负载分配的调度规则。全局应用负载均衡的实例级调度依赖内置逻辑,但长连接会绕过该调度。验证负载分配的正确方式
可以用不同客户端IP发起请求,或用curl循环发起请求(每次新建连接),观察请求是否均匀分配到实例。L7与L4负载均衡的差异
网络负载均衡(L4)基于TCP连接分发,而应用负载均衡(L7)基于HTTP请求分发,但长连接复用会导致L7 LB将同一连接上的所有请求发送到同一实例,这也是切换到网络LB后分配更均匀的原因。
内容的提问来源于stack exchange,提问作者yellowhat

