一、dnspod-sr  介绍

    dnspod-sr: A faster recursive dns server from DNSPod。 dnspod-sr 是一个运行在 Linux 平台上的高性能的递归 DNS 服务器软件,强烈公司内网或者服务器内网使用dnspod-sr,具备高性能、高负载、易扩展的优势,非 BIND、powerdns 等软件可以比拟。

    dnspod-sr 是递归 DNS 程序,功能类似于 BIND ,但是性能较 BIND 高出数倍。可以通过架设 dnspod-sr 集群,替换各大运营商目前基于 BIND 的陈旧方案,减少运营成本;也可以用于公司、学校、政府等组织内部 DNS,解析外部不可见的私有域名,提高上网速度。其中的基础模块也可以在其他项目上使用。

    此次开源的是递归 DNS 代码,而非授权 DNS 代码。这意味着 dnspod-sr 只能作为本地递归DNS服务器,也就是说 dnspod-sr 不具备授权功能。

1.1 dnspod-sr 特性


  1. 高性能,比所有流行的开源 DNS 软件性能高出2倍以上

  2. 安全,能抵御一般***

  3. 稳定,有效降低解析失败率

  4. 主动刷新缓存,响应速度更快

  5. 易于扩展,非常容易部署

  6. 防污染,能够正确解析被污染域名

1.2 dnspod-sr 性能

    dnspod-sr 依托于 DNSPod 多年运营和优化 DNS 服务的经验,针对国内复杂的网络情况,对递归 DNS 进行了一系列的优化,比较其他开源软件,性能得到大幅提升。

    这时dnspod-sr官方站点给出的测试数据:


测试环境
    千兆网卡,4核 CPU,4G 内存,Linux 64位系统。
性能测试

  •     dnspod-sr: 15万 qps

  •     BIND 9.9: 7万 qps

  •     unbound 4.7: 8万 qps

测试结果如图:

企业内部轻量级DNS首选方案: dnspod-sr_第1张图片

1.3 功能特点

  1. CNAME解析加速:在解析过程中,dnspod-sr会首先循环查找本地缓存的CNAME记录,如果解析到CNAME记录,再去解析最后一级CNAME记录值的指定记录类型,在解析请求存在CNAME时可以省却递归解析过程,大大加快解析速度。详见 author.c 的  find_record_from_mem() 接口。

  2. A记录组包缓存:根据我们的解析日志统计,DNS解析请求超过90%都是请求的A记录(即IPv4地址),所以dnspod-sr针对此进行了一项优化,如果授权返回了A记录,则会将该A记录提前组好应答包存储到本地缓存中,在客户端下次请求该记录时省去了组包的过程,加速解析过程。但该功能在前一版本中有时候会出现解析错误的问题,在新版本中暂时移除了该功能,待修正解析错误的问题后会再下一版本重新增加该功能。

  3. 请求转发功能:通过配置 sr.conf 文件的 xfer: 模块,可以设置在解析指定的域名时直接将该解析请求发送到指定的解析服务器(可以是递归服务器也可以是授权服务器)进行解析。详见 dns.c 的pre_find() 接口。

  4. 缓存刷新功能:该功能主要用于手动强制刷新dnspod-sr本地缓存中的记录值,主要用于刷新一些被污染的记录或者TTL时间很长但又更新了记录需要快速更新生效时。需要配合 tool 目录下的客户端工具使用。

  5. HASH表缓存:dnspod-sr的本地缓存方式使用了比较流行的HASH表方式,所有数据缓存在内存中,不进行数据库的相关操作,另外quizzer列表也使用HASH表方式。详见 storage.c 中的相关接口。

  6. 内存池:内存池是新版新加的功能,主要是减少在缓存查找和递归解析整个过程中频繁的内存分配和释放操作,提高性能。详见 memory.c 中的相关接口。

  7. 系统函数重写:因为系统函数的性能问题,所以对部分系统函数进行了自行实现,如大小写转换和域名有效性检查改为使用查表法进行;字符串比较和ip地址转换也进行了重新实现等。

1.4 解析过程

