云原生网络扫雷笔记:alpine镜像与DNS AAAA不得不防的坑

本文涉及的产品
云解析 DNS,旗舰版 1个月
全局流量管理 GTM,标准版 1个月
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
简介: 本文联合作者:@予栖 @遐宇问题的背景时间回溯到两个月之前,我突然被前线同学拉到一个会议上,时间差不多是深夜,一个核心客户突然在会议上反馈:“我们切了流量到alinux3上之后,ingress突然多了很多404报错,你们兼容性是不是有问题?”看到404这个响应,我第一反应就是,这是个纯粹的业务问题,404响应作为HTTP领域最出圈的一个响应码,表征的含义就是“404 Not Found”,得到这个

本文联合作者:@予栖 @遐宇

问题的背景

时间回溯到两个月之前,我突然被前线同学拉到一个会议上,时间差不多是深夜,一个核心客户突然在会议上反馈:

“我们切了流量到alinux3上之后,ingress突然多了很多404报错,你们兼容性是不是有问题?”

看到404这个响应,我第一反应就是,这是个纯粹的业务问题,404响应作为HTTP领域最出圈的一个响应码,表征的含义就是“404 Not Found”,得到这个响应,标志着TCP层面是正常工作的,HTTP层面也可以正常处理,况且用户并非所有的流量都出现了异常,因此我让客户对业务进行排查,但是客户坚持认定,是我们的兼容性问题,于是,我们开始了针对这个问题的排查。

问题的排查过程

查找报错原因

客户在回退发布后,已经没有了现场,好在ingress默认开启了sls记录日志,我们可以很快找到当时出现的404异常的日志分布 :

可以看到,确实存在客户所说的现象,即进行了流量的切换之后,在某个时间点,404的频率有了较大的升高,而在客户进行止血措施之后,频率又迅速回落,随后我们按照多个维度查看404的特征,在进行404现象与upstream addr关系的排查是发现了一个很特别的现象,绝大多数的404响应,其实是local-project这个upstream发出的:

 

这个upstream指向的是127.0.0.1,是default backend,按照经验,不该有很多流量转发到这个backend,于是我们联系客户开启调试日志后进行一次复现,果然发现了问题所在:

nginx: [emerg] host not found in upstream "************.******.cn" 
AI 代码解读

这条核心的日志解释了404的成因: ingress存在部分配置没有正常加载的情况,原因是由于某一些域名在进行域名解析时出现了失败。

随即我们向客户反馈了这个现象,然而客户表示,这个域名是他们一直在使用的域名,不是新增的,其他业务也没有遇到类似的问题,甚至之前的ingress也没有遇到过这个问题。。。

于是我们在客户的环境中,指定coredns作为server进行了测试和验证,验证的结果如下:

  1. 在节点上使用dig测试,可以正常解析。
  2. 切换到ingress容器中进行测试,无论是新增的还是存量的,均无法解析。

也就是说,相同的域名和server,在节点上和在容器中,竟然有表现上的差异!!!

追查差异根因

遇到这样的问题,首先进行了抓包分析,在客户的环境中我们发现了一个现象,当我们发起DNS查询的时候,会出现类似以下的现象:

  1. 同时发起A和AAAA记录的查询。
  2. A记录返回了正常的响应。
  3. AAAA记录返回了NXDOMAIN。

而在没有进行任何配置,直接从控制台创建出来的ACK集群中,他的表现是这样的:

  1. 同时发起A和AAAA记录的查询。
  2. A记录返回了正常的响应。
  3. AAAA没有返回有效的解析结果,但是响应码不是NXDOMAIN。

我们怀疑是这个差异,即AAAA记录返回NXDOMAIN与否,影响了进行dns查询时得到的结果,如果能够让dns的AAAA记录查询返回空的结果而不是NXDOMAIN,这个问题应该就可以解决了。我们排查了客户的CoreDNS配置文件,对比默认的配置,发现客户环境下的配置文件多了以下一段配置:

 

