简介:CoreDNS 1.8.0 的 Docker 镜像归档包,主要用于 Kubernetes 1.21.2 集群环境。它面向需要离线部署或在内网中搭建 Kubernetes 平台的运维工程师,解决因网络限制无法直接拉取镜像的实际问题。整个压缩包内一共包含八个文件,主要类型为 json、tar 和 version。json 文件保存镜像配置与 manifest 清单,tar 文件承载镜像分层内容,version 文件记录版本与校验信息,整个归档包体积约四十点六二兆字节,非常适合在多台服务器之间快速拷贝和分发。目前已有四百零五人学习下载。通过 docker load 方式加载该归档,可快速获得 CoreDNS 镜像,为集群初始化、组件更新或私有仓库导入提供基础支持。同时,包内文件结构清晰完整,不仅便于使用者直接部署,也有助于理解 Docker 镜像打包、迁移和加载的基本逻辑,对容器化交付和故障排查同样具有参考价值。在 Kubernetes 1.21.2 维护过程中,这份归档也能有效减少镜像获取环节的耗时,尤其适合离线环境下的快速部署准备。
1. 集群突然无法解析 Service 名时,我翻出了这份 v1.8.0 资源
K8s 集群里最怕的不是 Pod 崩溃,而是 DNS 悄悄挂掉。某个周一早上,线上反馈服务间调用大面积超时,kubectl get pods看 CoreDNS 三个副本全是 Running,但 Pod 内nslookup kubernetes.default.svc.cluster.local直接报connection timed out; no servers could be reached。查了一圈才发现是升级 K8s 版本时 CoreDNS 镜像 tag 还停留在旧版,和新版 kubelet 的兼容逻辑对不上。那次之后我把coredns_v1.8.0.tar.gz这套资源单独存档,不管是离线内网部署还是给客户集群重建 DNS,都会优先从它开始。这份资源包含了 CoreDNS v1.8.0 的编译产物、配置模板和部署清单,适合正在做 K8s 版本对齐、需要手工维护 DNS 模块的运维和平台工程师;新手可以直接拿来跑通,熟手可以拿它当基线版本对比差异。
2. CoreDNS v1.8.0 先看懂再动手:这个版本改了什么、它适合谁
2.1 从 1.7 到 1.8:v1.8.0 的升级要点与行为变化
CoreDNS v1.8.0 算是一个分水岭版本。它把kubernetes插件对 K8s API 的交互方式做了调整,不再依赖已弃用的pod标签来关联 Pod IP,而是直接通过 EndpointSlice 发现后端;如果你的集群 K8s 版本在 1.19 以上,旧版 CoreDNS 在处理大量 Service 时会出现事件订阅积压,而 1.8.0 的kubernetes插件默认启用了更精简的 watch 逻辑,减少了对 API Server 的无效连接。
另一个明显的差异是forward插件的默认行为。v1.8.0 里forward的max_concurrent不再默认为 1000,而是根据 CPU 核数自动计算;同时新增了对dns64、reload等插件的细节修正。如果你从 1.7.x 直接跳过来,最直接的变化是 Corefile 里prometheus的监听地址写法必须从:9153改为0.0.0.0:9153,否则多网卡环境下可能出现指标只能本机访问的尴尬情况。
对于正在维护离线环境的工程师来说,v1.8.0 还修复了若干plugin/loop在特定网络插件下的误判问题。比如 Flannel 和 Calico 混用的集群,旧版可能把本机 DNS 请求误认为环路而直接返回 SERVFAIL,1.8.0 的环检测逻辑增加了对本地链路地址的排除,误报概率低了很多。版本行为的差异并不只在 Release Notes 里,很多是跑到生产环境才暴露的;用这份资源对比你的旧配置,能提前规避掉大部分行为突变。
2.2 压缩包里到底装了啥:目录结构、镜像与二进制选择
打开coredns_v1.8.0.tar.gz,里面实际是三类东西:源码目录、预编译的coredns可执行文件、以及一套推荐的部署 YAML。源码目录用于你在内网自己编译带特殊插件的版本;预编译二进制是基于官方默认插件集构建的,包含kubernetes、forward、cache、rewrite、template等常用的 30 多个插件。如果你的集群需要etcd、redis这种外部存储插件,就得用源码重编。
tar -xzf coredns_v1.8.0.tar.gz tree -L 1 # coredns_v1.8.0/ # ├── Dockerfile # ├── Makefile # ├── README.md # ├── corefile.sample # └── deploy/ # ├── coredns.yaml # └── coredns-configmap.yaml我一般会先看deploy/coredns.yaml里的镜像 tag 是否写成coredns/coredns:1.8.0。这个 yaml 是社区发布时同步更新的清单,里面 Deployment 副本数、Service 端口、ConfigMap 挂载路径都是现成的。如果你要改成二进制方式运行,注意Makefile里的go build参数,-ldflags "-s -w"能显著减小体积,但去掉后便于 gdb 调试。内网环境里我更推荐直接用预编译二进制,因为它的构建参数和官方镜像一致,出问题时可复现性更强。
corefile.sample里默认启用了kubernetes、forward、cache、health、prometheus五个插件,这个组合覆盖了绝大多数 K8s 集群的基础需求。模板文件里还写了reload插件,它的作用是在 ConfigMap 变更后自动加载新配置,不用手动 restart Pod。这是 v1.8.0 里我认为最值得保留的一个默认项,因为生产环境修改 Corefile 的频次其实很高,全靠手动kubectl rollout restart迟早漏一次。
2.3 为什么 K8s 1.20 前后最好对齐 CoreDNS 版本
官方从 K8s 1.20 开始把默认 DNS 完全切换到 CoreDNS,但很多集群是从 kube-dns 时代迁移过来的。如果你手里的 K8s 版本在 1.19 到 1.23 之间,CoreDNS v1.8.0 是一个兼容性很好的基线:它同时支持旧版的 Pod 识别逻辑和新版的 EndpointSlice 特性,API 层面的兼容窗口比 1.9.0 更长。1.9.0 开始要求集群必须开启 EndpointSlice 特性,而 1.8.0 仍然兼容传统的 Endpoints 接口,这在混合架构的集群里特别重要。
另一个选型理由是安全合规。很多内网集群的镜像仓库没有外网访问能力,所有镜像需要提前同步;coredns_v1.8.0.tar.gz里自带的 Dockerfile 可以直接构建出镜像,配合docker save导出后再导入私有仓库,整个过程不依赖外网。而且 1.8.0 的镜像基于精简的基础镜像,进行过安全加固,大小在 50MB 左右,比手动从 scratch 构建要稳妥。对于需要过等保或者甲方安全扫描的场景,这个版本的已知漏洞列表比 1.7.x 少了一大截。
3. 把 v1.8.0 跑进 K8s:从解压 tar.gz 到 Corefile 生效
3.1 解压与镜像准备:Dockerfile 与 tag 的对应关系
拿到压缩包后,第一步不是直接扔进集群,而是先构建镜像并推到本地仓库。如果你用的是预编译二进制,Dockerfile 里需要做的是把二进制拷进一个精简运行环境,我一般会基于alpine:3.13加ca-certificates和bash,避免证书缺失导致 upstream TLS 连接失败。
FROM alpine:3.13 RUN apk add --no-cache ca-certificates bash tzdata COPY coredns /usr/bin/coredns COPY corefile.sample /etc/coredns/Corefile EXPOSE 53 9153 ENTRYPOINT ["/usr/bin/coredns", "-conf", "/etc/coredns/Corefile"]构建参数说明:-conf指定配置文件路径,必须用绝对路径,否则容器内工作目录不同会报unable to open Corefile。ca-certificates是给forward插件做 DoT(DNS over TLS)用的,如果你只做内网转发,这步可省略,但保留后遇到外网递归查询时不用返工。tzdata用于日志时间正确显示,否则容器默认 UTC 时间,排查问题对着 K8s 事件时间会对不上。
构建命令用docker build -t registry.local/library/coredns:v1.8.0 .。注意 tag 不要写latest,生产环境的 DNS 组件必须锁定具体版本。构建完成后做一次冒烟测试:docker run --rm -p 53:53 registry.local/library/coredns:v1.8.0 -conf /etc/coredns/Corefile,然后本机dig @127.0.0.1 example.org,能返回NOERROR才算镜像基础可用。
3.2 编写 Corefile:关键插件与参数逐行拆解
Corefile 是 CoreDNS 的核心配置,格式类似 Caddyfile。v1.8.0 的语法和 1.7 基本一致,但插件的默认值有变化。下面这份配置是我在多个集群里调过的基础模板:
.:53 { kubernetes cluster.local in-addr.arpa ip6.arpa { pods insecure fallthrough in-addr.arpa ip6.arpa ttl 30 } forward . /etc/resolv.conf { max_concurrent 0 } cache 30 errors log . { class denial error } prometheus 0.0.0.0:9153 health 0.0.0.0:8080 ready 0.0.0.0:8181 reload }参数说明:pods insecure表示不校验 Pod 名安全性,允许查询pod-ip.namespace.pod.cluster.local这类记录;如果你的安全策略严格,改成pods verified增加验证,但会影响解析性能。fallthrough指定在当前 zone 内找不到记录时继续向后匹配,in-addr.arpa和ip6.arpa必须加,否则反向解析会直接返回 NXDOMAIN。forward的max_concurrent 0表示不限制并发,生产环境建议设成1000,避免上游被突发流量打挂。
cache 30表示正向缓存 30 秒,这个值是我测下来比较平衡的;太短起不到缓存作用,太长会导致 Service 的 Endpoint 变更后 Pod 还访问旧 IP。errors和log建议同时开,但log输出过多会拖累性能,所以用class denial error只记录解析失败和网络错误,正常查询不打日志。prometheus的地址必须写0.0.0.0:9153,很多监控抓取失败都是因为写成:9153导致只绑定本机回环。
3.3 挂载与部署:Deployment、Service、ConfigMap 完整清单
把 Corefile 放进 ConfigMap,然后挂载到 Deployment 的/etc/coredns目录。注意 CoreDNS 的 ConfigMap 改动后,reload插件默认每隔 30 秒检查一次变更,但不是立刻生效;如果你需要马上生效,可以kubectl rollout restart deployment/coredns。业界常见做法是把 ConfigMap 的hash打到 Deployment 的 annotation 上,这样每次配置变更都会触发滚动更新。
apiVersion: v1 kind: ConfigMap metadata: name: coredns namespace: kube-system data: Corefile: | .:53 { kubernetes cluster.local in-addr.arpa ip6.arpa { pods insecure fallthrough in-addr.arpa ip6.arpa } forward . /etc/resolv.conf cache 30 errors prometheus 0.0.0.0:9153 } --- apiVersion: apps/v1 kind: Deployment metadata: name: coredns namespace: kube-system spec: replicas: 3 selector: matchLabels: app: coredns template: metadata: labels: app: coredns spec: containers: - name: coredns image: registry.local/library/coredns:v1.8.0 args: - -conf - /etc/coredns/Corefile ports: - containerPort: 53 protocol: UDP - containerPort: 9153 volumeMounts: - name: config-volume mountPath: /etc/coredns volumes: - name: config-volume configMap: name: corednsDeployment 里要特别留意ports的protocol,CoreDNS 同时监听 UDP 和 TCP 的 53 端口,但申明时只写 UDP 会导致部分客户端用 TCP 查询失败。kubectl apply -f之后检查kubectl get pods -n kube-system -l app=coredns,状态 Running 且 ready 数为 1/1 才算正常。Service 一般沿用集群原有的kube-dns名称,端口 53 指向 Deployment,这个细节放到下一章避坑里详细讲。
4. 升级翻车现场:CoreDNS v1.8.0 常见的五个坑与排查手段
4.1 坑一:升级后 Pod 一直 CrashLoopBackOff,日志只有 plugin 解析错误
现象:kubectl logs里大量输出plugin/kubernetes: parse error或者unknown plugin "bind"。原因是旧集群的 Corefile 使用了某个在 v1.8.0 中已弃用的插件,或者插件参数写法变化。解决方法是先coredns -plugins查看当前二进制支持的插件列表,对比旧 Corefile 逐个核对;比如proxy插件在 1.5 之后就被forward替代,旧配置里proxy . /etc/resolv.conf在 v1.8.0 中直接报错。我通常在处理这类问题时会先把 Corefile 精简到最小可用集,确定 Pod 能启动后再加回插件,避免一次引入多个变量。
4.2 坑二:集群内域名解析超时,但 CoreDNS Pod 显示 Running
现象:Pod 内ping外网 IP 正常,nslookup kubernetes.default超时。排查发现 Pod 的/etc/resolv.conf指向的ClusterIP正确,但 CoreDNS 所在节点的kube-proxy没有正确创建转发规则。v1.8.0 默认监听 UDP 和 TCP 的 53,但 Service 只映射了 UDP 端口导致 TCP 查询无响应。解决方法是检查 Service 定义,确保name: dns和name: dns-tcp都指向 53,或者在 Corefile 里显式声明bind 0.0.0.0并让 Pod 网络可直达节点。还有一个常见原因是节点上的 conntrack 表满,dmesg -T | grep conntrack能看到nf_conntrack: table full,此时需要调大内核参数,跟 CoreDNS 本身无关。
4.3 坑三:多副本流量全部打到同一个副本,缓存热点严重
现象:3 副本中一个 Pod 的 QPS 是另外两个的 10 倍。原因是节点选择让所有请求都通过某个节点的 kube-proxy 转发,而 kube-proxy 对 UDP 的会话保持策略没有按 destination IP 做负载均衡。解决方法是给 Service 设置sessionAffinity: None,并检查 kube-proxy 的--udp-timeout参数,默认 4 秒会让 UDP 连接在超时后重选节点。更稳妥的方法是直接用 DaemonSet 模式跑 CoreDNS 且每个节点开启hostNetwork: true,客户端请求直接到达本机 53,天然不产生跨节点转发。
4.4 坑四:Service 名字混乱:kube-dns 与 coredns 并存导致指错
现象:部分 namespace 的 Pod 能解析,部分不能,kubectl get svc -n kube-system显示多个 DNS 服务,且 ClusterIP 不一致。原因是旧环境从 kube-dns 迁移时残留了未清理的 Service,而新部署的 CoreDNS Service 叫coredns,Pod 的resolv.conf同时配置了多个 nameserver。解决方法是统一使用kube-dns这个 Service 名作为唯一入口,把 CoreDNS 的 Service 改名或直接别名到kube-dns——这也是 gcr.io 官方镜像部署时的一贯做法。清理时注意先确认哪些工作负载还在使用旧 ClusterIP,避免一刀切导致所有 Pod 断网。
4.5 坑五:缓存命中率极低,DNS 压力全打给上游
现象:prometheus指标里coredns_cache_hits_total远小于coredns_cache_misses_total,上游 DNS 解析量上升。原因是 Corefile 的cache插件作用域与转发路径不匹配。v1.8.0 中cache只能缓存在它之前完成的查询结果,如果你把forward写在cache前面,所有请求都先被转发到上游,缓存完全失效。解决方法是调整插件顺序,正确顺序是bind、template、hosts、kubernetes、forward、cache,缓存必须放在最外层。此外,cache默认只缓存成功的响应,对于 NXDOMAIN 可以加一个cache 30后面的denial选项来缓存否定答案,减少上游压力。
5. 参数调优与常见配置:让 v1.8.0 贴合实际规模
5.1 缓存与并发:cache、template、forward 的常用参数组合
缓存参数直接影响解析时延和上游压力。对于单集群 500 节点以内的规模,cache 30是一个比较安全的起点。如果你想让每个 Pod 的 DNS 响应更稳定,可以在 cache 后面加prefetch 2 10s,表示缓存快过期时提前刷新 2 个条目,能避免热点域名集中过期造成的抖动。
.:53 { cache 30 { prefetch 2 10s } forward . /etc/resolv.conf { max_concurrent 300 expire 5s } }expire 5s控制上游连接的闲置超时,对于并发较高的集群,太长的 idle 会占用大量 fd。max_concurrent设 300 可以防止极端情况下 CoreDNS 被上游慢响应拖死,超过限制的请求会直接返回 SERVFAIL 而不是排队等待。如果你的集群里有大量*.svc.cluster.local查询,可以在kubernetes插件的ttl 5,让 Service 的解析结果更早过期,配合 Endpoint 变更的快速收敛。
5.2 日志与指标:log、errors、prometheus 插件的启用方式
日志插件可以控制输出级别,避免刷屏。log .如果不带class会打印所有查询和错误,生产环境磁盘很快被占满。我建议只记录错误和服务端异常。
log . { class denial error } errors { consolidation 1m 10 }errors插件里的consolidation参数可以把重复错误在 1 分钟内合并为一条,减少日志量。Prometheus 指标建议暴露给集群监控系统,v1.8.0 的指标名比老版本更规范,coredns_dns_responses_total{server="dns://:53"}可以按响应码聚合。想要在 Grafana 上区分每个插件耗时,需要额外开启dnstap插件,但这会带来写文件开销,一般集群不需要那么细的链路追踪。
5.3 自定义 DNS 与 hosts 插件:处理内部域名和 NodeName
有的集群内部需要解析一些非 K8s 域名的别名,比如把registry.local指向某台内部服务器。v1.8.0 的hosts插件支持从文件读取静态映射,比直接改/etc/hosts好维护。
.:53 { hosts /etc/coredns/hosts { fallthrough } kubernetes cluster.local }/etc/coredns/hosts文件内容格式就是两列 IP 和域名。注意必须在 hosts 插件后面写fallthrough,否则它匹配到域名但找不到记录时不会继续交给 kubernetes 插件处理。对于 NodeName 这类特殊解析,CoreDNS 的template插件可以动态生成记录,但复杂度较高,大多数场景直接用rewrite插件把特定前缀重写为 Service 名更省事。
5.4 健康检查与就绪探针:readiness 插件的配置细节
v1.8.0 的ready插件和账号探针配合,可以准确反馈插件加载是否成功。很多人的误解是health插件就够了,但health只检查进程存活,无法感知 Corefile 解析错误导致的插件不可用。
health 0.0.0.0:8080 ready 0.0.0.0:8181在 Deployment 里就绪探针用 TCP 检查 8181 端口,当就绪接口返回 HTTP 200 才算 ready。我遇到过一个案例:Corefile 里有一个插件加载失败,CoreDNS 进程仍然能启动,health正常,但所有域名查询返回 REFUSED,就是因为只配了health没配ready。从那以后我强制在所有部署里同时配好两个探针。
6. 一套实用的验证流程:dig 之外还该看什么
6.1 从集群内发起解析验证:检查 Pod 到 Service 的完整链路
升级完 CoreDNS 后,不要只在本机 dig 一下就算完事。进入一个业务 Pod 里执行以下命令,确保走的是集群 DNS 链路:
kubectl exec -it my-app-pod -- /bin/sh nslookup kubernetes.default.svc.cluster.local nslookup myservice.myns.svc.cluster.local如果第一个命令返回正确,第二个却 NXDOMAIN,多半是 Service 的 namespace 拼接错误,或者 Corefile 里kubernetes的 domain 参数写错。此时先确认 Pod 内的/etc/resolv.conf是否指向kube-dns的 ClusterIP,再确认 Service 是否存在。注意不要直接用dig命令,部分基础镜像没装 dig;nslookup 兼容性更好。
6.2 用压测暴露瓶颈:dnsperf 或者简单的并发 dig
线上出问题前通常有前兆,就是 QPS 涨但时延没变,直到某个阈值时延突然飙升。我习惯在升级后用dnsperf打在 CoreDNS 的 ClusterIP 上,观察它的每秒查询能力和错误率。
dnsperf -s 10.96.0.10 -p 53 -t 30 -T 4 -Q 5000 -l 10 -f /tmp/qfile参数说明:-T 4表示 4 个线程,-Q 5000表示每秒发 5000 个查询,-f qfile里放要压测的域名列表,注意域名结尾必须带点。观察Timeout和Connection reset by peer的占比,如果超过 1% 就需要调整max_concurrent或增加副本。压测时注意打满的可能是节点 conntrack 而不是 CoreDNS 本身,所以同时看节点ss -s的 UDP socket 数量。
6.3 构建自己的排错工具包:从 tcpdump 到 coredns 的 debug 接口
最后分享一个我一直在用的排错方法:在 CoreDNS 容器里用 tcpdump 抓 53 端口的包,确认请求有没有到达容器。v1.8.0 镜像默认没有 tcpdump,可以在启动命令里挂载 hostPath 的方式临时引入,或者直接用nsenter从节点上抓容器网络命名空间的包。
nsenter -t $(pgrep coredns) -n tcpdump -i any port 53 -w /tmp/dns.pcap抓到包后用 Wireshark 打开,重点看响应码是否是 NOERROR/ NXDOMAIN 之外的值。如果dig返回REFUSED,说明请求命中了 Corefile 但没有任何插件处理它,大概率是 zone 没有匹配;如果返回SERVFAIL,说明上游转发失败,重点查 forward 插件配置的 resolv.conf 是否可达。这套组合拳比单看日志高效得多。从那以后我每次动 CoreDNS,都会强制自己按「先看配置差异、再盯日志、再抓包」的顺序走一遍,省掉了大部分回头路,希望帮到你。
本文还有配套的精品资源,点击获取