企业内部轻量级DNS首选方案: dnspod-sr_第2张图片

    上图是一次典型的域名解析过程,如果dnspod-sr已经在本地缓存了解析结果,则会直接返回解析结果,如果没有缓存结果,则会从根开始逐级递归进行解析,递归解析过程可以参考 dig +trace  的解析结果,当然如果中间某一级的结果已经在本地缓存中存在,则会直接从该级进行递归解析。

    在典型的解析过程之外,尚存在一些其他一些非典型操作,如在第一个quizzer线程中,每次循环都会检查用于自动刷新的记录的TTL时间,如果TTL剩余时间小于3秒,就该将记录添加到quizzer列表中,在下次解析时进行刷新。但是目前自动刷新功能尚存在问题,暂时关闭了所有解析记录的自动刷新功能,只对root.z文件中的记录进行刷新。

1.5 尚存在的问题

目前dnspod-sr尚在修改维护中,还存在一些如下问题,将会在之后的版本更新中进行解决,如:

  1. 不支持PTR反解析;

  2. 增加配置参数文件,解决现在需要通过修改代码修改配置的问题,如fetcher和quizzer线程数等,并可以通过动态加载部分参数实现不停机修改部分配置;

  3. 在最近更新中因存在问题而去掉的A记录提前组包功能;

  4. 红黑树如果自动刷新所有记录尚存在问题;

  5. 代码整理、包括风格、函数调用和无用代码清理等;

  6. 增加在程序崩溃时将缓存记录转储到磁盘的功能;

  7. 较老版本内核和32位系统的兼容性测试等。



二、dnspod-sr  快速开始

2.1 dnspod-sr 安装


下载源码(推荐):

# git clone https://github.com/DNSPod/dnspod-sr.git
# cd dnspod-sr

或者下载压缩包:

# wget https://github.com/DNSPod/dnspod-sr/zipball/master

编译源码

# cd dnspod-sr/src
# make
# 不需要 make install 步骤, 安装过程很快速

运行

# pwd : dnspod-sr/src
./dnspod-sr

备注:切记一定要在相对路径下执行


程序报了一个异常: set affinity fetcher failed, may be the cpu cores num less than (FETCHER_NUM + QUIZZER_NUM + 1)

set affinity quizzer failed, may be the cpu cores num less than (FETCHER_NUM + QUIZZER_NUM + 1)
[DBG:] dnspod-sr is successful running now!!
[DBG:] max_ele_size is 1000000 - 1808
[DBG:] server may contain 332730 useful records
[DBG:] hash_table_size is 65536
[DBG:] we have 10 hash tables
[DBG:] we have 2 fetchers,2 quizzers

在虚拟机测试的时候出现这个问题,难道CPU必须是双核才可以么?

大致看了一下dnspod-sr托管在github上的源码,上面的输出是 dnspod-sr/src/init.c文件的 create_author 函数产生:

for(i = 0;i < QUIZZER_NUM ;i ++)
{
   CPU_ZERO(&cpuinfo);
   CPU_SET_S(i + FETCHER_NUM + 1, sizeof(cpuinfo), &cpuinfo);
   if(0 != pthread_setaffinity_np(apt[i], sizeof(cpu_set_t), &cpuinfo))
   {
    printf("set affinity quizzer failed, may be the cpu cores num less than (FETCHER_NUM + QUIZZER_NUM + 1)\n");
//   exit(0);
   }
}

可以根据机器CPU实际情况,试试修改一下 src/author.h 头文件:

enum {
    FETCHER_NUM = 2,
    SERVER_PORT = 53,
};

然后重新,编译一次

# make clean
# make

实际上就是强制 fetcher 跑在0和1两个CPU上,没有双核实际上还是可以跑,只是强制把第二个线程强制在CPU1这个失败了,还是会跑在CPU0上面。

dnspod-sr内存占用情况?

    基本上是维持了一个比较稳定的范围,1G 左右。 所以建议服务器内存至少2G以上。


本文很多内容参考: 网站:运维生存时间   网址:http://www.ttlsa.com/linux/dnspod-sr-little-dns/

2.2 dnspod-sr 配置

    一、如果你仅仅需要一个dns转发器(DNS缓存服务器),那么什么都不需要配置,直接可以使用。

[root@skype ~]# dig www.baidu.com @127.0.0.1

