一个轻量级的域名解析服务器软件--dnsmasq

一、前言

最近,在研究Hyperledger Fabric的多机部署。因为docker容器分布在多台机器上,解决相互之间通信问题的方案通常有以下几种:

  1. K8s,虽然k8s也可以解决这个问题,但是k8s主要用于高可靠性,配置也比较复杂,学习曲线比较陡峭。
  2. overlay网络。Docker提供了一个原生的overlay网络,可以将多台机器视为同一个网络,该网络内部的容器可以直接使用名称通信(内部有dns解析和转发)。它一般使用consul作底层数据库。但由于它对linux内核有要求(大于3.16),并且在实际测试的过程中未知原因有几台机器通信失败,所以只好放弃。(个人猜测也无法阻止单点故障。)
  3. 自定义一个局域网的域名服务器(因为超级账本一般部署在局域网或者虚拟机上)。也是最简单的方法,可以配置多个dns服务器来防止单点故障。
  4. docker-compse配置文件中提供域名解析,因为是固定的无法增减,不可取。
  5. docker swarm等其它方式。

本文综合网上dnsmasq相关文章,对使用andyshinn/dnsmasq部署一个局域网内的域名解析服务进行了学习研究。针对Mac和Centos 7.3操作系统分别进行了测试,并测试了一些细节问题。

二、什么是dnsmasq

那什么是dnsmasq呢,它的文档(2020-04-5更新)中这样描述的:

dnsmasq is a lightweight DNS, TFTP, PXE, router advertisement and DHCP server. It is intended to provide coupled DNS and DHCP service to a LAN.

那么这句话是什么意思呢,我们google翻译一下:

dnsmasq是轻量级的DNS,TFTP,PXE,路由器通告和DHCP服务器。它旨在向局域网提供耦合的DNS和DHCP服务

可以看出它不仅能提供DNS服务,还可以提供DHCP服务,当然我们这里只需要DNS服务就行了。

三、dnsmasq的优点

这里个人总结一下dnsmasq用于傻瓜化操作的一些优点:

  • 轻量级,镜像大小只有6M
  • 自带viping工具,方便测试
  • Mac OS下它默认会载入宿主机器的/etc/hosts文件,不再需要额外的mount
  • Mac OS下它会跟踪/etc/hosts并会自动更新,你可以随时向宿主机器的/etc/hosts添加新的域名解析,然后就OK了。
  • 启动时有多种OPTIONS可供选择,也可以启动后再配置,满足不同的需求。

当然,它还有很多优点,这里是它的文档地址 DNSMASQ文档,大家有兴趣的可以读一读。

四、部署DNS服务

这里我们采用docker而不是可执行二进制文件的形式部署dnsmasq

4.1、拉取dnsmasq

这里我们拉取andyshinn/dnsmasq,虽然文档建议增加标签,但是我们直接拉取latest就行。

docker pull andyshinn/dnsmasq

4.2、获取本机IP

运行ifconfig,Mac系统中有如下类似输出:

en0: flags=8863<UP,BROADCAST,SMART,RUNNING,SIMPLEX,MULTICAST> mtu 1500
	options=400<CHANNEL_IO>
	ether f0:18:98:2b:a8:1c 
	inet6 fe80::488:3af:3961:e19c%en0 prefixlen 64 secured scopeid 0x6 
	inet 192.168.1.3 netmask 0xffffff00 broadcast 192.168.1.255
	inet6 240e:fe:1630:4800:1c0d:41b0:55ce:1427 prefixlen 64 autoconf secured 
	inet6 240e:fe:1630:4800:a0ea:b052:8ca:bb4b prefixlen 64 autoconf temporary 
	nd6 options=201<PERFORMNUD,DAD>
	media: autoselect
	status: active

可以看出本机IP为192.168.1.3,当然,如果在Mac机器上运行,也可以直接在网络连接中找到本机IP。

Centos系统下有类似如下输出:

eth0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500
        inet 192.168.0.202  netmask 255.255.255.0  broadcast 192.168.0.255
        ether a2:f3:78:ba:0e:ae  txqueuelen 1000  (Ethernet)
        RX packets 3257  bytes 385345 (376.3 KiB)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 243  bytes 24432 (23.8 KiB)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

