暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

自建CDN实战经验合集之——Bind9在内部DNS中的应用

运维军团 2017-03-23
2750



1 简介


ISC的Bind一直以来应该算是DNS行业的工业标准。我们的一些业务场景(自建CDN)也使用到了bind9来作为内部dns。本文主要分享一些bind9的线上业务使用经验,具体bind9的详细简介以及安装过程,则不再赘述,有兴趣的看客可以直接查看bind9官方文档。

https://www.isc.org/bind-9-11-arm/



2 内部DNS架构


线上采用bind9组建dns集群,为服务器节点提供解析服务。而每台服务器节点都安装配置了dnsmasq来实现本地解析的高可用性。大致架构如下:




3 DLZ智能解析


3.1 DNSMASQ简介

一般大家都知道在resolv.conf中配置多个nameserver来实现冗余或者高可用性。

nameserver 114.114.114.114
nameserver 223.5.5.5

稍微优化下的话,调整下超时以及使用轮询

nameserver 114.114.114.114
nameserver 223.5.5.5
options timeout:1 rotate

但实际上还是有1s的timeout时间,而这在海量并发下也是存在问题。


我们希望实现的效果是,可以配置多个nameserver,其中任何一个nameserver故障都不会影响到dns的解析,且本地有缓存可以加速dns解析的速度。dnsmasq刚好完美的解决了我们的痛点。


dnsmasq是我们线上经常使用的一个软件,主要看重了dnsmasq作为dns代理时的本地缓存以及高可用性,直接部署到节点服务器上的。


例如:节点部署dnsmasq监听127.0.0.1,而resolv.conf中设置nameserver 127.0.0.1

bind-interfaces
#指定监听的IP地址,一般情况下是本机服务,故只监听127.0.0.1即可
listen-address=127.0.0.1 
#记录日志
log-facility=/var/log/dnsmasq.log
log-queries
#不读取/etc/hosts,在提供内部dns服务时可以注释掉
no-hosts
#设置cache存活时间,2.64新增max-cache-ttl,2.73新增min-cache-ttl
#max-cache-ttl=1800
#min-cache-ttl=600
#缓存的条目数
cache-size=2000
#不缓存失败的查询结果
no-negcache
#不读取/etc/resolv.conf
no-resolv
#同时向所有的server发起查询请求,取响应最快的
all-servers
server=114.114.114.114
server=223.5.5.5

PS:还可以使用server来将不同域名的dns请求转发到不同的上层dnsserver

server=/baidu.com/114.114.114.114
server=/taobao.com/223.5.5.5

注意:dnsmasq缓存上层dns响应是有前提条件的,如果dns响应中Recursive avaiable位为0,那么不会缓存。如果上层是tcp响应,那么也不会缓存。



3.1 mysql dlz智能解析

bind9原生支持mysql dlz。下载获得bind9的源码包后,可以编译启用mysql dlz

./configure --with-dlz-mysql -enable-threads=no
#后面接着其他的选项,bind9支持各种类型的dlz,但是mysql dlz应用范围应该说相对比较广,大家也都比较熟悉mysql。

注意:myql dlz默认仅支持单进程,注意开启enable-thread=no,或者启动named进程的时候指定-n1


bind9实现dns智能解析最关键的就是acl以及view配置。通过acl将用户分成一个个独立的群组。而view中则定义匹配了哪些群组,以及针对这些群组要进行的操作(使用不同的sql查询mysql数据库)。


下面以自定义联通线路来具体展示acl和view的配置方法:


定义wt acl

#定义了wt acl,所有来源IP为120.192.0.0/16的请求都归到wt这个群组(严格意义上来,根据的是用户端出口DNS,而不是用户的IP地址)
acl "wt" {
12.22.0.0/16;
}

定义wt key

#wt指的是key的name,可以手工指定
rndc-confgen -r dev/urandom  -k wt
#生成如下key信息
key "wt" {
algorithm hmac-md5;
secret "XXXXX";
};

配置wt view

bind9 mysql dlz一般有两种方式,一种是采用自带的mysql驱动,通过--with-dlz-mysql启用(本文使用)。还有一种方式则是额外编译安装mysql驱动。