; <<>> DiG 9.8.2rc1-RedHat-9.8.2-0.30.rc1.el6_6.2 <<>> www.baidu.com @127.0.0.1
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 34337
;; flags: qr ra; QUERY: 1, ANSWER: 3, AUTHORITY: 0, ADDITIONAL: 0

;; QUESTION SECTION:
;www.baidu.com.                 IN      A

;; ANSWER SECTION:
www.baidu.com.          689     IN      CNAME   www.a.shifen.com.
www.a.shifen.com.       320     IN      A       61.135.169.121
www.a.shifen.com.       320     IN      A       61.135.169.125

;; Query time: 781 msec
;; SERVER: 127.0.0.1#53(127.0.0.1)
;; WHEN: Sun Apr 19 00:33:35 2015
;; MSG SIZE  rcvd: 90

    二、但是如果你想解析自己的域名,修改dnspod-sr上层目录的 root.z, 修改前,请注意备份。

增加 NS / A 记录:

企业内部轻量级DNS首选方案: dnspod-sr_第3张图片

添加完成后, 需要重新启动 ./dnspod-sr, 才能生效。

备注:经过测试发现它不支持CNAME、官方文档几乎是空白。无奈 root.z的最后一行一定要一个空行,否则最后一条记录解析不到。

企业内部轻量级DNS首选方案: dnspod-sr_第4张图片

企业内部轻量级DNS首选方案: dnspod-sr_第5张图片

指定NS服务器
  也就是配置DNS转发(forward),将非本区域负责解析的请求转发到其指定的DNS服务器。需要修改配置文件:
sr.conf。dnspod-sr 默认配置文件为当前目录下的 sr.conf,也可以在命令行参数中指定

企业内部轻量级DNS首选方案: dnspod-sr_第6张图片

集群式部署

  DNSPOD-SR说明文档里面提到支持集群,也没看出怎么支持集群,如果你想实现集群,同步 root.z 文件,前端可以用 lvs、haproxy、keepalived 等等来实现集群,方法很多,不在多说了。

三、DNS 性能测试

    bind 自带的 queryperf 可以比较方便的对DNS服务器进行性能测试,queryperf的安装比较简单,直接在bind-9.x.x/contrib/queryperf 下 ./configure && make 就可以编译好。

## 安装
1、下载 bind-9.x.x
# wget http://www.isc.org/downloads/file/bind-9-8-7rc2/?version=tar.gz

不知道为什么,这个文件下载完成后的名字是这个,index.html\?version\=tar.gz,改个名字,然后解压。
2、解压
# mv index.html\?version\=tar.gz bind-9-8-7.tar.gz
# tar xf bind-9-8-7.tar.gz

切换到 contrib 目录,bind自带的第三方软件全在这个目录里面,我们要用到的queryperf也在里面。
3、安装 切换到:bind-9.8.7/contrib/queryperf
# ./configure && make

4、生成queryperf可执行文件
# cp queryperf /usr/bin/

使用queryperf测试

    README 里有比较详细的使用说明。在测试前需要准备一个域名列表,可以使用 vim 命令 :1,$y 多复制几次,创建一个比较大的文件 。比如

# vim list.txt
xx1.dev.net  A
xx2.dev.net  A
xx3.dev.net  A

…………..
xxn.baidu.com  A

然后使用queryperf -d dnsfile -s DNS_SERVER就能进行测试了。queryperf 命令语法格式:

# queryperf [-d datafile] [-s server_addr] [-p port] [-q num_queries]

-d: 后面接上一个文件,文件的内容是用户对DNS的请求,一行为一条请求,所以为了测试,我们可以在里面写上几千几万条。
-s: DNS服务器地址
-p: DNS服务器端口
-q: 请求多少次


## 性能测试
# queryperf -d list.txt -s 192.168.3.133

这是虚拟机测试的结果:

Statistics:

  Parse input file:     once
  Ended due to:         reaching end of file

  Queries sent:         10000 queries        # 发起多少次查询请求
  Queries completed:    10000 queries        # 完成多少次查询请求
  Queries lost:         0 queries
  Queries delayed(?):   0 queries

  RTT max:              7.493286 sec
  RTT min:              0.000014 sec
  RTT average:          0.024813 sec
  RTT std deviation:    0.193285 sec
  RTT out of range:     2 queries

  Percentage completed: 100.00%
  Percentage lost:        0.00%

  Started at:           Sun Apr 19 09:38:52 2015
  Finished at:          Sun Apr 19 09:40:26 2015
  Ran for:              93.079442 seconds

  Queries per second:   107.435109 qps        # 每秒完成多少次请求


