LabHub
学习 学习路径 课程

云网络设计

L4 与 L7,以及健康检查在做什么

在 LabHub 中继续学习

一句话总结

负载均衡器不是分配流量的设备,而是移除故障目标的设备。流量分配只是附带作用,其真正价值来自健康检查。

概念图: 移除故障目标的设备 · 持续检查目标状态的设备 · 把该目标从列表中移除。 · 路径

为什么需要了解这些

服务器扩展为两台后,马上会出现两个问题:用户应该被发送到哪一台,以及由谁发现其中一台已经故障。

在 DNS 中填写两个地址看起来成本最低,但它无法回答第二个问题。DNS 不知道目标是否存活,因此仍会持续分发故障服务器的地址;即使修改记录,在 TTL 过期前也不会生效。

因此,需要在流量前方部署一个持续检查目标状态的设备。分摊负载只是它顺便完成的工作,真正有价值的部分是把故障目标从列表中移除。

L4 与 L7

L4(网络层) L7(应用层)
检查内容 IP、端口 HTTP 请求头、路径、主机名
能力 转发 基于路径的路由、按主机分流、重定向
TLS 透传或终止 通常终止
延迟 极低 略高
用途 TCP、gRPC、游戏、数据库 Web API、微服务

/api/* 发送到服务 A、把 /img/* 发送到服务 B,只有 L7 才能实现。反过来,如果需要极高吞吐量,或使用的不是 HTTP,就应选择 L4。

健康检查才是真正的工作

负载均衡器会定期向目标发送请求,当失败次数超过阈值时,把该目标从列表中移除。 正因如此,即使一个实例故障,用户也不会察觉。

配置时需要关注以下事项。

前面课程讲过的 liveness/readiness 区分,在这里同样适用——负载均衡器的健康检查更接近 readiness。它问的是“现在能否接收流量”,而不是“是否应该重启”。

连接排空

移除实例时,如果直接切断正在处理的请求,用户就会收到错误。排空(deregistration delay)是指不再发送新请求,但等待进行中的请求完成的时间。

部署时的顺序如下。

1. 대상을 목록에서 빼기 시작 (새 요청 중단)
2. 드레이닝 대기 — 진행 중 요청 완료
3. 애플리케이션에 SIGTERM
4. 정상 종료

第 3 步就是容器课程中讲过的 SIGTERM。如果排空时间短于应用程序的优雅关闭时间,请求就会被截断。

DNS 与 TTL

负载均衡器前端通常是 DNS,而这里的 TTL 决定故障切换速度。

因此,原则是不要把 DNS 切换作为故障处理的主要手段。在负载均衡器内部移除目标要快得多,也可靠得多。

会话粘滞(sticky session)

该功能会把同一用户持续发送到同一台服务器。它看起来很方便,但存在代价。

把会话放在外部存储(如 Redis)中,就不再需要粘滞。只要条件允许,这种方式更好。

负载均衡器后方发生崩溃的时刻

负载均衡器平时很安静,却会在承受负载时和部署时暴露问题。理解这两个时刻会发生什么,就能避开大多数故障。

健康检查与应用程序使用相同资源时,会一同崩溃。 如果让 /health 查询数据库,那么数据库变慢的瞬间,所有实例都会同时变为 unhealthy。 被移除的不是一个实例,而是全部实例,整个服务会因此停止。应区分检查是否存活的 liveness 与检查是否准备好接收流量的 readiness,并确保前者不访问依赖项。

移除判定要宽松,恢复判定要严格。 如果实例因短暂延迟被移除,负载就会集中到剩余实例,继而使它们也被移除,连锁反应由此开始。失败阈值应留有余量(3~5 次),恢复阈值则设得较短会更安全。

部署时出现 502,原因通常是顺序。 如果应用程序在负载均衡器将实例移出列表前就先停止,此时进入的请求便无处可去。应颠倒顺序:收到 SIGTERM 后先让健康检查失败,等待负载均衡器察觉所需的时间(检查间隔 × 阈值),再完成正在处理的请求并退出。在 Kubernetes 中,preStop 钩子和 terminationGracePeriodSeconds 提供了这段时间。

连接排空时间必须长于最耗时的请求。 如果排空设置为 30 秒,而生成报告需要 60 秒,那么该请求会在每次部署时中断。

各层超时时间应不同,而且越靠内越短。 例如负载均衡器 60 秒、应用程序 55 秒、数据库 50 秒。若顺序相反,负载均衡器会先断开,而应用程序仍会继续生成已经无人等待的响应并占用连接。

空闲超时会破坏连接池。 如果负载均衡器在连接空闲 60 秒后将其关闭,而应用程序的连接池仍认为该连接存活,那么下次请求就会出现 connection reset。正确做法是把连接池的空闲时间设置得比负载均衡器更短

生产现场会看到什么

下一门课程

成本。到目前为止的每项决策都有价格,下一门课程将讲解如何读取这些价格。