bigywater.tech
Docker · 93

DNS 轮询:同名多个容器的解析顺序

用共用别名做负载均衡靠谱吗?返回顺序是轮转还是随机,分布有多均匀,客户端缓存了地址会怎样,后端挂了客户端会不会踩坑?这篇用三个后端和一个计数客户端逐项量出。

103 / 99 / 98
每次重新解析并新建连接的 300 个请求在三个后端的分布
300 / 0 / 0
只解析一次的客户端,300 个请求全部落到同一个后端
错误 20
缓存了已停止后端 IP 的客户端,20 个请求全部失败

一次返回所有地址

实测 · 本机执行输出
  三个后端的 IP 末段:{1: '2', 2: '3', 3: '4'};getaddrinfo("svc") 返回的全部地址末段(排序后):2 3 4

多个容器共用同一个网络别名时(c50),内置 DNS 一次返回所有后端的地址,客户端自己选。

返回的顺序

实测 · 本机执行输出
  第一个地址的末段序列:4 4 3 4 2 3 3 4 2 2 2 2 2 2 3 3 2 4 4 2

顺序每次不同。20 次采样的第一个地址不是严格轮转(中间连续六次都是末段 2),更像是随机打乱,具体算法我没有查。

分布

实测 · 本机执行输出
  各后端收到的请求数(从多到少):[103, 99, 98];不同的响应者 3 个;错误 0

每次请求都重新解析并新建连接时,300 个请求分成 103 / 99 / 98,比较均匀。

缓存的客户端

实测 · 本机执行输出
  解析一次得到的 IP 末段 2;300 个请求各后端收到:[300];错误 0

只解析一次的客户端(连接池、长连接、应用启动时解析并缓存),所有请求都会落到同一个后端;这是 DNS 轮询最大的局限。

健康与失败

实测 · 本机执行输出
  解析 svc 得到的地址末段:2 4(停掉的是末段 3)
  每次重新解析的客户端 120 个请求:各后端收到 [63, 57],错误 0
  早先缓存了那个已停止后端的 IP 的客户端(模拟):20 个请求,各后端收到 [],错误 20(20 个请求总共用时 0.5 秒,含启动容器的开销)

DNS 轮询没有健康检查:后端停掉后,内置 DNS 立刻不再返回它的地址,但已经缓存了旧地址的客户端会一直失败,直到重新解析。需要真正的负载均衡与健康检查时,用反向代理或编排系统。

分步演示

DNS 轮询
本步的对象

    本文用到的工具与边界

    • 本机 macOS 27 arm64 / Docker Desktop(Engine 29.8.2,容器跑在 Docker Desktop 自带的 Linux 虚拟机里,内核与宿主机的 macOS 不同);镜像为 linux/arm64;需要看虚拟机内部时用特权容器 + nsenter;计时用 time / date +%s%N,数字每次运行会有小幅抖动。后端是 python:3.12-alpine 的小 HTTP 服务(返回自己的主机名);客户端用 Python 的 getaddrinfo 和 urllib,每个请求新建连接(Connection: close);统计由客户端按响应的主机名计数;顺序采样 20 次、分布采样 300 次,各一次;随机性意味着具体数字每次不同;实验后已删除容器和网络
    • 没有验证:Compose 的 scale 场景(见 c63)、IPv6 记录、不同语言解析器对多地址的处理(Java 缓存、Go 的行为)、连接复用(keep-alive)的客户端、DNS TTL 的实际值
    京ICP备2026067502号-1京公网安备11010502063487号