Loading...

文章背景图

CoreDNS 超详细部署指南:从二进制到 Kubernetes 全流程

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

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 场景下做服务发现。

项目仓库地址: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 采用语义化版本,最新稳定版可在 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 即可热加载,不需要重启进程。

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 调整参数即可。

五、浏览器端可视化管理

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。

相关链接:

评论交流

文章目录