Loading...

文章背景图

CoreDNS 部署指南:云原生 DNS 服务器从安装到生产

2026-10-29
1
- 字
- 分钟
|

CoreDNS Logo

一、项目介绍

CoreDNS 是一个用 Go 语言编写的开源 DNS 服务器与转发器,它通过插件链(plugin chain)的方式把各类 DNS 功能串联起来,每个插件负责一项具体的 DNS 任务。CoreDNS 是云原生计算基金会(CNCF)毕业级项目,也是 Kubernetes 集群默认内置的 DNS 组件,几乎所有 K8s 发行版都用它做服务发现,在云原生生态中地位等同于互联网早期的 BIND。

与传统的 BIND、Unbound、dnsmasq 不同,CoreDNS 的核心设计哲学是"灵活":当开箱功能不满足需求时,你既可以直接挑选社区已经写好的插件组合,也可以按照官方规范自行编写插件并通过 plugin.cfg 编译进二进制。它支持多种 DNS 传输协议,包括传统的 UDP/TCP、DNS over TLS(DoT,RFC 7858)、DNS over HTTP/2(DoH,RFC 8484)、DNS over HTTP/3(DoH3)、DNS over QUIC(DoQ,RFC 9250)以及私有 gRPC 协议,几乎覆盖了所有现代 DNS 加密传输标准。

常见能力包括:从区域文件加载权威数据(file/auto 插件)、作为从服务器做 AXFR 区域传送(secondary 插件)、在线 DNSSEC 签名(dnssec 插件)、响应负载均衡(loadbalance 插件)、响应缓存(cache 插件)、以 etcd 或 Kubernetes 为后端数据源、转发到上游递归 DNS(forward 插件)、Prometheus 指标暴露(prometheus 插件)、查询与错误日志(log/errors 插件)、对接 Route53 云厂商、Rewrite 重写查询名与类型(rewrite/template 插件)、阻断 ANY 查询(any 插件)、DNS64 IPv6 翻译、CHAOS 类查询(version.bind)、NSID 支持、pprof 性能剖析等。

由于 CoreDNS 是 CNCF 项目,社区非常活跃,在 GitHub 上收获了上万 Star,Docker Hub 月下载量超过千万次。它被大量互联网公司用于替代 BIND 做内网 DNS、作为 Kubernetes 集群 DNS、作为边缘节点的递归解析器,以及在 Service Mesh 场景下做服务发现。

CoreDNS 的另一个重要优势是低资源占用。静态编译后的二进制仅十几 MB,常驻内存通常在 50MB 以内,相比 BIND 动辄几百 MB 的内存占用和复杂的 named.conf 语法,CoreDNS 在容器化、边缘计算、IoT 网关等场景下优势非常明显。它还支持通过 file 插件做权威 DNS、通过 auto 插件自动加载目录下所有区域文件,省去手工 reload 的麻烦。

项目仓库地址:github.com/coredns/coredns,官方网站:coredns.io,插件列表:coredns.io/plugins,外部插件仓库:coredns.io/explugins。

二、项目结构与核心组件

CoreDNS 仓库根目录结构非常清晰,理解以下几个关键路径就掌握了它的整体骨架:

  • Corefile:CoreDNS 的配置文件,采用类 Caddy 的 DSL 语法,按"区域:端口 { 插件指令 }"组织。启动时默认从当前工作目录寻找 Corefile,也可以用 -conf 指定路径。
  • coremain.go:进程入口,负责解析命令行参数、加载 Corefile、启动 HTTP/HTTPS/DNS 监听器、安装信号处理器(SIGUSR1 重载、SIGTERM 退出)。
  • core/:DNS 请求处理核心,定义了 *dns.Msg 与插件链的调度逻辑,所有插件都实现 plugin.Handler 接口,按 Corefile 中声明的顺序串成一条中间件链。
  • plugin/:内置插件目录,每个子目录就是一个插件,包含 README.md 说明和实现代码。常用插件如 cache、forward、kubernetes、prometheus、log、errors、rewrite、hosts、file、auto、whoami、grpc、tls、mdns、forward 都在这里。
  • plugin.cfg:插件编译清单,按顺序列出所有被编译进二进制的插件。修改这个文件再 make 即可裁剪或扩展 CoreDNS,比如去掉 kubernetes 插件就能得到一个仅做递归转发的精简版。
  • request/:请求生命周期与缓存键构造逻辑,决定 cache 插件如何区分不同查询。
  • test/:端到端测试,使用脚本化测试验证各种 Corefile 行为,覆盖绝大多数插件组合。
  • deployment 仓库:github.com/coredns/deployment 提供 systemd unit、Kubernetes Helm chart、Docker Compose、Ansible 等部署样例。

