本篇文章,我将介绍一个方便本地开发和调试的方案,本地 DNS 代理服务器。

写在前面

不论是你做前端还是后端开发,本地调试带有域名的接口或页面是大概率绕不开的事情。甚至,如果你使用了自签名证书或者“虚拟域名”进行 HomeLab 服务搭建,某些不能使用网络默认 DNS 服务器时,也需要一些灵活的方案来动态切换一系列域名的指向。

不过,不能因为手持锤子就哪里都是钉子,简单的场景下,比如就修改一次的情况下,直接修改 hosts 解决问题会是更简单的方案,关于 Hosts Editor 类的工具的推荐,可以阅读文末章节。

言归正传,先来聊一个我使用了六个多月的方案。

方案一:带有界面的 dnsmasq 容器

dnsmasq 作为 DNS Server 被广泛用于 Linux 发行版。我们常见的 Ubuntu Server 版以及 Open WRT 路由器固件中,不少版本默认使用的都是它。

但是它是一个命令行软件,默认并不支持自动重载有修改后的配置文件,配置文件的编辑和我们常规修改 /etc/hosts 别无二致,国外有一个工程师为了解决这个问题,开发了一个简单的带有界面的配置工具 docker-dnsmasq,在配置文件被修改后,能够发送命令重启或重载 dnsmasq 主程序,达到“方便使用”的目的。

因为作者许久不更新软件,在今年二月的时候,我做了一个 fork 版本,soulteary/docker-dnsmasq,你可以使用下面的配置快速运行一个属于你的本地 DNS 服务器。

以往我们编辑 hosts 文件,会用下面的形式来进行域名绑定:

10.11.12.123 docker.lab.com
10.11.12.123 maven.lab.com
10.11.12.123 npm.lab.com
10.11.12.123 pypi.lab.com
...

所以当域名很多的时候,使用起来就会非常麻烦,相比之下 dnsmasq 的配置文件就会简单许多,因为它允许使用“泛解析”的方式,除此之外还能指定上游服务器,近一步扩展能力,下面就是一个 dnsmasq.conf 的配置文件的例子:

# HomeLab
## Use Home DNS Upstream
server=10.11.12.13