查询文档(https://coredns.io/plugins/template/)后得知,这一段配置生效的作用是:

  1. 对于53端口的服务生效。
  2. 对于所有查询了AAAA记录的请求生效。
  3. 将符合条件的请求的rcode,也就是返回码设置为NXDOMAIN。

于是这个问题也就能解释通了,客户添加的这一段配置,导致了CoreDNS在表现上的差异,我们建议客户参照官网文档对配置进行修改后验证(https://help.aliyun.com/document_detail/380963.html#section-6jf-fgj-j2f),果然,修改了针对AAAA记录的默认行为后,在node和ingress的pod内进行dns查询就都可以正常响应了!!

到这里,问题就转变成了,为什么相同的DNS响应,即A记录正常回复,AAAA记录返回NXDOMAIN,在节点上就没问题,在容器内就无法解析了呢?

差异背后的标准实现

在复现了客户的问题之后,我们收敛了问题的范围:

  1. 对于节点和pod中的dns查询请求,都发出了A和AAAA两条查询的报文。
  2. CoreDNS针对节点和pod发出的dns查询,回复时一样的,即A记录正常返回,AAAA记录返回NXDOMAIN。

在这个基础上,我们讲目光转移到了dns客户端本身,对于节点和pod来说,他们进行dns查询的客户端确实存在着差异:

  1. 对于节点来说,curl等工具是依赖于getaddrinfo()进行域名的解析,节点上默认提供getaddrinfo()的静态库时glibc提供的
  2. 对于以alpine为基础镜像的ingress容器来说,getaddrinfo()则是由musl实现的,musl提供了更加简洁和高效的POSIX标准库实现( https://www.musl-libc.org/intro.html),在嵌入式场景中广泛使用。  

随后我们分析了musl和glibc的代码,发现他们在针对相同的域名,两种不同类型的记录的查询结果有差异,尤其是其中一条的返回码是NXDOMAIN时,的确采取了不同的处理方式:

  1. glibc的处理中,会将所有正常返回的记录结果都提供给调用方。
  2. musl的处理中,针对出现AAAA有NXDOMAIN时,整个域名都会被认为是无法正常解析的,处理逻辑如下:

static int name_from_dns(struct address buf[static MAXADDRS], char canon[static 256], const char *name, int family, const struct resolvconf *conf)
{
    struct dpc_ctx ctx = { .addrs = buf, .canon = canon };
    static const struct { int af; int rr; } afrr[2] = {
        { .af = AF_INET6, .rr = RR_A },
        { .af = AF_INET, .rr = RR_AAAA },
    };

    // 分别获取ipv4和ipv6,也就是A与AAAA的解析记录
    for (i=0; i<2; i++) {
        if (family != afrr[i].af) {
            qlens[nq] = __res_mkquery(0, name, 1, afrr[i].rr,
                                      000, qbuf[nq], sizeof *qbuf);
            if (qlens[nq] == -1)
                return 0;
            qtypes[nq] = afrr[i].rr;
            qbuf[nq][3] = 0/* don't need AD flag */
            /* Ensure query IDs are distinct. */
            if (nq && qbuf[nq][0] == qbuf[0][0])
                qbuf[nq][0]++;
            nq++;
        }
    }
    // 对于每一个记录,如果出现了retcode=3即NXDOMAIN,则会返回没有任何解析结果
    for (i=0; i<nq; i++) {
        if (alens[i] < 4 || (abuf[i][3] & 15) == 2return EAI_AGAIN;
        if ((abuf[i][3] & 15) == 3return 0;
        if ((abuf[i][3] & 15) != 0return EAI_FAIL;
    }

    for (i=nq-1; i>=0; i--) {
        ctx.rrtype = qtypes[i];
        __dns_parse(abuf[i], alens[i], dns_parse_callback, &ctx);
    }

    if (ctx.cnt) return ctx.cnt;
    return EAI_NODATA;
}
AI 代码解读

问题到了这里也就真相大白了:

  1. 由于业务原因,客户主动将一部分域名的AAAA记录设置为NXDOMAIN(尤其是在IPv6双栈的演进过程中)。
  2. 由于musl所采用的处理逻辑,当客户进行设置后,所有基于alpine镜像的C程序针对配置了双栈,即会请求AAAA记录的域名,都会产生无法解析的报错。
  3. 客户进行了变更,然而由于ingress无法验证域名,导致新配置无法加载。
  4. 客户变更结束后进行流量切换,新域名的流量由于配置没有正常加载,被转发到了default的upstream,产生404报错。

问题的背后

在云原生场景下,alpine的使用极为广泛,不仅仅是ingress-nginx,大量的开源镜像都是基于alpine来实现轻量的体积,与此同时,由于IPv6和双栈的不断推进,在双栈的演化过程中,不可避免得会遇到因为兼容性(避免dns反复重试)而进行DNS的层面配置的情况,因此,由于musl的实现与glibc有差异而产生的坑也很有可能在其他客户身上再次出现,不得不防。

在这个问题发现以后,我们特地查询了RFC文档中对AAAA和NXDOMAIN的规范:

  1. AAAA记录设计之初就是为了能够将iPv6地址作为域名解析的结果进行返回  https://www.rfc-editor.org/rfc/rfc3596
  2. NXDOMAIN则在多个rfc中不断完善其描述,在rfc8020中的较新版本对他的补充定义是:
   The DNS protocol [RFC1035] defines response code 3 as "Name Error",
   or "NXDOMAIN" [RFC2308], which means that the queried domain name
   does not exist in the DNS.  Since domain names are represented as a
   tree of labels ([RFC1034], Section 3.1), nonexistence of a node
   implies nonexistence of the entire subtree rooted at this node.

   The DNS iterative resolution algorithm precisely interprets the
   NXDOMAIN signal in this manner.  If it encounters an NXDOMAIN
   response code from an authoritative server, it immediately stops
   iteration and returns the NXDOMAIN response to the querier.
   
   This document clarifies possible ambiguities in [RFC1034] that did
   not clearly distinguish Empty Non-Terminal (ENT) names ([RFC7719])
   from nonexistent names, and it refers to subsequent documents that
   do.  ENTs are nodes in the DNS that do not have resource record sets
   associated with them but have descendant nodes that do.  The correct
   response to ENTs is NODATA (i.e., a response code of NOERROR and an
   empty answer section). 
AI 代码解读

rfc中对NXDOMAIN的定义是针对整个域名的,即NXDOMAIN出现应该是这个域名所有的记录都无法被解析的情况下,同时对这种一部分记录没有的场景增加了建议,推荐返回NODATA,也就是最上方官网文档中建议的配置。

显然,musl的实现是完全遵照rfc的规范实现的,而glibc作为默认的实现,其实并没有严格按照rfc标准进行,但是实际在切换到云原生容器化的过程中,alpine的“符合标准”的行为却实打实的产生了差异,并造成了业务上的风险,这也是我们在容器化过程中不得不防的坑。

目录
打赏
0
0
0
0
0
分享
相关文章
深入解析PDCERF:网络安全应急响应的六阶段方法
PDCERF是网络安全应急响应的六阶段方法,涵盖准备、检测、抑制、根除、恢复和跟进。本文详细解析各阶段目标与操作步骤,并附图例,助读者理解与应用,提升组织应对安全事件的能力。
450 89
深入解析图神经网络注意力机制:数学原理与可视化实现
本文深入解析了图神经网络(GNNs)中自注意力机制的内部运作原理,通过可视化和数学推导揭示其工作机制。文章采用“位置-转移图”概念框架,并使用NumPy实现代码示例,逐步拆解自注意力层的计算过程。文中详细展示了从节点特征矩阵、邻接矩阵到生成注意力权重的具体步骤,并通过四个类(GAL1至GAL4)模拟了整个计算流程。最终,结合实际PyTorch Geometric库中的代码,对比分析了核心逻辑,为理解GNN自注意力机制提供了清晰的学习路径。
172 7
深入解析图神经网络注意力机制:数学原理与可视化实现
【Android】网络技术知识总结之WebView,HttpURLConnection,OKHttp,XML的pull解析方式
本文总结了Android中几种常用的网络技术,包括WebView、HttpURLConnection、OKHttp和XML的Pull解析方式。每种技术都有其独特的特点和适用场景。理解并熟练运用这些技术,可以帮助开发者构建高效、可靠的网络应用程序。通过示例代码和详细解释,本文为开发者提供了实用的参考和指导。
48 15
JS数组操作方法全景图,全网最全构建完整知识网络!js数组操作方法全集(实现筛选转换、随机排序洗牌算法、复杂数据处理统计等情景详解,附大量源码和易错点解析)
这些方法提供了对数组的全面操作,包括搜索、遍历、转换和聚合等。通过分为原地操作方法、非原地操作方法和其他方法便于您理解和记忆,并熟悉他们各自的使用方法与使用范围。详细的案例与进阶使用,方便您理解数组操作的底层原理。链式调用的几个案例,让您玩转数组操作。 只有锻炼思维才能可持续地解决问题,只有思维才是真正值得学习和分享的核心要素。如果这篇博客能给您带来一点帮助,麻烦您点个赞支持一下,还可以收藏起来以备不时之需,有疑问和错误欢迎在评论区指出~
阿里云CDN:全球加速网络的实践创新与价值解析
在数字化浪潮下,用户体验成为企业竞争力的核心。阿里云CDN凭借技术创新与全球化布局,提供高效稳定的加速解决方案。其三层优化体系(智能调度、缓存策略、安全防护)确保低延迟和高命中率,覆盖2800+全球节点,支持电商、教育、游戏等行业,帮助企业节省带宽成本,提升加载速度和安全性。未来,阿里云CDN将继续引领内容分发的行业标准。
71 7
阿里云网络安全体系解析:如何构建数字时代的"安全盾牌"
在数字经济时代,阿里云作为亚太地区最大的云服务提供商,构建了行业领先的网络安全体系。本文解析其网络安全架构的三大核心维度:基础架构安全、核心技术防护和安全管理体系。通过技术创新与体系化防御,阿里云为企业数字化转型提供坚实的安全屏障,确保数据安全与业务连续性。案例显示,某金融客户借助阿里云成功拦截3200万次攻击,降低运维成本40%,响应时间缩短至8分钟。未来,阿里云将继续推进自适应安全架构,助力企业提升核心竞争力。
小红书笔记详情 API 接口:获取、应用与收益全解析
小红书(RED)是国内领先的生活方式分享平台,汇聚大量用户生成内容(UGC),尤以“种草”笔记闻名。小红书笔记详情API接口为开发者提供了获取笔记详细信息的强大工具,包括标题、内容、图片、点赞数等。通过注册开放平台账号、申请API权限并调用接口,开发者可构建内容分析工具、笔记推荐系统、数据爬虫等应用,提升用户体验和运营效率,创造新的商业模式。本文将详细介绍该API的获取、应用及潜在收益,并附上代码示例。
290 13
深入解析:Linux网络配置工具ifconfig与ip命令的全面对比
虽然 `ifconfig`作为一个经典的网络配置工具,简单易用,但其功能已经不能满足现代网络配置的需求。相比之下,`ip`命令不仅功能全面,而且提供了一致且简洁的语法,适用于各种网络配置场景。因此,在实际使用中,推荐逐步过渡到 `ip`命令,以更好地适应现代网络管理需求。
70 11
NAS深度解析:面向云原生应用的文件存储
本文深入解析了面向云原生应用的文件存储NAS,由阿里云专家分享。内容涵盖Cloud Native与AI浪潮下的技术创新,包括高性能、弹性伸缩、成本优化及数据安全等方面。针对云原生应用的特点,NAS在Serverless生态中不断演进,提供多种产品规格以满足不同需求,如极速型NAS、归档存储等,确保用户在高并发场景下获得稳定低延时的存储体验。同时,通过优化挂载参数和容器访问策略,提升整体性能与可用性。
88 11
TCP报文格式全解析:网络小白变高手的必读指南
本文深入解析TCP报文格式,涵盖源端口、目的端口、序号、确认序号、首部长度、标志字段、窗口大小、检验和、紧急指针及选项字段。每个字段的作用和意义详尽说明,帮助理解TCP协议如何确保可靠的数据传输,是互联网通信的基石。通过学习这些内容,读者可以更好地掌握TCP的工作原理及其在网络中的应用。

相关产品

  • 云解析DNS
  • 推荐镜像

    更多
    AI助理

    你好,我是AI助理

    可以解答问题、推荐解决方案等