手动搭建Docker+Envoy环境出现No healthy upstream错误求解
核心差异及问题根因
你遇到的503错误本质不是网络连通性问题(毕竟Envoy内部curl能正常访问服务端),而是Envoy的DNS解析策略和Docker网络特性的匹配问题,docker-compose部署和手动部署的核心差异有以下几点:
1. 网络默认配置差异
- docker-compose会自动创建专用的自定义桥接网络,默认关闭IPv6分配,Docker内置DNS对该网络下的服务名只会返回IPv4的A记录,刚好匹配你Envoy默认只支持IPv4的配置
- 手动执行
docker network create创建的网络,Docker新版本默认会开启IPv6支持,内置DNS会同时返回server-stub的IPv4 A记录和IPv6 AAAA记录
你Envoy配置中的cluster没有开启IPv6支持,解析到AAAA记录后无法建立连接,就会标记上游不健康返回503,而curl默认优先尝试IPv4,所以手动curl能正常访问。
2. 容器启动顺序的隐性差异
- docker-compose默认按服务字典序启动容器,你的示例中
server-stub镜像启动速度通常快于Envoy,Envoy启动后第一次DNS解析时server-stub已经完成DNS注册,能正常解析到地址 - 手动部署如果按Envoy→服务端的顺序启动,Envoy的
STRICT_DNS模式启动时第一次解析server-stub失败,未配置DNS刷新规则的情况下,旧版本Envoy不会主动刷新解析结果,会一直标记上游不健康。
3. 网络别名的自动配置差异
- docker-compose会自动把服务名作为该服务所有容器的网络别名,无需额外配置
- 手动部署如果只指定了
--hostname而没有显式指定--network-alias server-stub,部分Docker版本可能只会在容器启动时注册一次DNS,更新不及时也可能导致解析异常。
适配手动部署的修复方案
你可以按以下步骤调整部署脚本:
- 创建网络时显式关闭IPv6:
docker network create --ipv6=false my-envoy-network
- 先启动服务端容器,再启动Envoy容器,两个容器都加入同一个网络,服务端显式设置网络别名:
# 启动服务端 docker run -d --name server-stub --network my-envoy-network --network-alias server-stub -p 5000:5000 你的服务端镜像名 # 启动Envoy docker run -d --name envoy --network my-envoy-network -p 10000:10000 -p 9901:9901 -v $(pwd)/envoy.yaml:/etc/envoy/envoy.yaml envoyproxy/envoy:v1.16-latest
- 也可以修改Envoy配置,给cluster添加DNS刷新配置和IPv4强制解析规则,避免启动顺序和IPv6的干扰:
clusters: - name: service connect_timeout: 0.25s type: STRICT_DNS lb_policy: round_robin dns_refresh_rate: 1s # 每秒刷新DNS解析 dns_lookup_family: V4_ONLY # 只解析IPv4,避免AAAA记录干扰 # 保留原有load_assignment等配置不变
内容的提问来源于stack exchange,提问作者TalR
相关产品推荐
相关产品推荐

