近来把 K8S 节点的操作系统升级了下,结果有个容器起不来了,日志报错:

File "/usr/lib/python3.12/site-packages/requests/adapters.py", line 700, in send raise ConnectionError(e, request=request) requests.exceptions.ConnectionError: HTTPConnectionPool(host='es1', port=9200): Max retries exceeded with url: / (Caused by NewConnectionError('<urllib3.connection.HTTPConnection object at 0x7f>: Failed to establish a new connection: Errno -3 Try again'))

不能建立新连接,也没说具体哪里不行,问了下 GPT 才知道 Errno -3 不是 urllib 的报错,是 getaddrinfo 定义的,-3 就是域名解析不了。 ( urllib3 v2.0 之后会提示 Failed to resolve )

同节点的容器都运行的好好的,怎么会有解析问题,直接抓包,实际ipv4解析都是成功的,只是ipv6无结果,没毛病啊,咱还没用上 ipv6 呢。

将容器调度到没升级的节点上,顺利启动,抓包看了下解析一模一样,怎么在新节点上就报无法解析了呢?突然发现 ipv6 解析结果有个 refused 标记:

Standard query response 0xfe14 Refused AAAA es1 OPT

看来可能和 ipv6 解析有关系,突然想起 alpine 镜像存在这个毛病,因为它用的是 musl libc (未实现Happy Eyeballs) ,解析域名时会优先把 ipv6 解析排在前面。 赶紧看下容器的基础镜像,果然是 alpine 啊,可是我们没有用 ipv6 啊,进入容器来看,发现升级后的节点,容器里 ipv6 被启用了!怎么回事? 此时想起升级操作系统后, docker 的版本也升级了,从 v20 -> v 27,好家伙,据说 docker 从 v26开始默认启用 ipv6了,官方给的禁用办法是加启动参数:

--sysctl net.ipv6.conf.all.disable_ipv6=1

这很不优雅,想来想去,去其他集群上抓包,ipv6解析返回虽然是空,但是没看到Refsued标记:

Standard query response 0x0181 AAAA es1 OPT

可能是这个拒绝标记捣鬼,马上实验相同容器条件,结果启动成功,看来只要去掉这个拒绝标记就行了。 我们DNS服务是用的dnsmasq,对比了下两个集群的配置,发现有 Refused 标记的少一个配置:

--domain-needed

Tells dnsmasq to never forward A or AAAA queries for plain names, without dots or domain parts, to upstream nameservers. If the name is not known from /etc/hosts or DHCP then a "not found" answer is returned.

明白了,没有点的域名,如 es1 这样的开启这个配置,就不会发给上游,并返回无结果。配上参数再试,果然就没有 Refsued 标记了!

那正常有点的域名如 es1.com 就会发给上游了?结果会 Refused 吗。但是我们内网dnsmasq全是没有上游的啊,两个集群的 dnsmasq 测试结果居然还不一样,一个拒绝,一个无拒绝。 还得问 GPT ,原来 dnsmasq v2.86 实现了 RFC-8914 extended DNS errors (EDE) , 所以这个版本之后都会拒绝,日志里也能看到:

dnsmasq: config error is REFUSED (EDE: not ready)

小于这个版本的就不会有拒绝,是正常的空(实际是NOERROR):

dnsmasq: config es1.com is NODATA-IPv6

嗯,先这样吧,看来要好好学习下 ipv6 了。