最近,在研究Hyperledger Fabric的多机部署。因为docker容器分布在多台机器上,解决相互之间通信问题的方案通常有以下几种:
- K8s,虽然k8s也可以解决这个问题,但是k8s主要用于高可靠性,配置也比较复杂,学习曲线比较陡峭。
- overlay网络。Docker提供了一个原生的overlay网络,可以将多台机器视为同一个网络,该网络内部的容器可以直接使用名称通信(内部有dns解析和转发)。它一般使用consul作底层数据库。但由于它对linux内核有要求(大于3.16),并且在实际测试的过程中未知原因有几台机器通信失败,所以只好放弃。(个人猜测也无法阻止单点故障。)
- 自定义一个局域网的域名服务器(因为超级账本一般部署在局域网或者虚拟机上)。也是最简单的方法,可以配置多个dns服务器来防止单点故障。
- docker-compse配置文件中提供域名解析,因为是固定的无法增减,不可取。
- docker swarm等其它方式。
本文综合网上dnsmasq
相关文章,对使用andyshinn/dnsmasq
部署一个局域网内的域名解析服务进行了学习研究。针对Mac和Centos 7.3操作系统分别进行了测试,并测试了一些细节问题。
那什么是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
- 自带
vi
和ping
工具,方便测试 - Mac OS下它默认会载入宿主机器的/etc/hosts文件,不再需要额外的mount
- Mac OS下它会跟踪/etc/hosts并会自动更新,你可以随时向宿主机器的/etc/hosts添加新的域名解析,然后就OK了。
- 启动时有多种OPTIONS可供选择,也可以启动后再配置,满足不同的需求。
当然,它还有很多优点,这里是它的文档地址 DNSMASQ文档,大家有兴趣的可以读一读。
四、部署DNS服务这里我们采用docker而不是可执行二进制文件的形式部署dnsmasq
。
这里我们拉取andyshinn/dnsmasq
,虽然文档建议增加标签,但是我们直接拉取latest
就行。
docker pull andyshinn/dnsmasq
4.2、获取本机IP
运行ifconfig
,Mac系统中有如下类似输出:
en0: flags=8863 mtu 1500
options=400
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
media: autoselect
status: active
可以看出本机IP为192.168.1.3
,当然,如果在Mac机器上运行,也可以直接在网络连接中找到本机IP。
Centos系统下有类似如下输出:
eth0: flags=4163 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
。
未知原因,在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目录。
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
可以使用/
键查找。
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
了。
让我们换一台机器来进行域名解析测试。
在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
容器。
# 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 docker
,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.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
-
202
机器上运行systemctl start docker
-
203
机器上运行systemctl stop docker
-
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
。
在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系统):
- IPV4转发要打开。
- 启动dnsmasq时指定端口要加上本机IP,并进行dns解析目录(文件)映射。
- 启动dnsmasq后要进行上级dns服务器及附加hosts解析设置。
- 更新DSN解析规则时,只需更新dns所在服务器的
/usr/share/dnsmasq/myhosts
文件,如果有多台服务器,则均需要更新,更新完成后要发信号docker kill -s HUP
重新载入。
由于时间及个人能力有限,也未阅读dnsmasq文档,只是参考网上多篇文章根据自身实际写了这篇文章。欢迎大家留言提正问题和提出改进意见。