1、使用queryperf作性能测试时,最好测试多次,取平均值。

2、可以修改配置文件的部分参数测试,如,开启递归,开启查询日志等功能作测试。

3、其它开源测试工具,tcpcopy

四、稳定性

    Linux的后台进程运行有好几种方法,例如 nohup,screen等,但是,如果是一个服务程序,要可靠地在后台运行,我们就需要把它做成daemon,最好还能监控进程状态,在意外结束时能自动重启。

    supervisor就是用Python开发的一套通用的进程管理程序,能将一个普通的命令行进程变为后台daemon,并监控进程状态,异常退出时能自动重启。

    ./dnspod-sr 需要这种机制来保证其稳定性。这对于很多服务端程序来说是十分必要的,没有人愿意在深夜2点的时候从被窝里爬出来重新启动 ./dnspod-sr 。

Ubuntu/Debian 系列:

$ sudo apt-get install supervisor

配置文件目录:
在/etc/supervisor 目录下有supervisord.conf 文件

CentOS系列安装 supervisor

第一步:根据 python 版本下载对应的 setuptools
# python -V
Python 2.6.6

# wget http://pypi.python.org/packages/2.6/s/setuptools/setuptools-0.6c11-py2.6.egg#md5=bfa92100bd772d5a213eedd356d64086

直接安装
# sh setuptools-0.6c11-py2.6.egg

第二步:下载并安装supervisor
# wget http://pypi.python.org/packages/source/s/supervisor/supervisor-3.0b1.tar.gz
# tar xf supervisor-3.0b1.tar.gz
# cd supervisor-3.0b1
# python setup.py install

## 也可以通过 setuptools安装: easy_install supervisor

第三步:生成 supervisor 配置文件
# mkdir /etc/supervisor/conf.d
# echo_supervisord_conf > /etc/supervisord.conf
# vim /etc/supervisord.conf

1、可以开启web访问,这样可以直接通过浏览器观察和控制进程
取消以下的注释,并修改IP为0.0.0.0
[inet_http_server]      ; inet (TCP) server disabled by default
port=0.0.0.0:9001       ; (ip_address:port specifier, *:port for all iface)
username=admin         ; (default is no username (open server))
password=123         ; (default is no password (open server))

2、注释 include 语句