可以看出本机IP为192.168.0.202

4.3、启动dnsmasq

未知原因,在Centos操作系统和Mac OS操作系统表现不一致。下面分环境进行介绍:

4.3.1、Mac OS系统

使用下面的命令来启动dnsmasq:

docker run -d --restart=always --name dns-server -p 53:53/tcp -p 53:53/udp --cap-add=NET_ADMIN andyshinn/dnsmasq --log-facility=-
docker ps -a

这里会得到类似的输出:

CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS              PORTS                                    NAMES
82012eee8714        andyshinn/dnsmasq   "dnsmasq -k --log-fa…"   7 seconds ago       Up 6 seconds        0.0.0.0:53->53/tcp, 0.0.0.0:53->53/udp   dns-server

这表明我们的dns-server已经启动成功了。让我们来看一下它的日志:

docker logs dns-server

这里会得到类似的输出:

dnsmasq[1]: started, version 2.81 cachesize 150
dnsmasq[1]: compile time options: IPv6 GNU-getopt no-DBus no-UBus no-i18n no-IDN DHCP DHCPv6 no-Lua TFTP no-conntrack ipset auth no-DNSSEC loop-detect inotify dumpfile
dnsmasq[1]: reading /etc/resolv.conf
dnsmasq[1]: using nameserver 192.168.65.1#53
dnsmasq[1]: read /etc/hosts - 7 addresses

这里dnsmasq本身也会有上一级域名解析服务器,这里为192.168.65.1,我们不管它,因为我们主要应用于docker内的域名解析。这里read /etc/hosts - 7 addresses代表它的容器内部的etc/hosts有七条记录(注意这里不是宿主机器的),这七条是系统自动生成的。让我们来看一下:

运行:

docker exec -it dns-server /bin/sh
/# cat /etc/hosts

会得到类似的结果:

127.0.0.1	localhost
::1	localhost ip6-localhost ip6-loopback
fe00::0	ip6-localnet
ff00::0	ip6-mcastprefix
ff02::1	ip6-allnodes
ff02::2	ip6-allrouters
172.17.0.2	82012eee8714

让我们先退出该容器:

/# exit

4.3.2、Centos系统

经过测试,未知原因,虽然我们没有使用--no-host参数,但是也无法直接从宿主机器的/etc/hosts进行解析。因此,和Mac OS中的启动命令相比,需要手动在配置里添加一个addn-hosts目录。

1、首先配置外部dns服务器:

mkdir /usr/share/dnsmasq
cd /usr/share/dnsmasq
vim resolv.conf
# 写入下列阿里云的DNS
nameserver 223.5.5.5
nameserver 223.6.6.6

2、配置本地解析规则:

仍然在/usr/share/dnsmasq目录下:

vim myhosts
# 这里配置自定义解析
127.0.0.1 dns-server1

3、启动dns服务器:

注:经过测试,centos环境下有时需要在指定端口处添加本机IP地址,有时又不需要,暂未明白原因。这里为了不出问题,统一加上本机IP地址。

docker run -d --restart=always --name dns-server1 -p 192.168.0.202:53:53/tcp -p 192.168.0.202:53:53/udp -v /usr/share/dnsmasq:/etc/dnsmasq --cap-add=NET_ADMIN andyshinn/dnsmasq --log-facility=-

docker ps -a

这里会得到类似的输出:

CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS              PORTS                                    NAMES
ad32ccbe7e06        andyshinn/dnsmasq   "dnsmasq -k --log-fa…"   5 seconds ago       Up 3 seconds        0.0.0.0:53->53/tcp, 0.0.0.0:53->53/udp   dns-server1

这表明我们的dns-server已经启动成功了。让我们来看一下它的日志:

docker logs dns-server1

这里会得到类似的输出:

dnsmasq[1]: started, version 2.81 cachesize 150
dnsmasq[1]: compile time options: IPv6 GNU-getopt no-DBus no-UBus no-i18n no-IDN DHCP DHCPv6 no-Lua TFTP no-conntrack ipset auth no-DNSSEC loop-detect inotify dumpfile
dnsmasq[1]: reading /etc/resolv.conf
dnsmasq[1]: using nameserver 8.8.8.8#53
dnsmasq[1]: using nameserver 4.4.4.4#53
dnsmasq[1]: read /etc/hosts - 7 addresses