核心组件可以归纳为三层:传输层(DNS/UDP、DNS/TCP、DoT、DoH、DoH3、DoQ、gRPC 监听器,统一在 coremain 中根据 Corefile 的 scheme 前缀启动)、插件链(按 Corefile 顺序执行的 Handler 列表,每个插件既可以直接返回响应,也可以调用下一个插件继续处理)、后端数据源(区域文件、etcd、Kubernetes Service、hosts 静态文件、上游转发、Route53 等)。

另外,CoreDNS 支持 import 指令把多个 Corefile 片段组合起来,也支持 {$ENV_VAR} 语法从环境变量注入配置,方便在容器化部署时按环境差异化。

CoreDNS 还支持通过 auto 插件自动加载指定目录下的所有区域文件,新增区域只需要把 .db 文件放进目录,无需修改 Corefile、无需 reload,非常适合多租户、多项目的内网 DNS 场景。

三、版本与环境要求

CoreDNS 采用语义化版本,最新稳定版可在 Releases 页面查看。从源码编译时,官方明确要求 Go 1.26.0 或更高版本(因为使用了 go mod 与较新的标准库 API)。官方 Docker 镜像基于 golang:1.27 多阶段构建,最终运行镜像基于 alpine,体积不到 20 MB。

运行时环境要求:

  • 操作系统:Linux(推荐生产使用)、macOS、Windows 均可。官方 Release 提供 darwin/amd64、darwin/arm64、linux/386、linux/amd64、linux/arm、linux/arm64、linux/ppc64le、linux/s390x、windows/amd64、windows/arm64 等预编译二进制。
  • 架构:x86_64、ARMv7、ARM64、ppc64le、s390x 都有官方镜像;在树莓派等 ARM 设备上也能流畅运行。
  • 权限:监听 53 端口需要 root 或 CAP_NET_BIND_SERVICE capability。CoreDNS 在 Linux 上会在绑定端口失败时自动临时启用该能力(前提是镜像允许该 capability),不需要手动 setcap。
  • 资源:单实例日常 DNS 解析只需几十 MB 内存;Kubernetes 集群规模下,cache 插件命中率决定内存占用,建议给 CoreDNS Pod 分配 128Mi 以上内存、100m 以上 CPU。
  • 防火墙:放行 UDP/53、TCP/53;如启用 DoT/DoH 再放行 853、443;prometheus 端口 9153 一般只对内网监控网段开放。
  • 依赖:CoreDNS 是纯 Go 静态二进制,没有任何外部动态库依赖,移植非常方便。

注意:CoreDNS 不提供官方 apt/yum 包,多数发行版需要通过二进制、Docker 或 K8s Deployment 方式部署。Ubuntu 等发行版的 universe 仓库虽然有 coredns 包,但版本通常滞后,生产环境建议自行下载官方 Release。

四、本地部署

4.1 从 Release 下载二进制(最快上手)

访问 GitHub Releases,选择对应平台的 tar.gz 下载。以 Linux amd64 为例:

wget https://github.com/coredns/coredns/releases/latest/download/CoreDNS_linux_amd64.tar.gz
tar -xzf CoreDNS_linux_amd64.tar.gz
chmod +x coredns
sudo mv coredns /usr/local/bin/
coredns -version
coredns -plugins | head -30

第二条命令会列出当前二进制编译进去的所有插件,可以确认 cache、forward 等是否都在。

4.2 从源码编译

git clone https://github.com/coredns/coredns
cd coredns
make
./coredns -version

如需启用额外插件,设置环境变量 COREDNS_PLUGINS,按 plugin.cfg 同样的顺序逗号分隔:

COREDNS_PLUGINS="whoami:whoami,rewrite:rewrite" make

编译完成后会在当前目录得到一个 coredns 二进制。如果 Go 版本不够,make 会直接报错并提示最低版本。

4.3 使用 Docker 镜像

