本文为原创文章,请注明作者及出处
背景
在运维生涯中,会出现各种故障和问题,如何解决问题成为工作的重点。运维技术文章会从问题点或故障现象入手,一步步分析故障原因,最后寻求问题的解决方案。
问题
这个问题源于开发同事前段时间举报的盐奴工作异常,无法正常解决。重新出现现场跟踪后,很快就被定位为缓存DNS的问题。
当时的现象是缓存的DNS的地址是迁移机房的DNS的IP,但是检查了当前/etc/resolv.conf配置文件中的名称服务器地址是新的机房服务器的IP,Linux系统中/etc/resolv.conf中的DNS配置是正确的,但是salt-minion应用仍然使用了错误的DNS。
然后,在python salt-minion中搜索代码,看看salt是否使用第三方DNS库作为解析。搜索过程中发现salt-minion并没有调用第三方DNS库,而是使用了pythonsocket.getaddrinfo,在python中,socket.getaddrinfo实际上是glibc库getaddrinfo(旧glibc版本中的getbyhostname)的包。
当应用层(salt)使用getaddrinfo函数时,实际上会用到glibc的相关函数。当glibc应用第一个域名解析时,它将触发res_init()函数的调用,该函数用于读取/etc/resolv.conf的内容,例如名称服务器地址、负载平衡策略、重试次数、超时时间等。,并将读取的数据放入static _res_ structure。
由于Linux进程在glibc动态链接库中使用全局静态变量时会在用户进程空之间生成自己的独立变量副本(感兴趣的同学可以查看每个进程的smaps文件,glibc在每个用户进程空之间有可读可写的段),所以每个发起DNS解析的进程都有一个independent _res_ structure变量。
回到salt-minio的具体案例,salt-minion进程在启动后发起第一个域名解析请求,最后会调用底层的res_query。第一个res_query将调用res_init()。res_init()初始化后,nameserver地址被初始化为当前进程的_res_ structure。但是,对于每个进程,res_init(),
操作和维护学生在用户进程运行时修改了/etc/resolv.conf中的配置,但修改后的配置无法生效的根本原因在这里(因为旧的nameserver配置仍然存储在用户进程空之间的_res结构中)。
以下是两个平台对应的glibc的res_init()的描述:
BSD平台下:
res_init()函数是初始化__res_state结构以供其他解析器函数使用的解析器函数。初始化通常发生在第一次调用任何地址解析例程时,通常称为XL C/C++运行时库解析器。res_init()例程通过将__res_state结构传递给z/ OS Resolver的CS来进行初始化。解析器读取“TCPIP”。数据”配置文件,并指定了__res_state结构。_res_state结构中的数据是根据“TCPIP”的内容填充的。数据”配置文件,然后可以在_res变量中引用。Resolverroutines使用的全局配置和状态信息保存在structure _res中。大多数值都有合理的默认值,可以保持不变。
在Linux平台下:
下面描述的函数对来自互联网域名服务器的响应进行查询和解释。该应用编程接口由一组更现代的可重入函数和一组已被取代的旧的不可重入函数组成。传统的解析器接口,如res_init()和res_query()使用存储在_res结构中的一些静态(全局)状态,使得这些函数是非线程安全的。BIND 8.2引入了一组新的接口res_ninit(),res _ nquery(),等等,它们将res_state作为它们的第一个参数,所以您可以使用每线程
解析器状态。res_ninit()和res_init()函数读取配置文件(参见resolv.conf(5))以获取默认域名和名称服务器地址。如果没有服务器,则尝试本地主机。如果没有给定域,则使用与本地主机相关联的域。它可以被环境变量LOCALDOMAIN覆盖。res_ninit()或res_init()通常由对其他函数之一的第一次调用来执行。
Centos6.5的glibc-2.12中的代码:
Res_init()函数调用__res_vinit(),__res_vinit使用res_state数据结构,res_state最后使用__res_state的结构。
解决方案
既然我们知道了问题的根本原因,下面是解决方案:
1.用户进程直接调用底层接口,并定期调用res_init(),以防止文件/etc/resolvov . conf被修改和察觉不到(有一定的实现成本)。
2.直接重启用户进程,让用户进程在第一次做域名解析时触发res_init()(实现成本低)
3.用户进程实现DNS客户端的所有功能(实现成本很高)。
4.通过非常规手段修改用户流程空之间的数据(实施成本很高)。
稍微扩展一下这个问题,假设公司所有业务的配置文件都采用了域名方案,比如redis、memcached、rabbitmq、codis、zookeeper、DB等。,而呼叫端一般以连接池的方式使用这些基本服务,也面临着上述问题。由于故障/迁移等不可控制的原因,无论是需要修改数百个业务配置文件还是
在这种场景下,最好的解决方案是直接使用底层的域名解析函数,而不是java/.NET自带的域名解析函数,当连接池中的连接重新连接时,应用端需要重新执行glibc的res_init(),然后调用res_query()进行域名解析,可以很好的解决上述问题。自主开发的DNS SDK适用于所有长连接的应用场景。一旦发现IP变化,只需修改DNS解析,然后关闭旧IP上的服务,即可完成基本业务切换。
今天就到这里。运行维护后续文章将在沪江工学院发表。请期待!
1.《DNS配置异常 DNS客户端异常如何解决?》援引自互联网,旨在传递更多网络信息知识,仅代表作者本人观点,与本网站无关,侵删请联系页脚下方联系方式。
2.《DNS配置异常 DNS客户端异常如何解决?》仅供读者参考,本网站未对该内容进行证实,对其原创性、真实性、完整性、及时性不作任何保证。
3.文章转载时请保留本站内容来源地址,https://www.lu-xu.com/yule/1233702.html