注意,这里和Mac系统有所不同,它的nameserver为通用值。但还不是我们设定的阿里云域名服务器地址,因为我们的dnsmasq的配置还没有更改。

4、进入容器修改配置

docker exec -it dns-server1 /bin/sh

因为我们做了目录映射,可以在容器中看到刚才我们编辑的两个文件:

/ # cd /etc/dnsmasq
/etc/dnsmasq # ls
myhosts      resolv.conf
/etc/dnsmasq # 

修改配置:

vi /etc/dnsmasq.conf

# 修改下述两个配置
resolv-file=/etc/dnsmasq/resolv.conf
addn-hosts=/etc/dnsmasq/myhosts
/# exit

提示vim可以使用/键查找。

5、重启dns-server1服务:

docker restart dns-server1

五、测试

5.1、拉取测试镜像

我们使用busybox镜像来进行测试,因为它包含ping工具,并且只有1.23M大小。

拉取最新版本的busybox

docker pull busybox

5.2、docker run 测试

我们可以在docker run 中使用--dns=IP_ADDRESS添加 DNS 服务器到容器的 /etc/resolv.conf 中。

运行下面命令:

docker run --name dns-client -it --dns=192.168.0.202 busybox /bin/sh
/ # cat /etc/resolv.conf

会进入该容器并显示DNS域名服务器地址:

nameserver 192.168.0.202

接着在该容器中运行:

/ # ping peer0.test.example.com
ping: bad address 'peer0.test.example.com'

从结果看出来,我们无法解析peer0.test.example.com

新开一个终端,运行下面的命令,在宿主机器的解析目录中加上peer0.test.example.com的解析:

  • 如果是centos操作系统,除了向宿主机器的/usr/share/dnsmasq/myhosts添加记录外,还要发信号让dns服务器重新载入:

    echo '127.0.0.1 peer0.test.example.com' >> /usr/share/dnsmasq/myhosts
    docker kill -s HUP dns-server1
    
  • 如果是Mac系统,直接添加/etc/hosts即可,但是使用echo的方式会提示没有权限,sudo也不行,只能:

    sudo vim /etc/hosts
    

    然后选择E(按下E键),手动在最后添加127.0.0.1 peer0.test.example.com,最后保存退出。

我们接下来继续在busybox的容器中运行:

/ # ping peer0.test.example.com

短时间后ping成功,在有返回信息后ctrl + c中断它。有类似如下输出:

PING peer0.test.example.com (127.0.0.1): 56 data bytes
64 bytes from 127.0.0.1: seq=0 ttl=64 time=0.067 ms
64 bytes from 127.0.0.1: seq=1 ttl=64 time=0.093 ms
64 bytes from 127.0.0.1: seq=2 ttl=64 time=0.095 ms
^C
--- peer0.test.example.com ping statistics ---
3 packets transmitted, 3 packets received, 0% packet loss
round-trip min/avg/max = 0.067/0.085/0.095 ms

当然我们也可以多添加几个本地解析规则测试。

5.3、docker-compose测试

busybox容器内接着运行exit退出容器:

接下来移除刚才的busybox容器:

docker rm dns-client
docker ps -a

可以看到,当前只有dns-server1这一个容器了。

我们来创建一个docker-compose的配置文件docker-compose-dns-client.yaml作为配置文件。

vim docker-compose-dns-client.yaml

内容如下:

# docker-compose-dns-client.yaml
version: '2'

volumes:
  dns-client:

networks:
  test:

services:
  dns-client:
    container_name: dns-client
    image: busybox:latest
    command: tail -f /dev/null
    dns:
      - 192.168.0.202
    networks:
      - test

这其中command: tail -f /dev/null的作用是容器启动后一直运行,不然busybox镜像会直接退出。

我们运行下面的命令来启动容器:

docker-compose -f ./docker-compose-dns-client.yaml up -d 2>&1
docker ps -a

可以看到,我们的测试客户端dns-client已经在运行了。

接着我们进入容器进行测试:

docker exec -it dns-client /bin/sh

注意:这里我们看一下容器的DNS服务器:

/ # cat /etc/resolv.conf 
nameserver 127.0.0.11
options ndots:0

说明:可以看到这里的nameserver的值并不是本机IP地址,而是docker的默认内部dns服务器地址。这个我在一篇贴子中看到有人提出,要在docker-compose配置文件中设置网络类型为桥接模式,network_mode: "bridge",这里才会变成本机IP。但因为docker-compose网络类型默认就是桥接模式,并且后面有人回贴说不能更改这个**DNS proxy**值,(应该是更改后内部网络无法解析),我们让这个nameserver值 为127.0.0.11就行。

经过测试,发现的确可以不设置network_mode,这个nameserver值让它是默认值就行。

我们先测试刚才的peer0.test.example.com,同样在获取回应之后ctrl + c中断它,短时间等待后,输出如下:

/ # ping peer0.test.example.com
PING peer0.test.example.com (127.0.0.1): 56 data bytes
64 bytes from 127.0.0.1: seq=0 ttl=64 time=0.069 ms
64 bytes from 127.0.0.1: seq=1 ttl=64 time=0.119 ms
64 bytes from 127.0.0.1: seq=2 ttl=64 time=0.095 ms
64 bytes from 127.0.0.1: seq=3 ttl=64 time=0.089 ms
^C
--- peer0.test.example.com ping statistics ---
4 packets transmitted, 4 packets received, 0% packet loss
round-trip min/avg/max = 0.069/0.093/0.119 ms

我们再测试一个未存在的域名peer1.test.example.com

/ # ping peer1.test.example.com
ping: bad address 'peer1.test.example.com'

会提示找不到名称。

我们按照前面的方法添加解析规则后,接着测试:

/ # ping peer1.test.example.com
PING peer1.test.example.com (127.0.0.1): 56 data bytes
64 bytes from 127.0.0.1: seq=0 ttl=64 time=0.072 ms
64 bytes from 127.0.0.1: seq=1 ttl=64 time=0.084 ms
64 bytes from 127.0.0.1: seq=2 ttl=64 time=0.091 ms
^C
--- peer1.test.example.com ping statistics ---
3 packets transmitted, 3 packets received, 0% packet loss
round-trip min/avg/max = 0.072/0.082/0.091 ms
/ # 

可以看到,现在可以成功解析peer1.test.example.com了。

5.4、局域网测试

让我们换一台机器来进行域名解析测试。

192.168.0.204机器上进行 docker run 测试:

docker run --name dns-client -it --dns=192.168.0.202 busybox /bin/sh

注意:因为环境不同,某些情况虽然可以进入容器,但是有如下提示:

WARNING: IPv4 forwarding is disabled. Networking will not work.

意思为IPv4转发没有启用,网络不会工作。因此,我们运行/ # exit退出容器,然后再运行下面的命令删除刚才的测试容器并打开内核转发设置:

docker stop $(docker ps -a -q) && docker rm $(docker ps -a -q)

vim /etc/sysctl.conf
net.ipv4.ip_forward=1

sysctl -p

重新进行 docker run 测试:

docker run --name dns-client -it --dns=192.168.0.202 busybox /bin/sh
/ # ping peer0.test.example.com
PING peer0.test.example.com (127.0.0.1): 56 data bytes
64 bytes from 127.0.0.1: seq=0 ttl=64 time=0.070 ms
64 bytes from 127.0.0.1: seq=1 ttl=64 time=0.091 ms
64 bytes from 127.0.0.1: seq=2 ttl=64 time=0.089 ms
64 bytes from 127.0.0.1: seq=3 ttl=64 time=0.084 ms
^C
--- peer0.test.example.com ping statistics ---
4 packets transmitted, 4 packets received, 0% packet loss
round-trip min/avg/max = 0.070/0.083/0.091 ms

5.5、冗余dns服务器测试

5.5.1、部署一个新的dns服务器

按照部署dns服务的步骤在192.168.0.203的机器上重新部署一个dns-server2容器。

5.5.2、在docker-compose测试中添加新的dns

# docker-compose-dns-client.yaml
version: '2'

volumes:
  dns-client:

networks:
  test:

services:
  dns-client:
    container_name: dns-client
    image: busybox:latest
    command: tail -f /dev/null
    dns:
      - 192.168.0.202
      - 192.168.0.203
    networks:
      - test