# HomeLab Domain Example:
address=/.lab.com/10.11.12.123
address=/*.lab.com/10.11.12.123
address=/*.demo.lab.com/10.11.12.123
address=/*.api.lab.com/10.11.12.123
address=/*.some.api.lab.com/10.11.12.123

# localhost
address=/.lab.io/127.0.0.1
address=/*.lab.io/127.0.0.1

在上面的例子中,将 lab.com 和一些子域名指向了内网的一台机器,而将 lab.io 全部指向了本机,将上面的内容保存为 dnsmasq.conf,我们来编写容器编排文件:

version: "3"
services:

  dns:
    image: soulteary/docker-dnsmasq
    restart: always
	# 如果你需要一个简单的 Basic Auth 认证
    #environment:
    #  - HTTP_USER=user
    #  - HTTP_PASS=pass
    ports:
      - "53:53/udp"
      - "53:53/tcp"
      - "8080:8080"
    volumes:
      - ./dnsmasq.conf:/etc/dnsmasq.conf:rw

将上面的内容保存为 docker-compose.yml,然后使用 docker-compose up -d 启动服务,接着使用浏览器访问 8080 端口,就能看到控制面板了,开始使用啦。

需要额外注意的是,为了减少后续设置的复杂,我们默认使用 53 端口来提供服务,管理面板默认使用的端口是 8080,如果你有端口冲突,建议进行调整或修改。

【图片】

界面比较简单,这里就不过赘述了,编辑器支持使用快捷键(CMD+/)切换注释,所以,如果在配置存放一些不同环境的配置,通过快捷键就能进行批量快速切换啦,最近半年,我是这么解决不同环境的 DNS 记录切换的:

# HomeLab
## Use Home DNS
server=10.11.12.13

# Office
## Use Dev NS Servers
# server=219.141.136.10
# server=219.141.140.10

## Use CloudFlare NS Servers
# server=1.0.0.1
# server=1.1.1.1


# Local
address=/.lab.io/127.0.0.1
address=/*.lab.io/127.0.0.1

address=/.lab.com/10.11.12.123
address=/*.lab.com/10.11.12.123
address=/*.demo.lab.com/10.11.12.123
address=/*.api.lab.com/10.11.12.123
address=/*.some.api.lab.com/10.11.12.123


# address=/.lab.com/192.11.12.123
# address=/*.lab.com/192.11.12.123
# address=/*.demo.lab.com/192.11.12.123
# address=/*.api.lab.com/192.11.12.123
# address=/*.some.api.lab.com/192.11.12.123

当然,这个小程序同时会读取容器内的 /etc/hosts,你也可以通过左侧侧边栏切换编辑器打开 hosts 文件,用传统的方式添加修改 DNS 记录。

在使用过程中,也会有一些体验不好的地方,比如程序重载需要几秒的时间,过程中会有服务不可用的状态,编辑器只有最基础的功能,缺少快捷键等。偶尔有的时候程序会出现一些异常的资源使用,社区里有人反馈,所以,我开始计划将这个方案替换掉。

结合系统使用

以 macOS 为例,打开网络设置,选择当前网络,点击“高级”按钮,然后切换到 DNS 选项卡,在左侧的 DNS 服务器里,添加 “127.0.0.1”即可。

【图片】

这里有一个小技巧,为了保证网络完全不间断(比如重启服务的时候),这里可以除了添加我们指定的 DNS 服务之外,将当前网络的 DNS 服务器也添加进去。

配合Traefik 使用 dnsmasq

如果你是我的老读者,那么应该对 Traefik 不会感到陌生,这里提供一个简单的配置,方便你使用 Traefik:

version: "3"
services:

  dns:
    image: soulteary/docker-dnsmasq
    restart: always
	# 如果你需要一个简单的 Basic Auth 认证
    # 使用 Traefik 推荐使用 Forward Auth 进行取代
    #environment:
    #  - HTTP_USER=user
    #  - HTTP_PASS=pass
    ports:
      - "53:53/udp"
      - "53:53/tcp"
      - "8080:8080"
    volumes:
      - ./dnsmasq.conf:/etc/dnsmasq.conf:rw
    labels:
      - "traefik.enable=true"
      - "traefik.docker.network=traefik"
      - "traefik.http.routers.dnsmasq-web.entrypoints=http"
      - "traefik.http.routers.dnsmasq-web.rule=Host(`dns.lab.io`)"
      - "traefik.http.routers.dnsmasq-ssl.entrypoints=https"
      - "traefik.http.routers.dnsmasq-ssl.tls=true"
      - "traefik.http.routers.dnsmasq-ssl.rule=Host(`dns.lab.io`)"
      - "traefik.http.services.dnsmasq-backend.loadbalancer.server.scheme=http"
      - "traefik.http.services.dnsmasq-backend.loadbalancer.server.port=8080"
    networks:
      - traefik

networks:
  traefik:
    external: true

方案二:使用 go-dnsmasq 方案

go-dnsmasq 是一个轻量到只有 1.2MB 的DNS缓存/转发工具,但是可惜的是作者在 16 年之后就没有在继续维护项目,在翻阅了几十个 fork 衍生版之后,我最终将两个国外的改进版本合并成了一个新的版本 https://github.com/soulteary/go-dnsmasq,并制作了一个 2.7 MB 左右的容器镜像。

使用方法其实比上面还要简单,先来看配置文件:

127.0.0.1 lab.com
127.0.0.2 *.lab.com

平凡无奇的 hosts 记录的语法中,支持了泛解析,比 dnsmasq.conf 少了不少符号记忆的负担。虽然日常使用肯定会使用复制粘贴,但是少一个字符,出错的可能就少了一分,不是吗?将上面的内容保存为 hosts.conf,稍后使用。

我们继续编写容器配置文件:

version: "3"
services:

  dns:
    image: soulteary/go-dnsmasq
    command: dnsmasq -l 0.0.0.0:53 -f /hosts.conf -p 1s --nameservers 10.11.12.13:53
    restart: always
    ports:
      - "53:53/udp"
      - "53:53/tcp"
    volumes:
      - ./hosts.conf:/hosts.conf:rw

相比较方案一,这个方案显然更“轻量环保”。至于切换环境配置,只需要准备多份不同环境的配置文件,使用 docker 挂载的时候切换文件就可以啦。

如何结合系统使用本地 DNS 服务器,上文有提过,这里不再赘述。接着来聊聊文章开头聊到的编辑本地 Hosts 文件。

其他:如何简单的修改 Hosts 文件

如果你只需要管理几个域名,也不太想启动一个服务(哪怕它只有2M),可以尝试编辑系统的 Hosts 文件来完成域名指向,如果你厌倦了命令行或者记事本修改文件,也可以考虑下载一些 Hosts Editor 类的工具。

最初的时候,我使用过 Gas MaskHostsMan,以及一些类似的软件,在第一次去淘宝工作的时候,被安利了一个内网神器“iHosts”(不是搜索引擎搜索出的同名软件),除了多了清新的界面之外,还支持记录分组,以及本地 DNS 服务器,对于调试移动端场景、或者虚拟机场景还是挺方便的。

在许多年前离开淘宝后,因为无法再从内网下载“ihosts”,于是就切换到了 “SwitchHosts!”(最新版本改名字去掉了“!”),虽然没有内置 DNS 服务、请求日志等功能,但是胜在功能简单够用,加上作者靠谱,也就一直用了下来。

为什么说作者靠谱呢?早些时候和刚从淘宝离开的季札大神一起吃饭,聊起招聘困难,当时玩笑的说要不要在 SwitchHosts README 里加个招聘广告,季札大神说希望这个软件能够一直纯粹下去,然后这些年这个软件,至今为止就一直这么干净纯粹着,还是挺难得的。

但是,在使用过程中难免会遇到需要做“泛解析”的场景,批量改动记录总归是比较麻烦的事情,以及我的电脑常年不关机,经常休眠唤醒,基于 Electron 的 SwitchHosts 总是会出现内存溢出的问题,所以只好忍痛把主力方案切换到了上文中的 DNS 方式。其实解决的方案也很简单,就是每次使用完 SwitchHosts 之后,把它的进程彻底关掉。

后续我应该会继续使用 SwitchHosts 这个功能,不过不会再作为主力工具。

最后

原本以为离职休假能够把草稿箱里的文章清理一下,没想到积累的草稿更多了。

–EOF