使用 CoreDNS 搭建本地域名服务
前言⌗
习惯了 laravel/valet 的方便后,在使用 Docker 搭建的开发环境中,经常需要再本地 /etc/hosts
文件中给项目设置本地域名。这样会导致一个问题,那就是每个人自己配置的域名可能不同,而很多时候成员间需要通过 URL 来交流和复现问题。
比如 A 开发给 CRM 项目的域名是 crm.localhost
,B 开发给 CRM 项目的域名是 crm.dev
这样导致我们公用的 API 调试器中需要为每个人定义一套环境,与此同时在复现问题时,也增加了一定的沟通成本。
那么为了尽可能的保持环境的一致性,我们将服务的访问域名也通过配置文件内置于 Docker 的 Compose 编排中,这样一来,每个人不需要做什么,只需要执行 docker compose up -d
就能够得到一致的开发环境,包括域名和 HTTPS 证书等!
选型⌗
为了实现高度统一的愿景,我调研了如下一些开源项目:
- dnsmasq
- CoreDNS
- AdGuardDNS
因为 laravel/valet 使用的也是 dnsmasq,所以我优先考虑选择 dnsmasq,但是扒了一圈,没有找到官方维护的 Docker Image,有一些个人开发者维护的版本,想想还是算了!
后来发现 CoreDNS 基于 Go 语言开发,且是 Cloud Native 孵化的毕业项目,后期的社区肯定是没的说。简单试用了了一下发现基本符合我们的需求,支持通配符解析。
AdGuard Home 算是一个意料之外的收货,为什么这么说呢,因为它面向的是普通用户,而不是开发者,提供了简洁美观的管理界面。但是不太适合我们的地方在于,它主要是用来拦截广告的,并且配置需要登录管理后台配置自定义域名的重写,也支持通配符解析。
经过对比我们最终选择了 CoreDNS,一方面是更强大的社区、丰富的插件以及可通过配置文件保证一致性。
获取镜像⌗
因为我们团队使用的都是基于 ARM 架构的 M1 Chip,而官方目前并未提供 ARM 架构的 Docker Image,所以我们使用的是自己构建了一个 ARM 架构的 Docker Image。
docker pull betterde/coredns
其他架构 CPU 可以拉取 CoreDNS 官方 Docker Image
配置⌗
首先创建 docker-compose.yml
容器编排配置文件:
services:
coredns:
image: betterde/coredns:latest
ports:
- 53:53/udp
restart: always
command: -conf /root/Corefile
volumes:
- ./config:/root
hostname: coredns
container_name: coredns
新版的 Docker Compose 不再需要定义
version
了。
接着创建 CoreDNS 的配置文件夹:
mkdir config
例如我我们将 betterde.it
这个 TLD 作为本地开发环境的域名,并在文件夹下创建 Corefile 配置文件:
.:53 {
forward . 223.5.5.5 223.6.6.6
log
errors
}
betterde.it:53 {
file /root/betterde.it.db
log
loop
reload
errors
}
最后创建一个用于定义域名解析的数据文件 betterde.it.db
:
betterde.it. IN SOA dns.betterde.it. george.example.com. 2017042745 7200 3600 1209600 3600
*.betterde.it. IN A 127.0.0.1
dns.betterde.it. IN A 127.0.0.1
host.betterde.it. IN A 192.168.1.10
server.betterde.it. IN CNAME host.betterde.it.
如果你配置过 BIND 的域名服务器 zone,对上面的
betterde.it.db
配置文件应该并不陌生,其中george.example.com.
是管理者邮箱,因为@
在zone
配置文件中有其他用户,所以,这里用.
替代。
启动服务⌗
docker compose up -d
验证结果⌗
dig @127.0.0.1 dns.betterde.it
; <<>> DiG 9.10.6 <<>> @127.0.0.1 dns.betterde.it
; (1 server found)
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 37167
;; flags: qr aa rd; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 1
;; WARNING: recursion requested but not available
;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 4096
;; QUESTION SECTION:
;dns.betterde.it. IN A
;; ANSWER SECTION:
dns.betterde.it. 0 IN A 127.0.0.1
;; Query time: 21 msec
;; SERVER: 127.0.0.1#53(127.0.0.1)
;; WHEN: Mon Nov 28 15:49:28 CST 2022
;; MSG SIZE rcvd: 67
使用 CoreDNS⌗
在 macOS 上使用自己的 DNS 可以通过如下步骤来管理命令来进行配置:
- 查看系统 DNS 优先级
scutil --dns
DNS configuration
resolver #1
nameserver[0] : 223.6.6.6
nameserver[1] : 202.46.34.75
if_index : 14 (en0)
flags : Request A records
reach : 0x00000002 (Reachable)
resolver #2
domain : local
options : mdns
timeout : 5
flags : Request A records
reach : 0x00000000 (Not Reachable)
order : 300000
resolver #3
domain : 254.169.in-addr.arpa
options : mdns
timeout : 5
flags : Request A records
reach : 0x00000000 (Not Reachable)
order : 300200
resolver #4
domain : 8.e.f.ip6.arpa
options : mdns
timeout : 5
flags : Request A records
reach : 0x00000000 (Not Reachable)
order : 300400
resolver #5
domain : 9.e.f.ip6.arpa
options : mdns
timeout : 5
flags : Request A records
reach : 0x00000000 (Not Reachable)
order : 300600
resolver #6
domain : a.e.f.ip6.arpa
options : mdns
timeout : 5
flags : Request A records
reach : 0x00000000 (Not Reachable)
order : 300800
resolver #7
domain : b.e.f.ip6.arpa
options : mdns
timeout : 5
flags : Request A records
reach : 0x00000000 (Not Reachable)
order : 301000
resolver #8
domain : it
nameserver[0] : 127.0.0.1
flags : Request A records, Request AAAA records
reach : 0x00030002 (Reachable,Local Address,Directly Reachable Address)
DNS configuration (for scoped queries)
resolver #1
nameserver[0] : 223.6.6.6
nameserver[1] : 202.46.34.75
if_index : 14 (en0)
flags : Scoped, Request A records
reach : 0x00000002 (Reachable)
可以看到现在使用的是阿里云 DNS 223.6.6.6 和电信 202.46.34.75 作为 DNS 的。
- 列出所有的网络连接方式:
networksetup -listallnetworkservices
An asterisk (*) denotes that a network service is disabled.
USB-LAN
Wi-Fi
Thunderbolt Bridge
iPhone USB
GAVPN
Tailscale Tunnel
- 给指定的网络连接方式设定 DNS 服务器
networksetup -setdnsservers Wi-Fi 127.0.0.1
例如我这里是通过 Wi-Fi 联网的,所以我这里给 Wi-Fi 设备设置 DNS 查询走本地 Loopback 地址
127.0.0.1
。
- 验证结果
scutil --dns
DNS configuration
resolver #1
nameserver[0] : 127.0.0.1
flags : Request A records, Request AAAA records
reach : 0x00030002 (Reachable,Local Address,Directly Reachable Address)
resolver #2
domain : local
options : mdns
timeout : 5
flags : Request A records
reach : 0x00000000 (Not Reachable)
order : 300000
resolver #3
domain : 254.169.in-addr.arpa
options : mdns
timeout : 5
flags : Request A records
reach : 0x00000000 (Not Reachable)
order : 300200
resolver #4
domain : 8.e.f.ip6.arpa
options : mdns
timeout : 5
flags : Request A records
reach : 0x00000000 (Not Reachable)
order : 300400
resolver #5
domain : 9.e.f.ip6.arpa
options : mdns
timeout : 5
flags : Request A records
reach : 0x00000000 (Not Reachable)
order : 300600
resolver #6
domain : a.e.f.ip6.arpa
options : mdns
timeout : 5
flags : Request A records
reach : 0x00000000 (Not Reachable)
order : 300800
resolver #7
domain : b.e.f.ip6.arpa
options : mdns
timeout : 5
flags : Request A records
reach : 0x00000000 (Not Reachable)
order : 301000
resolver #8
domain : it
nameserver[0] : 127.0.0.1
flags : Request A records, Request AAAA records
reach : 0x00030002 (Reachable,Local Address,Directly Reachable Address)
DNS configuration (for scoped queries)
resolver #1
nameserver[0] : 127.0.0.1
if_index : 14 (en0)
flags : Scoped, Request A records, Request AAAA records
reach : 0x00000000 (Not Reachable)
通过上面的结果可以看到,现在使用的是 127.0.0.1 作为 DNS 的。
总结⌗
dig www.baidu.com
; <<>> DiG 9.10.6 <<>> www.baidu.com
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 40287
;; flags: qr rd ra; QUERY: 1, ANSWER: 3, AUTHORITY: 0, ADDITIONAL: 1
;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 4096
;; QUESTION SECTION:
;www.baidu.com. IN A
;; ANSWER SECTION:
www.baidu.com. 196 IN CNAME www.a.shifen.com.
www.a.shifen.com. 196 IN A 180.101.49.13
www.a.shifen.com. 196 IN A 180.101.49.14
;; Query time: 22 msec
;; SERVER: 127.0.0.1#53(127.0.0.1)
;; WHEN: Mon Nov 28 16:02:29 CST 2022
;; MSG SIZE rcvd: 149
dig crm.betterde.it
; <<>> DiG 9.10.6 <<>> crm.crm.betterde.it
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 28646
;; flags: qr aa rd; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 1
;; WARNING: recursion requested but not available
;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 4096
;; QUESTION SECTION:
;crm.crm.betterde.it. IN A
;; ANSWER SECTION:
crm.crm.betterde.it. 0 IN A 127.0.0.1
;; Query time: 5 msec
;; SERVER: 127.0.0.1#53(127.0.0.1)
;; WHEN: Mon Nov 28 16:04:42 CST 2022
;; MSG SIZE rcvd: 67
到此,一个小而全的 DNS 就搭建完成了,而它将成为我们团队协作的一个必要基础设施!
如果需要取消使用 CoreDNS 可以使用如下命令:
# 清空设备当前的 DNS 设置
networksetup -setdnsservers Wi-Fi empty
# 检查设备当前 DNS 设置
networksetup -getdnsservers Wi-Fi
最后清理 DNS 缓存
dscacheutil -flushcache
I hope this is helpful, Happy hacking…