容器化dns服务
之前将一个小应用使用docker compose部署之后,遇到一个问题,修改域名解析的IP。之前在虚拟机上可以直接修改hosts文件,
在docker容器中修改就比较麻烦,修改主机hosts文件也没有效果。
为了解决这个问题,引入了dnsmasq作为dns服务器。由于主机上本身已经有docker环境,因此也打算把dnsmasq放到容器中去运行。
首先是选择容器,在docker hub上下载量最多的是 andyshinn/dnsmasq 镜像。这个镜像的最大优势就是简单,整个镜像只有6M多。
对于无法访问外网的docker主机来说,部署比较方便。当然,这个镜像最大的劣势也是简单,没有提供配置文件,也无法可视化配置,使用时需要对dnsmasq有一定的了解。本文后面会介绍到刚开始使用遇到的不方便的地方。
安装
由于真正部署这个镜像的服务器无法访问外网,因此不能直接拉去docker镜像,暂时在一台可以访问外网的机器上拉取,然后导出,最后在机器上重新加载的方式来进行安装。也就是
docker pull andyshinn/dnsmasq
docker save andyshinn/dnsmasq > dnsmasq.tar
scp dnsmasq.tar admin@xxx:.
docker load < dnsmasq.tar
这样镜像就算是安装完成了。
启动
启动镜像前,参考了该镜像文档中的启动命令:
docker run -p 53:53/tcp -p 53:53/udp --cap-add=NET_ADMIN andyshinn/dnsmasq -S /consul/10.17.0.2
同时,看了下这个镜像的Dockerfile:
FROM alpine:3.2
RUN apk -U add dnsmasq
EXPOSE 53 53/udp
ENTRYPOINT ["dnsmasq", "-k"]
可以发现,这个镜像真是简单至极。默认就是运行了dnsmasq -k,即强制让dnsmasq运行在前端。然后dnsmasq没有任何特殊的配置,都需要启动过的时候手工指定。
而示例中的启动命令,有这几个含义:
- 将容器的53端口(TCP和UDP)映射到主机上,这是dns的标准端口
- 增加NET_ADMIN参数,使得容器中的进程可以修改网络配置
- 给dnsmasq传递参数,针对.consul后缀的域名,使用10.17.0.2这个上游dns服务器进行解析
这样的启动方式,当然是无法满足需求的。dnsmasq命令的参数,可以在这个文档中进行查询。如果仅仅是解析一个域名,可以通过–address参数指定,域名和IP格式和上面类似:
docker run -p 53:53/tcp -p 53:53/udp --cap-add=NET_ADMIN andyshinn/dnsmasq --address /api.test.com/127.0.0.1
这样容器启动之后,就可以解析api.test.com这个域名到127.0.0.1。
应用
启动容器还是比较方便,然后就是将这个dns服务提供给其他docker容器使用。docker在启动参数中支持通过–dns指定dns服务器地址(指定后,会修改启动容器的/etc/resolv.conf文件)。如果在docker-compose中使用,可以在yml文件中增加dns项。
这样,容器启动之后,就可以使用自己搭建的dns服务了。然而,并没有~
NAT模式下dns服务的失效
首先遇到的问题,是容器中的服务仍然无法解析域名。使用docker exec命令进入容器,ping了域名还是提示无法解析。排查dnsmasq的日志没有输出。
由于dnsmasq解析日志不太好排查,查询了手册之后发现,可以通过–log-facility参数指定日志输出,将容器启动参数改成:
docker run -p 53:53/tcp -p 53:53/udp --cap-add=NET_ADMIN andyshinn/dnsmasq --address /api.test.com/127.0.0.1 -q --log-facility=-
这样可以将dnsmasq的日志输出到标准错误,也就能够在当前命令行中查看到。(如果在docker run的时候使用了-d参数放到了后台执行,可以直接通过docker logs来查看输出)
再次到应用容器中ping域名,会发现dnsmasq实际上已经完成了域名解析,但是域名解析的来源IP都是docker0设备的IP。如果将应用容器的dns服务器地址,修改成dnsmasq容器的内网地址,则解析正常。因此判断这个解析错误,可能和两个容器都运行在NAT模式中有关。
将dns容器改成host模式:
docker run --net=host --cap-add=NET_ADMIN andyshinn/dnsmasq --address /api.test.com/127.0.0.1 -q --log-facility=-
在将应用容器的dns服务器地址修改成主机的IP,可以发现能够正常解析域名。
dns解析自动更新
按照上面方式启动dns容器,可以正常工作。但是如果要增加解析记录,需要重新启动这个容器。文档里面有提到dnsmasq可以通过向进程发送SIGHUP信号,重新加载文件:
When it receives a SIGHUP, dnsmasq clears its cache and then re-loads /etc/hosts and /etc/ethers and any file given by –dhcp-hostsfile, –dhcp-hostsdir, –dhcp-optsfile, –dhcp-optsdir, –addn-hosts or –hostsdir. The dhcp lease change script is called for all existing DHCP leases. If –no-poll is set SIGHUP also re-reads /etc/resolv.conf. SIGHUP does NOT re-read the configuration file.
也就是说,如果通过–addn-hosts或者–hostsdir参数指定的文件(夹),可以通过信号来触发dnsmasq重新加载。为了方便区分,最终采用了后者,既–hostsdir指定hosts文件夹。
首先,在主机上创建一个文件夹和一个文件,写入需要解析的域名:
mkdir hosts
echo '127.0.0.1 a.test.com' > hosts/common
然后启动容器的时候,设置挂载卷,将这个目录共享给容器,同时指定这个目录未hostsdir:
docker run --net=host --cap-add=NET_ADMIN -v /home/admin/hosts:/media andyshinn/dnsmasq --addn-hosts=/media/common -q --log-facility=-
这样,如果需要新增解析记录,需要两步:
- 在主机hosts目录中,新增一条记录:
echo 'b.test.com' >> hosts/common
- 向dns容器发送信号,使其重新加载文件:
docker kill -s HUP 2beb1d9f3554
这样虽然还需要手工操作,但是无须再重新运行dns容器。
转载自:https://coolex.info/blog/511.html