[include]
files = /etc/supervisor/conf.d/*.conf

然后,给我们的应用程序编写一个配置文件,让supervisor来管理它。每个进程的配置文件都可以单独分拆,放在/etc/supervisor/conf.d/目录下,以.conf作为扩展名,如果修改了 /etc/supervisord.conf ,,需要执行 supervisorctl reload (重启)来重新加载配置文件,否则会感觉没有生效,折腾到抓狂。。。

添加自定义被监控的进程[program:XXXXX]
# vi /etc/supervisor/conf.d/dnspod-sr.conf

[program:dnspod-sr]
command=/root/dnspod-sr/src/dnspod-sr
directory=/root/dnspod-sr/src
user=root
autorestart=true
startretries=10

command是命令,directory是进程的当前目录,user是进程运行的用户身份。

设置 supervisord 启动脚本

# vi /etc/init.d/supervisord
 
#! /bin/sh
PATH=/sbin:/bin:/usr/sbin:/usr/bin
PROGNAME=supervisord
DAEMON=/usr/bin/$PROGNAME
CONFIG=/etc/$PROGNAME.conf
PIDFILE=/tmp/$PROGNAME.pid
DESC="supervisord daemon"
SCRIPTNAME=/etc/init.d/$PROGNAME
# Gracefully exit if the package has been removed.
test -x $DAEMON || exit 0
 
start()
{
        echo -n "Starting $DESC: $PROGNAME"
        $DAEMON -c $CONFIG
        echo "..."
}
stop()
{
        echo -n "Stopping $DESC: $PROGNAME"
        supervisor_pid=$(cat $PIDFILE)
        kill -15 $supervisor_pid
        echo "..."
}
case "$1" in
  start)
        start
        ;;
  stop)
        stop
        ;;
  restart)
        stop
        start
        ;;
  *)
        echo "Usage: $SCRIPTNAME {start|stop|restart}" >&2
        exit 1
        ;;
esac
exit 0


# chmod +x /etc/init.d/supervisord

重启supervisor,让配置文件生效,service supervisord restart ,然后运行命令supervisorctl启动进程:

# supervisorctl start dnspod-sr

查看状态:

# supervisorctl status

停止进程:

# supervisorctl stop dnspod-sr

之前配置文件开启了web访问,这样可以直接通过浏览器观察和控制进程(Restart, Stop, Clear Log),非常方便。

通过 http://IP:9001 即可访问

企业内部轻量级DNS首选方案: dnspod-sr_第7张图片

【注意】:当supervisord以非daemon方式运行时,杀掉supervisord后,被监控的进程也退出了。而以daemon方式运行,杀掉supervisord对被监控进程无影响。

测试效果:

## Session1中, 我们监控 supervisord 的日志:
[root@skype ~]# tail -f /tmp/supervisord.log 
2015-04-19 12:33:04,763 INFO stopped: dnspot-sr (terminated by SIGTERM)
2015-04-19 12:44:36,849 CRIT Supervisor running as root (no user in config file)
2015-04-19 12:44:36,850 WARN Included extra file "/etc/supervisor/conf.d/dnspod-sr.conf" during parsing
2015-04-19 12:44:36,935 INFO RPC interface 'supervisor' initialized
2015-04-19 12:44:36,936 INFO RPC interface 'supervisor' initialized
2015-04-19 12:44:36,936 CRIT Server 'unix_http_server' running without any HTTP authentication checking
2015-04-19 12:44:36,937 INFO daemonizing the supervisord process
2015-04-19 12:44:36,938 INFO supervisord started with pid 10353
2015-04-19 12:44:37,944 INFO spawned: 'dnspod-sr' with pid 10354
2015-04-19 12:44:38,946 INFO success: dnspod-sr entered RUNNING state, process has stayed up for > than 1 seconds (startsecs)

## Session2 中, 我们给 dnspod-sr发送中断信号, kill 10354
[root@skype ~]# kill 10354

## Session1 中,日志变化:
[root@skype ~]# tail -f /tmp/supervisord.log 
2015-04-19 12:33:04,763 INFO stopped: dnspot-sr (terminated by SIGTERM)
2015-04-19 12:44:36,849 CRIT Supervisor running as root (no user in config file)
2015-04-19 12:44:36,850 WARN Included extra file "/etc/supervisor/conf.d/dnspod-sr.conf" during parsing
2015-04-19 12:44:36,935 INFO RPC interface 'supervisor' initialized
2015-04-19 12:44:36,936 INFO RPC interface 'supervisor' initialized
2015-04-19 12:44:36,936 CRIT Server 'unix_http_server' running without any HTTP authentication checking
2015-04-19 12:44:36,937 INFO daemonizing the supervisord process
2015-04-19 12:44:36,938 INFO supervisord started with pid 10353
2015-04-19 12:44:37,944 INFO spawned: 'dnspod-sr' with pid 10354
2015-04-19 12:44:38,946 INFO success: dnspod-sr entered RUNNING state, process has stayed up for > than 1 seconds (startsecs)
### 注意下面的内容
2015-04-19 12:47:20,141 INFO exited: dnspod-sr (terminated by SIGTERM; not expected)
2015-04-19 12:47:21,146 INFO spawned: 'dnspod-sr' with pid 10365
2015-04-19 12:47:22,150 INFO success: dnspod-sr entered RUNNING state, process has stayed up for > than 1 seconds (startsecs)


最后

  在内网使用dnspod-sr值得推荐,配置简单,集群搭建也简单。相比bind等等要简单很多,但是功能也简单。但是内网下足够用了。dnspod-sr的wiki基本上空白,官方文档没本文详细。官方文档只包含了安装,并未提到如何配置。


参考文章:

DNSPOD-SR Wiki:  https://github.com/DNSPod/dnspod-sr/wiki

TTLSA:http://www.ttlsa.com/html/4071.html

网站:运维生存时间   网址:http://www.ttlsa.com/linux/dnspod-sr-little-dns/