把切流下沉到 DNS:一次多云服务发现迁移设计
在专线带宽有限、不能停机且必须快速回滚的条件下,使用 dnsmasq、NLB 与 CoreDNS 构建临时迁移控制面,完成 AWS 到阿里云/腾讯云的渐进式切流。
背景与约束
旧系统使用静态配置维护 service -> cluster -> host:port 映射,服务间请求最终经过 AWS ELB / NLB。迁移目标是把大部分服务移动到阿里云,少量服务移动到腾讯云,并逐步下线 AWS。
这不是一次简单的域名替换,因为迁移同时受到四个约束:
- 跨云专线带宽有限,不能一次性切走全部流量;
- 线上服务不能停机;
- 每个服务需要独立灰度;
- 出现错误时必须快速回滚。
原始调用链可以抽象为:
Application
-> Static Service Config
-> Client-side Endpoint Selection
-> Legacy Load Balancer
-> Target Service两种方案
方案一:在应用配置中维护多套 Endpoint
最直接的方式是在配置中按云厂商维护三组地址,再通过环境变量选择:
$cloud = getenv('CLOUD_PROVIDER');
$serviceHosts = [
'aws' => [['host' => '<legacy-endpoint>', 'port' => 8080]],
'aliyun' => [['host' => '<aliyun-endpoint>', 'port' => 8080]],
'tencent' => [['host' => '<tencent-endpoint>', 'port' => 8080]],
];它不需要新增基础设施,但切换粒度通常是进程或部署单元。改一次流量就要改配置、发布、重启;回滚也要再次发布。对于需要 1% / 10% / 50% 渐进切流的迁移,这个控制面太慢。
方案二:在 DNS 层增加迁移控制面
应用继续使用原来的主机名,DNS 覆盖层决定当前应该解析到哪一朵云:
Application
-> local resolver
-> dnsmasq cache
-> Internal NLB
-> CoreDNS cluster
-> Legacy / Aliyun / Tencent endpoint这样,应用配置在迁移期间保持稳定,切流与回滚只修改 DNS 覆盖记录。
为什么选择 DNS 覆盖层
| 能力 | 应用配置切换 | DNS 覆盖层 |
|---|---|---|
| 单服务切换 | 需要发布 | 修改记录 |
| 主机分组灰度 | 需要不同配置版本 | 可按 resolver / zone view 分组 |
| 回滚速度 | 取决于发布链路 | 取决于 TTL、连接池和缓存 |
| 应用改动 | 中等 | 较小 |
| 运维复杂度 | 配置膨胀 | 新增 DNS 关键链路 |
DNS 方案不是永久替代服务注册中心。它在迁移期承担流量控制;稳态后,应用配置应换成目标云中由团队控制的服务域名,并逐步拆除临时覆盖。
组件职责
dnsmasq
每台应用机器部署本地缓存,减少跨网络 DNS 查询,并允许只把迁移名单中的 FQDN 转发到 CoreDNS。
no-hosts
cache-size=10000
# 只转发明确进入迁移名单的域名
server=/legacy-service-a.example.internal/<COREDNS_NLB_IP>
server=/legacy-service-b.example.internal/<COREDNS_NLB_IP>
# 其他请求仍走原有解析链路
server=<ORIGINAL_DNS_IP>不要把整个云厂商域名后缀都劫持到内部 DNS,否则对象存储、消息队列等无关服务也可能受到影响。
NLB
内网 NLB 为 CoreDNS 提供稳定入口,后端至少跨可用区部署三个实例。DNS 同时使用 UDP 和 TCP 53,健康检查与安全组需要覆盖两种协议。
CoreDNS
CoreDNS 维护迁移名单中的精确记录,未命中的域名继续转发到原 DNS:
.:53 {
errors
health :8080
prometheus :9153
hosts /etc/coredns/migration.hosts {
ttl 30
fallthrough
}
forward . <ORIGINAL_DNS_IP>
cache 30
}覆盖文件只使用占位示例:
<LEGACY_TARGET_IP> legacy-service-a.example.internal
<ALIYUN_NLB_IP> legacy-service-b.example.internal
<TENCENT_CLB_IP> legacy-service-c.example.internal真实 IP、云账号、负载均衡器标识和内部域名不应出现在公开仓库。
不要把 DNS 轮询当成精确权重
在 zone file 中增加多条 A 记录,再用 loadbalance 打乱顺序,只能近似改变首地址分布,不能保证真实请求严格按 10% / 90% 切分。原因包括:
- dnsmasq、系统 resolver 和应用连接池都会缓存;
- 客户端可能固定使用第一个地址,也可能自行重试;
- 相同 RDATA 可能被当成同一个 RRset 成员;
- 长连接会让 DNS 查询比例与请求流量比例失真。
迁移初期更稳定的方法是按应用机器分组:
1% canary hosts -> canary DNS view -> new cloud
99% stable hosts -> stable DNS view -> legacy cloud如果必须做精确请求级权重,应使用云流量调度、L7 网关、服务网格或明确支持权重的 DNS 能力。
五阶段迁移
Phase 1:搭建解析链路
- 部署跨可用区 CoreDNS 集群;
- 配置 UDP / TCP 53 的内网 NLB;
- 批量部署 dnsmasq,但先不修改业务解析目标;
- 建立配置版本、自动校验和一键回滚。
Phase 2:生成覆盖记录并验证
从旧服务配置自动提取主机名,生成迁移清单。初始记录仍指向旧云目标,先证明新解析链路不会改变流量。
生成器至少校验:
- FQDN 是否合法且无重复;
- 目标 IP 是否属于允许网段;
- 每条记录是否能在旧配置中找到来源;
- 变更前后 diff 是否经过双人 review。
性能门槛可以设置为:热缓存 P99 小于 1ms,冷查询 P99 小于 20ms,CoreDNS CPU 在压测下保留足够余量。
Phase 3:灰度接入新 DNS 链路
按 dev / test / production 的顺序接入:
non-production -> 1% production hosts -> 10% -> 50% -> 100%此阶段解析目标仍是旧云,只验证 dnsmasq、NLB 和 CoreDNS 的稳定性。重点观察缓存命中率、解析失败、P99 延迟和 SERVFAIL / NXDOMAIN。
Phase 4:按服务迁移流量
每个服务使用固定流程:
- 新云实例与负载均衡器健康检查通过;
- 记录 QPS、错误率、P95 / P99 延迟和专线带宽基线;
- 先让 canary 主机组解析到新云;
- 观察一个完整窗口后扩大主机组;
- 100% 切换后继续观察至少一个业务周期;
- 保留旧云实例,直到回滚窗口结束。
服务迁移顺序应从离线任务和非核心读服务开始,再到核心读服务,最后处理交易、库存等写链路。
Phase 5:替换正式域名并清理覆盖层
所有服务稳定运行后,把静态配置中的旧地址替换为目标云的正式内部域名。配置灰度完成并稳定一段时间后:
- 确认覆盖域名的查询量归零;
- 删除 dnsmasq 的定向转发;
- 清理 CoreDNS 覆盖记录;
- 下线旧云负载均衡器和实例;
- 保留迁移记录与审计数据。
回滚设计
单服务回滚
把目标记录恢复到旧云,刷新配置并等待 TTL。不要把“TTL 30 秒”等同于“30 秒内所有请求都回去”,连接池、HTTP Keep-Alive 和 JVM / PHP DNS 缓存都可能延长生效时间。
DNS 控制面故障
本地 dnsmasq 应配置受控的备用上游,自动化脚本可以把 resolver 恢复到原 DNS。CoreDNS 配置发布前必须经过语法检查,并采用不可变版本与原子切换。
全链路回滚
专线或新云发生大面积故障时,将所有迁移视图恢复到旧云。演练时需要同时验证 DNS、连接池、数据库依赖和消息链路,而不只是 dig 的返回结果。
四层可观测性
多云之后,单看应用错误无法判断请求落在哪一朵云,需要补齐四层信息。
1. DNS 查询日志
记录查询时间、客户端分组、域名、返回地址、响应码和配置版本。稳定后应采样,避免日志量失控。
2. 应用侧目标地址
在请求日志中记录解析后的目标 IP、服务名和 trace ID,但不要记录认证 Header 或完整业务参数。
3. 响应 Header 染色
各云网关返回标准 Header:
add_header X-Served-Cloud "aliyun" always;
add_header X-Served-Region "region-a" always;内部客户端可把它写入链路日志,公网响应则应评估是否需要隐藏基础设施信息。
4. 统一 Dashboard
按服务和云维度展示:QPS、成功率、延迟、带宽、DNS 响应码、缓存命中率和 CoreDNS 实例状态。
常见问题
- 切了但没生效:逐层检查应用缓存、系统 resolver、dnsmasq 和 CoreDNS TTL;
- 专线带宽上涨:立即冻结扩量,降低 canary 主机比例并拆分大流量服务;
- 解析延迟上升:检查缓存穿透、NLB 健康状态和 CoreDNS CPU;
- 配置错误:发布前执行解析测试集,使用原子文件替换并保留上一版本;
- 无法判断流量去向:使用 DNS 日志、目标 IP、响应染色和 trace ID 联合定位;
- 跨云依赖绕路:把服务调用、数据库和消息系统一起纳入依赖图,避免只迁移计算节点。
总结
这套方案的核心不是 CoreDNS 本身,而是把迁移控制面从应用发布链路中抽离出来:先验证解析链路,再按主机组灰度,最后按服务切换,并始终保留可观察、可回滚的旧路径。
DNS 覆盖层适合承担迁移期的临时编排。迁移完成后应回到团队自有域名和清晰的稳态架构,避免临时设施成为永久债务。
评论(0)
还没有评论,来抢沙发吧 ✦