企业级内网DNS实战:用 dnsmasq 搭个内网 DNS

内网服务一多,Jenkins、Git、测试环境各一个 IP,开发运维各记一套,换台机器还得重新配。

与其继续维护 hosts,不如在内网放一台自己的 DNS:内部域名统一解析,外部域名缓存转发,客户端只改一个 DNS 地址。dnsmasq 轻量、配置直观,很适合干这件事。

先写 dnsmasq 配置

先在宿主机上建一个工作目录,把配置文件和 compose 文件放在一起,后面改起来方便。

bash 复制代码
mkdir ~/dnsmasq-docker && cd ~/dnsmasq-docker
nano dnsmasq.conf

配置文件可以这样写:

conf 复制代码
# 监听所有地址(容器内 0.0.0.0 才能被外部访问)
listen-address=0.0.0.0
bind-interfaces
port=53

# 不读取容器内的 /etc/resolv.conf,避免行为不可控
no-resolv
server=223.5.5.5
server=119.29.29.29

# 你的内部域名(以 internal.com 为例)
local=/internal.com/
address=/jenkins.internal.com/10.8.0.2
address=/git.internal.com/10.8.0.2

# 记录查询日志,方便排障
log-queries
log-facility=-

用 Docker Compose 启动

同目录下新建 docker-compose.yml:

yml 复制代码
services:
  dnsmasq:
    image: dockurr/dnsmasq:latest
    container_name: dnsmasq
    ports:
      - "53:53/tcp"
      - "53:53/udp"
    volumes:
      - ./dnsmasq.conf:/etc/dnsmasq.conf:ro
    restart: unless-stopped
    cap_add:
      - NET_ADMIN

启动并看状态:

bash 复制代码
docker compose up -d
docker ps | grep dnsmasq
docker logs dnsmasq

如果启动后发现 53 端口被占用

有时候 docker compose up -d 之后容器反复重启,docker logs dnsmasq 里能看到 53 端口绑定失败。先确认一下宿主机上是谁占着:

复制代码
ss -lunpt | grep :53

比较常见的情况是宿主机上的 systemd-resolved 在监听 127.0.0.53:53,和 dnsmasq 要绑定的 0.0.0.0:53 冲突了。

不建议直接 systemctl disable systemd-resolved。这样做会连带影响 Netplan 的 DNS 渲染、VPN 的分离 DNS,以及部分软件对 systemd-resolved 的依赖。更稳妥的做法是只关闭它的 53 监听,保留服务本身:

bash 复制代码
sudo mkdir -p /etc/systemd/resolved.conf.d/

sudo tee /etc/systemd/resolved.conf.d/disable-stub.conf <<'EOF'
[Resolve]
DNSStubListener=no
EOF

sudo systemctl restart systemd-resolved

执行完后再启动一次容器:

复制代码
docker compose up -d

测试 DNS 解析

先在宿主机上直接向容器查询:

bash 复制代码
dig @127.0.0.1 jenkins.internal.com
dig @192.168.1.10 git.internal.com

应返回你在 dnsmasq.conf 中配置的 10.8.0.2。

然后从另一台内网机器测试:

bash 复制代码
dig @192.168.1.10 jenkins.internal.com

这一步很关键。如果本机成功但远程失败,通常是两个原因。

  • 宿主机防火墙未放行 53 端口
  • dnsmasq 在容器内监听的地址不对。
    如果 dnsmasq.conf 里写的是 listen-address=127.0.0.1,容器内的 dnsmasq 只监听容器自己的回环地址,外部流量根本进不来。需要改成 listen-address=0.0.0.0,或者按你的规划改成宿主机具体内网 IP。

让内网服务器把 DNS 指向 dnsmasq

dnsmasq 跑通后,还需要让内网机器把查询发给它。不同系统改法不一样,但原则相同:首选 DNS 指向 dnsmasq,备用 DNS 放一个可用的公网上游,避免 dnsmasq 本身故障时整网解析中断。

使用 systemd-resolved 的系统,编辑 /etc/systemd/resolved.conf:

ini 复制代码
[Resolve]
DNS=192.168.1.10 223.5.5.5

然后重启:

bash 复制代码
sudo systemctl restart systemd-resolved

使用 Netplan 的 Ubuntu 服务器,在对应网卡下添加:

yml 复制代码
nameservers:
  addresses: [192.168.1.10, 223.5.5.5]

改完后用 dig 或 resolvectl 确认解析结果。到这里,内网 DNS 的主体部分就已经可用了。