docker pull coredns/coredns:latest
# 准备最小 Corefile
mkdir -p /etc/coredns && cd /etc/coredns
cat > Corefile <<EOF
.:53 {
    forward . 8.8.8.8 1.1.1.1 223.5.5.5
    log
    cache 30
    errors
}
EOF
docker run -d --name coredns \
  -p 53:53/udp -p 53:53/tcp \
  -p 9153:9153 \
  -v /etc/coredns/Corefile:/Corefile \
  --restart=unless-stopped \
  coredns/coredns:latest -conf /Corefile

如果没有 Go 环境但想从源码构建 Docker 镜像,可以用官方提供的 golang 镜像一键构建:

docker run --rm -i -t \
  -v $PWD:/go/src/github.com/coredns/coredns -w /go/src/github.com/coredns/coredns \
  golang:1.27 sh -c 'GOFLAGS="-buildvcs=false" make gen && GOFLAGS="-buildvcs=false" make'

4.4 编写最小 Corefile 并启动

cat > Corefile <<EOF
.:53 {
    forward . 8.8.8.8
    log
    cache 30
}
EOF
./coredns -conf Corefile

另开终端测试:

dig @127.0.0.1 google.com
dig @127.0.0.1 -p 1053 www.example.com
nslookup www.baidu.com 127.0.0.1

如果 53 端口被占用,可以改用 1053:coredns -dns.port 1053,或者在 Corefile 里写 .:1053 { ... }。

4.5 systemd 常驻(生产推荐)

先创建专用用户:sudo useradd -r -s /usr/sbin/nologin coredns,然后创建 /etc/systemd/system/coredns.service:

[Unit]
Description=CoreDNS DNS Server
After=network.target

[Service]
LimitNOFILE=1048576
LimitNPROC=512
CapabilityBoundingSet=CAP_NET_BIND_SERVICE
AmbientCapabilities=CAP_NET_BIND_SERVICE
NoNewPrivileges=true
User=coredns
WorkingDirectory=/etc/coredns
ExecStart=/usr/local/bin/coredns -conf /etc/coredns/Corefile
ExecReload=/bin/kill -SIGUSR1 $MAINPID
Restart=on-failure
RestartSec=5

[Install]
WantedBy=multi-user.target
sudo systemctl daemon-reload
sudo systemctl enable --now coredns
sudo systemctl status coredns
sudo journalctl -u coredns -f

修改 Corefile 后执行 sudo systemctl reload coredns 即可热加载,不需要重启进程。

在实际生产中,建议同时部署两台 CoreDNS 做高可用,主从之间通过 transfer 插件做区域传送,客户端 resolv.conf 同时写两个 nameserver 实现自动故障转移。对于 K8s 集群,CoreDNS 本身就是 Deployment,可以直接用 kubectl scale deploy coredns -n kube-system --replicas=3 横向扩容。

4.6 Kubernetes 部署

直接使用官方 Helm chart:

helm repo add coredns https://coredns.github.io/helm
helm repo update
helm install coredns coredns/coredns -n kube-system \
  --set serviceType=ClusterIP \
  --set clusterIP=10.96.0.10

如果是 kubeadm 初始化的集群,CoreDNS 已经默认部署,只需要 kubectl -n kube-system edit deploy coredns 调整参数即可。

4.7 生产级 Corefile 完整示例

下面给出一个在中小公司内网中常用的 Corefile,同时承担权威解析、递归转发、内网 hosts、指标暴露四项职责:

# 内网权威区域
internal.example.com:53 {
    file /var/lib/coredns/internal.example.com
    transfer {
        to * 10.1.2.3
    }
    prometheus 0.0.0.0:9153
    log
    errors
    cache 30
}

# 递归转发
.:53 {
    hosts /etc/coredns/hosts.internal
    forward . 223.5.5.5 119.29.29.29 180.76.76.76 {
        health_check 5s
    }
    cache 30 5400
    loadbalance
    prometheus 0.0.0.0:9153
    log . {
        class error
    }
    errors
}

这份配置的关键点:internal.example.com 区域直接从区域文件加载并允许 AXFR 给备用 DNS;根区域 . 走转发,通过 hosts 插件把内网机器名映射到固定 IP,loadbalance 插件会对多 A 记录做轮询,cache 30 5400 把负缓存设为 30 秒、正缓存设为 90 分钟,log 只记录错误类查询,避免日志量过大。

五、浏览器端可视化管理