5.5.3、正常测试

192.168.0.204机器上进行新增加了dns的docker-compose测试:

docker-compose -f ./docker-compose-dns-client.yaml up -d 2>&1
docker ps -a
docker exec -it dns-client /bin/sh
/ # ping peer0.test.example.com
PING peer0.test.example.com (127.0.0.1): 56 data bytes
64 bytes from 127.0.0.1: seq=0 ttl=64 time=0.064 ms
64 bytes from 127.0.0.1: seq=1 ttl=64 time=0.061 ms
^C
--- peer0.test.example.com ping statistics ---
2 packets transmitted, 2 packets received, 0% packet loss
round-trip min/avg/max = 0.061/0.062/0.064 ms

5.5.4、关闭dns-server1

202的机器上运行 systemctl stop docker204机器上接着ping测试:

/ # ping peer0.test.example.com
PING peer0.test.example.com (127.0.0.1): 56 data bytes
64 bytes from 127.0.0.1: seq=0 ttl=64 time=0.066 ms
64 bytes from 127.0.0.1: seq=1 ttl=64 time=0.093 ms
64 bytes from 127.0.0.1: seq=2 ttl=64 time=0.072 ms
64 bytes from 127.0.0.1: seq=3 ttl=64 time=0.101 ms
64 bytes from 127.0.0.1: seq=4 ttl=64 time=0.101 ms
^C
--- peer0.test.example.com ping statistics ---
5 packets transmitted, 5 packets received, 0% packet loss
round-trip min/avg/max = 0.066/0.086/0.101 ms

5.5.5、关闭dns-server2

  1. 202机器上运行systemctl start docker

  2. 203机器上运行systemctl stop docker

  3. 204机器上接着ping测试

    / # ping peer0.test.example.com
    PING peer0.test.example.com (127.0.0.1): 56 data bytes
    64 bytes from 127.0.0.1: seq=0 ttl=64 time=0.068 ms
    64 bytes from 127.0.0.1: seq=1 ttl=64 time=0.100 ms
    64 bytes from 127.0.0.1: seq=2 ttl=64 time=0.104 ms
    64 bytes from 127.0.0.1: seq=3 ttl=64 time=0.093 ms
    64 bytes from 127.0.0.1: seq=4 ttl=64 time=0.109 ms
    64 bytes from 127.0.0.1: seq=5 ttl=64 time=0.103 ms
    ^C
    --- peer0.test.example.com ping statistics ---
    6 packets transmitted, 6 packets received, 0% packet loss
    round-trip min/avg/max = 0.068/0.096/0.109 ms
    

从测试可以看出,冗余效果达到了。在203机器上运行systemctl start docker再次启动dns-server2

5.6、centos默认防火墙测试

在Centos环境下,默认防火墙为firewall,查看、停止和启动防火墙的状态

firewall-cmd --state
systemctl stop firewalld.service
systemctl start firewalld.service

经过测试,防火墙打开或者关闭都不影响dns解析,但是打开或者关闭的操作可能会影响docker。此时如果有问题,请重新启动docker(注意不是重新启动容器)。命令为systemctl restart docker

六、总结

dnsmasq作为一个轻量级的dns服务器软件,在Mac OS系统下使用起来还是挺方便的,在Centos上使用就稍微麻烦一些。由于局域网一般为虚拟机,也就是Linux,所以在使用时必须注意以下几点(Centos系统):

  1. IPV4转发要打开。
  2. 启动dnsmasq时指定端口要加上本机IP,并进行dns解析目录(文件)映射。
  3. 启动dnsmasq后要进行上级dns服务器及附加hosts解析设置。
  4. 更新DSN解析规则时,只需更新dns所在服务器的/usr/share/dnsmasq/myhosts文件,如果有多台服务器,则均需要更新,更新完成后要发信号 docker kill -s HUP <container_name>重新载入。

由于时间及个人能力有限,也未阅读dnsmasq文档,只是参考网上多篇文章根据自身实际写了这篇文章。欢迎大家留言提正问题和提出改进意见。

猜你喜欢

转载自blog.csdn.net/weixin_39430411/article/details/108666241