支持与 L4 负载均衡接入实战指南)
MongoDB 代理协议PROXY Protocol支持与 L4 负载均衡接入实战指南【免费下载链接】mongoThe MongoDB Database项目地址: https://gitcode.com/GitHub_Trending/mo/mongo本指南基于 MongoDB 仓库的 docs/load_balancer_support.md 展开系统讲解mongod与mongos对 HAProxy 代理协议Proxy ProtocolV1/V2的内建支持包括proxyPort与loadBalancerPort两个关键端口的配置方法、反向代理与 L4 负载均衡两种部署形态的差异、必须满足的安全前提以及连接断开时游标与事务的特殊处理行为。读完本文你将掌握如何正确地把mongod/mongos放入 L4 负载均衡器或反向代理之后并理解其底层握手与hello处理的实现原理。为什么需要代理协议支持mongod和mongos原生支持通过 L4 负载均衡器建立的连接负载均衡器使用代理协议Proxy Protocol头部来传递真实客户端信息。当客户端连接的是负载均衡器而不是数据库节点本身时数据库服务端默认只能看到负载均衡器的地址代理协议允许负载均衡器在连接流的起始处附加一段携带真实源地址、目标地址以及可选的 TLV 元数据的头部服务端据此还原真实端点。把mongos或mongod放到负载均衡器之后需要对负载均衡器、mongos、mongod三端同时做正确配置缺一不可。这正是本文后续各节要逐一解决的问题。安全考虑代理端口的信任边界这是整个方案中优先级最高、也最容易出事故的一点。文档明确指出proxyPort/loadBalancerPort头部中声称的源地址是在没有认证的情况下被无条件信任的并且会原样verbatim用于审计日志的归属audit log attribution。也就是说任何能够触达该端口的主机都可以伪造一个任意来源 IP从而在审计日志中留下虚假记录。因此该端口的访问必须通过防火墙 / 安全组规则严格限定为可信主机即负载均衡器或反向代理本身任何其他主机如果能够直连该端口即可伪造其记录中的源地址构成安全风险。这一点在仓库的配置定义中也被反复强调mongod的net.proxyPort与mongos的loadBalancerPort描述中都带有 Access to this port MUST be restricted to trusted hosts (the load balancer or reverse proxy) 的明确警告见 mongod_options_general.idl 与 mongos_server_parameters.idl。配置 mongod开启 proxyPort要让mongod与 L4 负载均衡器或反向代理配合必须在启动时配置proxyPort选项。该选项的值可以通过服务器配置文档中提到的任意方式命令行参数或 YAML 配置文件在启动时指定。配置项在仓库中的正式定义位于 src/mongo/db/mongod_options_general.idl关键属性如下属性值说明完整名称net.proxyPortYAML 配置中的写法短名称proxyPort命令行参数写法类型Int端口号取值校验gte: 1, lte: 65535合法端口范围不允许为 0来源cli, yaml仅支持命令行与 YAML 两种传入方式启用proxyPort后mongod会额外打开一个新端口L4 负载均衡器必须连接这个新端口。同时负载均衡器或反向代理必须在连接流的起始处发送代理协议头部mongod同时支持代理协议标准的V1 和 V2两个版本。命令行示例mongod --proxyPort 20200 --bind_ip 0.0.0.0YAML 配置示例net: port: 27017 proxyPort: 20200注意proxyPort是独立于常规net.port的第二个监听端口。常规端口继续为不需要代理协议的直接客户端提供服务而代理端口专门接收来自负载均衡器的、携带代理协议头部的连接。反向代理 vs 负载均衡器两种形态的语义差异分片集群可以配置为与 L4 负载均衡器或反向代理配合工作。两种情况下代理或负载均衡器都必须连接到mongos的负载均衡端口loadBalancerPort。但两者对客户端视角的影响截然不同反向代理Reverse Proxy场景将mongos放在反向代理之后并不会隐藏mongos的列表驱动程序会通过反向代理选择一个特定的mongos来建立连接因此客户端仍然能够感知并通过代理路由到具体的mongos实例。L4 负载均衡器场景将mongos放在 L4 负载均衡器之后会隐藏mongos列表驱动程序只看到负载均衡器它建立的连接由负载均衡器路由到某个mongos不保证来自同一个驱动程序的全部连接都命中同一个mongos通常可以预期来自一个驱动程序的连接会被分散到多个mongos上。这一差异直接决定了客户端是否需要额外的loadBalanced选项以及mongos对连接断开后游标/事务的处理策略详见下文。配置 mongos 反向代理当分片集群通过反向代理部署时需要满足两个条件mongos必须配置loadBalancerPort服务器参数Server Parameter该参数的值可在启动时以服务器参数文档所述的任意方式指定。启用后mongos会打开第二个端口。来自反向代理的所有连接都必须走这个端口而且该端口上不允许出现普通连接即不带 HAProxy 协议头部的连接。反向代理必须被配置为在连接流起始处发出代理协议头部mongos同时支持代理协议标准的V1 和 V2两个版本。值得注意的是与没有反向代理的集群相比驱动程序不需要任何配置变更。这是反向代理形态与 L4 负载均衡器形态的重要区别。loadBalancerPort在仓库中的定义位于 src/mongo/db/sharding_environment/mongos_server_parameters.idl属性值说明类型Atomicintcpp_varname: loadBalancerPort原子整数便于运行时读取默认值00 表示完全禁用该端口设置时机set_at: [startup]仅启动时可设置取值校验gte: 0, lte: 655350 为禁用命令行示例mongos --configdb csrs/cfg1:27019,cfg2:27019,cfg3:27019 \ --setParameter loadBalancerPort20100配置 mongos L4 负载均衡器当分片集群通过 L4 负载均衡器部署时需要满足三个条件mongos必须配置loadBalancerPort服务器参数同上节打开第二个端口、所有负载均衡器连接必须走该端口、该端口不允许普通连接。L4 负载均衡器必须被配置为在连接流起始处发出代理协议头部mongos支持 V1 和 V2 两个版本。客户端必须设置loadBalanced选项通过负载均衡器连接到mongos的客户端驱动程序或 shell必须设置loadBalanced选项。例如本地mongos的loadBalancerPort设为 20100则连接串必须形如mongodb://localhost:20100/?loadBalancedtrueloadBalancedtrue的作用是告知mongos该连接来自负载均衡器客户端是支持负载均衡的load-balancer-aware driver。负载均衡连接的特殊行为游标与事务loadBalancerPort选项还会启用一些微妙的行为差异核心在于mongos如何处理客户端断开连接时的打开游标open cursors普通连接客户端断开后mongos会在一小段时间内保留打开的游标以防客户端重连后继续从该游标取数。负载均衡连接由于客户端在负载均衡器后面重连时负载均衡器很可能会把客户端路由到另一个不同的mongos实例所以重连后继续使用原游标是不现实的。因此mongos会在负载均衡客户端断开时急切地关闭其游标并且中止abort该负载均衡客户端发起的任何进行中的事务in-progress transactions。这两个行为在 src/mongo/s/load_balancer_support.cpp 中有对应的支撑结构PerClient装饰器会记录每个客户端最近一次用于多语句事务的逻辑会话 IDgetMruSession/setMruSession供连接断开时的清理逻辑使用PerService装饰器则持有该mongos的serviceId。源码原理代理协议头部的解析mongos/mongod对代理协议的支持并不是靠外部库而是在仓库内部实现了完整的解析器位于 src/mongo/transport/proxy_protocol_header_parser.h 与 src/mongo/transport/proxy_protocol_header_parser.cpp。头部签名识别解析器通过两个签名常量区分 V1 与 V2V1 签名PROXYkProxyV1SignatureV1 头部是明文 ASCII 文本行以\x0D\x0ACRLF结尾最大头部长度限制为 107 字节源码中的kMaximumV1HeaderSizeV2 签名\x0D\x0A\x0D\x0A\x00\x0D\x0A\x51\x55\x49\x54\x0AkProxyV2Signature其中包含嵌入的 NUL 字节因此源码注释特别提醒必须用.size()而不是strlen()V2 为二进制格式前 12 字节为固定魔数。解析入口parseProxyProtocolHeader(buffer, isProxyUnixSock)会先探测缓冲区是否以 V1 或 V2 签名开头如果两者都不是则抛出FailedToParse错误错误信息会提示 Make sure your proxy is configured to emit a Proxy Protocol header。如果头部不完整例如 TCP 分片导致只收到部分字节则返回空 optional等待更多数据。V1 解析细节V1 解析parseV1Buffer处理形如PROXY TCP4 srcIP dstIP srcPort dstPort\r\n的明文行支持TCP4、TCP6与UNKNOWN三种协议标记IPv4 地址会被逐段校验每段 0~255IPv6 地址则校验十六进制段hexadectet的数量与格式并限制最多一个::压缩段端口通过十进制解析器严格解析并校验不超过 65535解析成功的端点会被封装为ProxiedEndpoints真实源地址 真实目标地址。V2 解析细节V2 解析parseV2Buffer按二进制格式逐字节解码第 13 字节高 4 位必须是版本号 2低 4 位区分LOCAL\x20与PROXY\x21即远程连接下一字节的高 4 位为地址族AF_UNSPEC/AF_INET/AF_INET6/AF_UNIX低 4 位为传输协议校验不超过 0x2随后按地址族解析端点IPv4 占 12 字节源/目的地址各 4 字节 各 2 字节端口IPv6 占 36 字节UNIX 域占 216 字节路径最大长度 108即kMaxUnixPathLength头部剩余部分是可选的 TLV 向量仅当连接来自代理 UNIX 域套接字isProxyUnixSock时才被强制要求存在并解析否则忽略TLV 类型须落在合法区间0x01~0x05、0x20~0x25、0x30、0xE0~0xEF其中0x02kProxyProtocolTypeAuthority是 AUTHORITY TLV用于携带 SNI 信息0x20是 SSL TLV其内部还包含子 TLV 结构如0xE0表示客户端证书的 distinguished name。MongoDB 利用这些信息填充SSLPeerInfo供 split horizon 逻辑判断应套用哪个 horizon。值得一提的实现细节解析器定义kDefaultProxyProtocolHeaderReadSize 536即最小 TCP MTU表示代理协议头部所需的最大字节数服务端据此决定首次读取的缓冲区大小保证一个头部一定能在单次读取内完整到达。源码原理hello 握手与 serviceId负载均衡支持的核心逻辑在 src/mongo/s/load_balancer_support.h 与 src/mongo/s/load_balancer_support.cpp 中实现主要包括以下几个部分。每个 mongos 一个 serviceId每个mongos进程在启动时会通过PerService装饰器生成一个全局唯一的OID serviceIdOID::gen()并存活于整个 service context 生命周期。这个serviceId就是负载均衡场景下客户端识别我当前连的是哪个mongos的依据。handleHello仅首次 hello 特殊handleHello(opCtx, result, helloHasLoadBalancedOption)负责mongos上hello命令的负载均衡处理其逻辑是如果该客户端已经做过hellodidHello或并非从负载均衡端口连入直接返回不做任何特殊处理否则记录该客户端是否为负载均衡对端setIsLoadBalancerPeer(helloHasLoadBalancedOption)只有当首个hello请求携带loadBalanced: true时才会在回复中附加serviceId字段标记didHello保证后续hello不再附加serviceId。从源码结构可以推断这样的设计实现了两条协议约定连接标记从负载均衡端口连入的连接必须通过首个hello命令中的loadBalanced: true来确认它来自支持负载均衡的驱动程序否则服务端将抛出ErrorCodes::LoadBalancerSupportMismatch见 load_balancer_support.h 的注释说明服务发现serviceId让驱动程序能够区分负载均衡器后面的不同mongos从而在建立会话、执行游标续读等操作时正确感知后端实例的变化。端口启停判断getLoadBalancerPort()读取loadBalancerPort服务器参数的当前值非 0 时返回端口号为 0 时返回空即未启用。isEnabled()还叠加了一个测试用 fail pointloadBalancerSupportClientIsFromLoadBalancerPort用于在测试中模拟来自负载均衡端口的连接即使没有实际配置端口。测试验证仓库中的单元测试负载均衡支持的握手行为有完整的单元测试覆盖位于 src/mongo/s/load_balancer_support_test.cpp它通过 fail pointloadBalancerSupportClientIsFromLoadBalancerPort模拟来自负载均衡端口的连接并验证serviceId字段的返回规则测试用例模拟场景期望结果HelloNormalClientNoOption普通连接hello 无loadBalanced回复不含serviceIdHelloNormalClientGivesOption普通连接hello 带loadBalanced回复不含serviceIdHelloLoadBalancedClientNoOption负载均衡连接hello 无loadBalanced回复不含serviceIdHelloLoadBalancedClientGivesOption负载均衡连接hello 带loadBalanced首个 hello 回复含serviceId后续 hello 不再包含only first hello is special这组测试精确地印证了上文描述的规则只有来自负载均衡端口 首个 hello 携带 loadBalanced: true两者同时成立时serviceId才会返回。此外代理协议解析器本身也有对应的解析测试见 src/mongo/transport/proxy_protocol_header_parser_test.cpp 与 src/mongo/transport/proxy_protocol_tlv_extraction.cpp含 SSL/TLV 提取与测试 proxy_protocol_tlv_ssl_peer_info_test.cpp。配置检查清单最后把文档中的硬性要求汇总为一份可直接对照的检查清单部署任何形态前必做通过防火墙/安全组把proxyPort/loadBalancerPort的访问限制为仅可信主机负载均衡器/反向代理。负载均衡器/反向代理启用代理协议Proxy Protocol发送V1 或 V2 均可。mongod 形态mongod以--proxyPort port或 YAMLnet.proxyPort启动端口范围 1~65535。负载均衡器连接proxyPort对应端口。mongos 反向代理形态mongos设置loadBalancerPortport启动时0 表示禁用。反向代理的所有连接走该端口且都带代理协议头部。客户端无需任何配置变更。mongos L4 负载均衡器形态mongos设置loadBalancerPortport。L4 负载均衡器的所有连接走该端口且都带代理协议头部。客户端连接串必须带?loadBalancedtrue例如mongodb://localhost:20100/?loadBalancedtrue。理解代理端口上的源地址被无条件信任这一安全前提正确区分反向代理与 L4 负载均衡两种形态的客户端配置差异再结合对 load_balancer_support.cpp 与 proxy_protocol_header_parser.cpp 源码级行为的认知就能在生产环境中稳定、安全地把 MongoDB 放入负载均衡体系。【免费下载链接】mongoThe MongoDB Database项目地址: https://gitcode.com/GitHub_Trending/mo/mongo创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考