CoreDNS 本身是纯 CLI 工具,没有官方 Web UI。但它内置了几个可通过浏览器访问的端点,配合监控系统就能搭出完整的可视化面板。

  • Prometheus 指标:在 Corefile 中加入 prometheus :9153,启动后浏览器访问 http://localhost:9153/metrics,即可看到 coredns_dns_requests_total、coredns_dns_request_duration_seconds、coredns_cache_hits_total、coredns_cache_misses_total、coredns_forward_healthcheck_broken_total、coredns_panics_total 等指标。把它接入 Prometheus + Grafana 后,就有了完整的浏览器面板。Grafana 官方 Dashboard ID 为 6523,导入后即可看到 QPS、延迟分位数、缓存命中率、上游健康度等曲线。
  • pprof 性能剖析:在 Corefile 中加入 pprof :6053,浏览器访问 http://localhost:6053/debug/pprof/,可查看 goroutine 数量、堆分配、CPU profile、阻塞分析。线上排查内存泄漏或 CPU 飙高时非常有用。
  • Kubernetes 场景:直接使用 coredns/helm chart 部署,配合 Kubernetes Dashboard 或 Lens 即可在浏览器里看到 CoreDNS Pod 日志、资源曲线、ConfigMap 内容。
  • JSON 日志接入 Loki/ELK:启动参数加 -log-format=json,日志会输出 RFC3339 时间戳、level、msg、plugin 等结构化字段,可以直接 Filebeat/Fluent Bit 采集到 Elasticsearch 或 Loki,在 Kibana/Grafana 里按查询名、按插件筛选日志。

Corefile 示例(同时开启指标、pprof、缓存、JSON 日志):

.:53 {
    prometheus :9153
    pprof :6053
    forward . 223.5.5.5 119.29.29.29
    cache 30 5400
    log
    errors
}

六、API 用法

CoreDNS 没有传统意义上的 REST API,它的"API"就是 DNS 协议本身。下面给出最常用的 curl/dig 调用方式,覆盖日常排障、监控、编程集成三类场景。

6.1 用 dig 做递归查询

dig @127.0.0.1 -p 53 www.baidu.com A +noall +answer
dig @127.0.0.1 -p 53 www.baidu.com AAAA
dig @127.0.0.1 -p 53 mx.163.com MX
dig @127.0.0.1 -p 53 -x 8.8.8.8      # 反向解析
dig @127.0.0.1 -p 53 version.bind CH TXT  # CHAOS 类
dig @127.0.0.1 -p 53 www.baidu.com A +trace  # 追踪递归路径

6.2 用 kdig/curl 测试 DoT/DoH

# DoT (RFC 7858),假设 CoreDNS 监听 853 端口
kdig -d @127.0.0.1 +tls www.example.com
# DoH (RFC 8484)
curl -s "https://dns.example.com/dns-query?name=www.example.com&type=A" \
  -H "accept: application/dns-json"

6.3 通过 Prometheus HTTP 拉指标

curl -s http://127.0.0.1:9153/metrics | grep coredns_dns_requests_total | head
curl -s http://127.0.0.1:9153/metrics | grep -E "cache_hits|cache_misses"

6.4 Go 代码调用示例(基于 miekg/dns)

package main
import (
    "fmt"
    "github.com/miekg/dns"
)
func main() {
    m := new(dns.Msg)
    m.SetQuestion("www.google.com.", dns.TypeA)
    r, err := dns.Exchange(m, "127.0.0.1:53")
    if err != nil { panic(err) }
    for _, ans := range r.Answer {
        fmt.Println(ans.String())
    }
}

6.5 热加载配置

CoreDNS 收到 SIGUSR1 会重新加载 Corefile(不重启进程):

sudo pkill -USR1 coredns
# 或者通过 systemd
sudo systemctl reload coredns

6.6 常用启动参数速查

-conf Corefile       指定配置文件
-dns.port 1053       覆盖 Corefile 中的端口
-log-format=json     结构化 JSON 日志
-pidfile path        写 PID 文件
-ports=53            一次性指定所有监听端口
-version             打印版本并退出
-plugins             打印编译进二进制的插件列表

七、常见问题 FAQ 与相关链接

Q1:启动时报 "listen udp :53: bind: address already in use"。
A:系统自带的 systemd-resolved 或 dnsmasq 占用了 53 端口。执行 sudo systemctl stop systemd-resolved && sudo systemctl disable systemd-resolved,然后编辑 /etc/resolv.conf 把 nameserver 改成 127.0.0.1;或者把 CoreDNS 改到 1053 端口:coredns -dns.port 1053。