view "view_wt" {
      #匹配wt acl
  match-clients { key wt; wt; };
  dlz "Mysql zone" {
   database "mysql
       #定义mysql的连接信息
   {host=127.0.0.1 dbname=mydns ssl=false port=3306 user=root pass=XXX }
   {select zone from dns_records where zone = '$zone$' and view='CNC' limit 1}
   {select  ttl, type, mx_priority, case when lower(type)='txt' then concat('\"', data, '\"') when lower(type) = 'soa' then  concat_ws (' ', data, resp_person, serial, refresh, retry, expire, minimum) else data end as mydata from dns_records where zone = '$zone$' and host = '$record$' and (view = 'CNC' or view = 'DF') and in_use = 1}
   {}
   {select  ttl, type, host, mx_priority, case when lower(type)='txt' then concat('\"', data, '\"') else data end as mydata, resp_person, serial, refresh, retry, expire, minimum from dns_records where zone = '$zone$' and view='CNC' and in_use = 1 }";
   };
};

dns_records建表语句

CREATE TABLE `dns_records` (
  `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
  `zone` varchar(255) NOT NULL,
  `host` varchar(255) NOT NULL DEFAULT '@',
  `type` enum('MX','CNAME','NS','SOA','A','AAAA','PTR') DEFAULT NULL,
  `data` varchar(255) DEFAULT NULL,
  `ttl` int(11) NOT NULL DEFAULT '300',
  `view` char(20) DEFAULT 'DF',
  `mx_priority` int(11) DEFAULT NULL,
  `refresh` int(11) NOT NULL DEFAULT '3600',
  `retry` int(11) NOT NULL DEFAULT '3600',
  `expire` int(11) NOT NULL DEFAULT '86400',
  `minimum` int(11) NOT NULL DEFAULT '3600',
  `serial` bigint(20) NOT NULL DEFAULT '2008082700',
  `resp_person` varchar(64) NOT NULL DEFAULT 'root.domain.com.',
  `primary_ns` varchar(64) NOT NULL DEFAULT 'ns1.domain.com.',
  `data_count` int(11) NOT NULL DEFAULT '0',
  `in_use` tinyint(1) NOT NULL DEFAULT '1',
  PRIMARY KEY (`id`),
  KEY `type` (`type`),
  KEY `host` (`host`),
  KEY `zone` (`zone`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8

其他自定义线路的配置类似。


压测单进程bind9 mysql dlz的性能在3K左右,对于一般的应用场景性能已经足够了。如果对于可用性有较高的要求的话,则可以两台机器分别搭建mysql dlz,同时mysql进行主主数据同步。可以保证任何一台dns server宕机的情况下都不会影响到管理员对解析的修改以及正常查询响应。


如果对性能有较高的要求的话,则可以在一台master下面配置多个slave,从master中同步数据,同时使用lvs或者nginx udp反向代理统一对外服务。


个人感觉lvs使用还是有一定的要求的且配置较麻烦些。而bind9完全可以启用多个实例,监听不同的端口,配合前端使用nginx udp反向代理来实现bind9的高性能,高可用以及统一对外服务。

由于bind9的acl使用的是dns请求的源IP地址来作为分类依据,那么经过nginx udp反向代理一转的话,源IP地址全部会丢失,自然也无法实现智能解析了。这个问题可以使用ECS DLZ来进行规避。


4 ECS DLZ智能解析


4.1 ECS简介

EDNS0(Extension Mechanisms for DNS Version 0)是DNS在rfc1035基础上对DNS协议的扩展。主要在现有的DNS消息格式的基础上增加一些字段(OPT RR),来支持更多的DNS请求业务。同时保证向后兼容性。


传统的DNS智能解析,权威dns服务器实际上只能获取用户出口DNS,获取不到用户的IP地址,故智能解析实际上是按照递归DNS的IP地址进行解析的。


ECS(edns-client-subnet),由谷歌提交的DNS拓展协议,允许DNS resolver传递用户的ip地址给authoritative DNS server。使得DNS基于用户IP进行精准解析有了可能性。



4.2 ECS BIND9

edns-client-subnet(简称ecs)现在还没有正式被bind支持。需要对bind重新编译,ISC的git上有支持ecs authoritative的源码,git克隆到到本地编译即可。

git clone https://source.isc.org/git/bind9.git
cd bind9/
./configure --with-dlz-mysql  --prefix=/usr/local/bind9 
make && make install

注意:ecs的区别主要体现在acl规则中,在需要支持的ecs的规则前面,增加ecs关键字就可以

acl localnet{
    ecs 120.0.0.1/24;
};

其他配置不需要变动,使用dig命令进行测试

dig XXX.com +subnet=A.B.C.D @119.29.29.29

bind9日志

查看日志虽然记录的clientip还是dns请求的源ip地址,但是可以看到bind9已经获取到client subnet并且成功响应。



4.3 ECS DnsProxy

目前我们的bind9服务器已经配置支持了ecs解析,且前端采用nginx udp进行反向代理,负载均衡。但传统的dnsmasq不支持ecs,如果在节点继续使用dnsmasq的话,那么就无法发挥ecs的优势(bind9无法获取用户的ip,而非ecs的方式获取的是nginx代理的ip)。


我们在第三方开源库的基础上稍微修改实现了ednsproxy程序,有以下功能:

  • 支持多个上游dns服务器

  • 自带本地缓存,成功的响应缓存在本地,下次客户端请求,有效期内,可以直接响应给用户

  • 支持普通dns请求到ecs dns请求的转换

  • 单机性能>3万qps,满足单机dns缓存代理的性能要求



4.3.1 下载地址

github

https://github.com/zhxiaom5/ednsproxy



4.3.2 使用教程

Usage of ./dnsproxy:
#默认跳过ipv6解析
-6    skip ipv6 record query AAAA (default true)  
#是否开始缓存,默认不开启  
-cache
        enable go-cache 
#debug level,默认不开启  
-debug int
        debug level    
#上游dns服务器,可以支持udp和tcp 
-dns ,
        dns address, use , as sep (default "114.114.114.114:53:udp,119.29.29.29:53:udp")  
#ecs ip的地址
-ecsip string
        ecs ip address (default "127.0.0.1") 
#ecs ip的netmask
-ecsnetmask int
        ecs netmask (default 32) 
#缓存过期时间
-expire int
        default cache expire seconds, -1 means use doamin ttl time (default 60) 
#缓存文件
-file string
        cached file (default "cache.dat") #
#监听地址
-local string
        local listen address (default ":53")
#失败响应不缓存
-negcache
        enable negcache
#超时时间
-timeout int
        read/write timeout in ms (default 200)

运行示例:

./dnsproxy -dns 119.29.29.29:53:udp -cache=true -negcache=true -ecsip 121.9.212.177 -ecsnetmask 32



5 总结


本文主要简述了使用bind9进行智能解析并且采用ecs进行横向扩展的一种方式。前端采用bind9作为权威dns,后端采用mysql作为记录的存储数据库。


当自建CDN量级较小的时候,内部回源完全可以采用上述的方案,即使用内部自建DNS解析源机IP进行回源,与公网解析进行隔离,来实现源机安全和灵活调度。前端可以对应开发一套DNS的管理界面。



当自建CDN量级逐渐增加,边缘节点数越来越多的时候,继续单纯采用内部bind9自建DNS来实现回源系统的方式就很难维持下去了。


一方面因为bind mysql dlz的性能原因,横向扩展不可能无限进行下去。另一方面也是随着边缘节点分布的越来越广,回源网络的差异性也越来越大。即使是同一线路的节点,也不可能将所有的边缘节点回源指向同一台源机。


后面会另出一篇文章来详细阐述大型CDN如何设计内部回源DNS系统。有兴趣的看客,可以继续关注公众号。






END



全中国只有不到1% 的人关注了运维军团

你是个有眼光的人!



(由于交流群人数已超100人,需要进群的小伙伴可以添加运维小编的微信:qq834775039




如果你喜欢我们的文章,请转发到朋友圈

 

公众号

ywjtshare

运维军团

 

专注运维技术与传承,分享丰富原创干货



文章转载自运维军团,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

评论