Q2:普通用户启动报错 "bind: permission denied"。
A:53 是特权端口。给二进制加 capability:sudo setcap cap_net_bind_service=+ep /usr/local/bin/coredns,或用 root 启动,或改到 1053。在容器里需要确保 Docker 运行时没有 drop 掉 NET_BIND_SERVICE capability。

Q3:forward 后查询超时,但上游 8.8.8.8 直连正常。
A:检查服务器是否在国内网络环境,8.8.8.8 可能被阻断。换成国内公共 DNS:forward . 223.5.5.5 119.29.29.29。同时确认 UDP 53 出站未被防火墙拦截,必要时在 forward 块里加 policy sequential 按顺序试。

Q4:Kubernetes 里 CoreDNS 偶尔 5 秒延迟。
A:这是著名的 "DNS 5 秒延迟" 问题,通常由上游 UDP 响应被丢、NodeLocal DNSCache 未启用引起。推荐部署 NodeLocal DNSCache,并在 Corefile 中把 forward 加上 prefer_udp 关闭,强制使用 TCP 转发。

Q5:如何开启 JSON 日志方便采集?
A:启动参数加 -log-format=json:./coredns -conf Corefile -log-format=json。日志会输出 RFC3339 时间戳、level、msg、plugin 等结构化字段,可直接接入 Loki/ELK。注意查询日志仍需在 Corefile 中写 log 插件。

Q6:cache 插件命中率低。
A:检查 Corefile 是否忘记写 cache;缓存大小默认 10000 条,大流量场景可加 cache 30 5400(负缓存 30 秒、正缓存 5400 秒)。如果是 K8s 集群,确保每个 Node 上都跑了 NodeLocal DNSCache,避免跨节点查询。

Q7:Corefile 语法错误导致启动失败。
A:CoreDNS 启动时会把错误打到 stderr,常见错误是花括号不匹配、插件名拼错、端口被另一个 server block 占用。可以先用 coredns -conf Corefile -dns.port 1053 在测试环境验证。

Q8:如何做 DNSSEC 签名?
A:在 Corefile 中用 file 插件加载已签名的区域文件,再配合 dnssec 插件指定 KSK/ZSK 密钥文件。CoreDNS 目前只支持 NSEC,不支持 NSEC3。

Q13:CoreDNS 能否作为公司内网的统一 DNS?
A:完全可以。典型架构是:两台 CoreDNS 主备,前面用云负载均衡或 keepalived 做 VIP,Corefile 中用 hosts 插件管理固定 IP、用 file 插件管理权威区域、用 forward 插件做递归转发,再配合 prometheus 插件把指标送到 Prometheus。这套方案在很多互联网公司已经替代了传统的 BIND + dnsmasq 组合。

Q14:如何升级 CoreDNS?
A:二进制部署直接下载新版替换即可,systemctl restart coredns。Docker 部署 docker pull coredns/coredns:latest && docker rm -f coredns && docker run ...。K8s 部署 helm upgrade coredns coredns/coredns。升级前建议先在测试环境验证 Corefile 兼容性,CoreDNS 遵循严格的弃用策略:x.y.z 预告、x.(y+1).0 兼容、x.(y+1).1 移除旧语法。

相关链接:

Q9:如何在国内服务器上获得低延迟解析?
A:在 forward 块中同时配置多个国内上游,并使用健康检查:forward . 223.5.5.5:53 119.29.29.29:53 180.76.76.76:53 { health_check 5s }。这样 CoreDNS 会自动剔除故障节点,请求延迟可以控制在 10ms 以内。

Q10:CoreDNS 与 BIND 相比有什么优劣?
A:CoreDNS 配置更简洁、插件化程度高、原生支持云后端和 DoT/DoH/DoQ,容器化友好;BIND 历史悠久、权威区域功能更强、支持 NSEC3、TSIG 等高级特性。做递归解析和 K8s 服务发现首选 CoreDNS,做企业级权威 DNS 仍可考虑 BIND9。

Q11:如何限制只允许内网客户端查询?
A:使用 acl 插件:acl { allow 10.0.0.0/8 192.168.0.0/16 172.16.0.0/12; drop }。这样外网客户端查询会被直接丢弃,避免被用作放大攻击。

Q12:CoreDNS 暴露在公网安全吗?
A:不建议把递归解析器直接暴露在公网,会被 DNS 放大攻击滥用。如果必须对外提供服务,请配合 acl 插件、response policy zone(rpz)插件限速,并启用 DoT/DoH 加密传输。

评论交流

文章目录