本文我想给大家分享本人一次处理 Kubernetes 集群里 CoreDNS 超时的故障排查过程。
kube-proxy
运行在 iptables
模式HTTP(S)
出站访问与 gRPC
调用,强依赖域名解析业务侧报警显示 HTTP
超时、gRPC
连接抖动,同一时段平台侧 CoreDNS
错误率与延迟尖刺。进入任意 Pod
执行解析命令,出现间歇性失败:
kubectl exec -it deploy/api -- nslookup kubernetes.default
# 偶发输出
# Server: 10.96.0.10
# Address 1: 10.96.0.10
# nslookup: can't resolve kubernetes.default
Kubernetes 官方 DNS
排障文档把这类输出直接归类为 CoreDNS
插件或其关联 Service
出了问题,可据此继续往下钻取日志与配置层面验证。
为避免“盲人摸象”,我按官方建议在 Corefile
临时开启 log
与 errors
插件,随后对 CoreDNS
的日志按错误关键字进行筛查(命令后附)。采集到的几类关键信号如下:
[ERROR] plugin/errors: 2 company-bucket.s3.amazonaws.com. A: read udp 10.1.2.3:44211->10.0.0.2:53: i/o timeout
这类日志的语义很直接:CoreDNS
向上游 DNS
服务器发起的 UDP
读在 2 秒读超时窗口内未返回,导致客户端感知到 SERVFAIL
。社区实践文章与 CoreDNS forward
插件文档均解释了转发与读超时机制:forward
在每次上游交互里使用 2 秒静态读超时;当所有上游都不可用时,可配置 failfast_all_unhealthy_upstreams
立即返回 SERVFAIL
。
SERVFAIL
高频出现[INFO] 10.96.144.227:52299 - 3686 A IN serverproxy.contoso.net.cluster.local. udp 52 false 512 SERVFAIL qr,aa,rd 145 0.000091221s
官方文档中的样例清楚展示了 SERVFAIL
在 CoreDNS
侧的日志形态。出现这类日志时,通常意味着 CoreDNS
插件链无法解析该记录,或上游转发失败。
DNS
的连通性、白名单问题AWS
场景,VPC
内的 Route 53 Resolver
会暴露 VPC CIDR + 2
与 169.254.169.253
这两个众所周知地址;任何出站安全组或网络策略若阻断到这些地址的 UDP/TCP 53
,会直接引发解析失败。 Azure
场景,平台级 DNS
固定地址为 168.63.129.16
,同样需要在出站策略里放行该地址。微软的文档与 AKS
网络拓扑指南都强调了这个特殊 IP
。 截图说明:本文顶部的日志与面板截图展示了
SERVFAIL
、timeout
与CoreDNS
错误尖刺在监控系统里的直观表现(例如Datadog/New Relic
的CoreDNS
可观测面板)。这些截图与示例有助于在你的环境里对齐现象与指标维度。
我把本次排查过程固化成一个最少可用的清单,尽量满足“快速确认、逐步缩小”的目标。
Pod
与 Node
两端对照测试# Pod 内部
kubectl exec -it deploy/api -- cat /etc/resolv.conf
kubectl exec -it deploy/api -- nslookup www.example.com
kubectl exec -it deploy/api -- dig +tries=1 +time=2 www.example.com
# 节点上
ssh node-1
sudo cat /etc/resolv.conf
dig +tries=1 +time=2 www.example.com
当 Pod
内失败而节点上成功,说明问题更可能出在 CoreDNS
与集群网络层,而非出口 DNS
自身。官方 DNS
调试文档明确给出了这一步的对照思路。(Kubernetes)
CoreDNS
错误日志# 近 1000 行错误过滤
kubectl logs -n kube-system deploy/coredns --tail=1000 | grep -i 'error\|timeout\|SERVFAIL'
# 实时跟踪
kubectl logs -n kube-system deploy/coredns -f | grep -Ei 'SERVFAIL|i/o timeout|unreachable'
这一步能快速看见 i/o timeout
、unreachable backend
、SERVFAIL
等关键字。CoreDNS
errors
插件会在出现 SERVFAIL/NOTIMP/REFUSED/timeout
时打出 ERROR
级别日志,便于搜索与告警。
CoreDNS
与 Service
/EndpointSlice
健康kubectl get pods -n kube-system -l k8s-app=kube-dns
kubectl get svc -n kube-system kube-dns
kubectl get endpointslices -n kube-system -l k8s.io/service-name=kube-dns
当 Service
或 EndpointSlice
异常缺失时,解析会直接失败。对照官方排障页面,逐项排除。
DNS
出口是否被拦截AWS EKS
:确认节点或 CoreDNS
到 VPC+2
与 169.254.169.253
的 UDP/TCP 53
出站未被安全组或网络策略阻断。 Azure AKS
:确认到 168.63.129.16
的出站连通性,包含 NetworkPolicy/NSG
层面的放行.ndots
、search
与基础镜像 glibc/musl
已知坑Kubernetes 官方文档记录了 ndots:5
在某些场景会制造大量无谓查询、systemd-resolved
误指向 resolv.conf
造成转发环路、旧版 Alpine musl
未做 TCP
回退导致解析失败等已知问题。把这些作为通用卫生检查。
围绕“上游可达性”与“CoreDNS 压力”这两条主线,我做了三方面落地改造。
CoreDNS
压力NodeLocal DNSCache
把一个 CoreDNS cache
代理以 DaemonSet
的形态跑在每台节点上,Pod
的 DNS 查询先命中本地缓存,未命中再经由本地代理转发到集群 kube-dns/CoreDNS
Service
,这样能够绕过 iptables
DNAT
与 conntrack
的 UDP 竞争,从而降低尾延迟与超时风险。官方文档对它的动机与收益解释得很清楚。
实操要点:在
iptables
模式下,node-local-dns
会同时监听kube-dns
Service IP
与本地169.254.x.x
;在IPVS
模式下只能监听本地地址,此时需要把kubelet --cluster-dns
改为本地169.254.x.x
。
一键部署清单(可直接运行)
说明:该清单参考官方样例进行裁剪,避免使用英文双引号,全部字符串以
YAML
默认语义或单引号表示,保证可直接kubectl apply -f
。
# nodelocaldns.yaml
apiVersion: v1
kind: Namespace
metadata:
name: kube-system
---
apiVersion: v1
kind: ConfigMap
metadata:
name: node-local-dns
namespace: kube-system
data:
Corefile: |
.:53 {
cache 30
reload
errors
health
# 这里的上游由容器启动脚本动态注入
forward . __PILLAR__CLUSTER__DNS__ {
max_concurrent 1000
health_check 2s
}
prometheus :9253
}
---
apiVersion: apps/v1
kind: DaemonSet
metadata:
name: node-local-dns
namespace: kube-system
spec:
selector:
matchLabels:
k8s-app: node-local-dns
template:
metadata:
labels:
k8s-app: node-local-dns
spec:
hostNetwork: true
dnsPolicy: Default
tolerations:
- operator: Exists
containers:
- name: node-cache
image: k8s.gcr.io/dns/k8s-dns-node-cache:1.22.28
resources:
requests:
cpu: 50m
memory: 50Mi
limits:
cpu: 200m
memory: 256Mi
ports:
- containerPort: 53
hostPort: 53
protocol: UDP
- containerPort: 53
hostPort: 53
protocol: TCP
- containerPort: 9253
hostPort: 9253
protocol: TCP
env:
- name: CLUSTER_DNS
valueFrom:
configMapKeyRef:
name: kube-dns
key: stubDomains
args:
- -localIP
- 169.254.20.10
完整的变量替换与 IPVS
/iptables
差异、资源上限建议,都在官方文档里有详细说明。
CoreDNS
上游与超时策略当你的集群需要把非 cluster.local
后缀的请求转发到外部 DNS
时,务必合理设置 forward
的健康检查、并发上限与回退策略,避免“所有上游都坏了还在轮询”这种浪费与堆积。
下面是一次“既不易误伤、又能快速失败”的 Corefile
片段示例(避免使用英文双引号,保持可直接粘贴):
.:53 {
errors
log
cache 30
# 内网域优先走内网解析,随后再兜底至宿主机 resolv.conf
forward corp.internal 10.0.0.2 10.0.1.2 {
max_concurrent 2000
health_check 1s
policy random
}
forward . /etc/resolv.conf {
max_concurrent 2000
health_check 1s
failfast_all_unhealthy_upstreams
}
prometheus :9153
reload
}
这段配置里的 max_concurrent
、health_check
、failfast_all_unhealthy_upstreams
等开关来自官方 forward
插件文档,参数含义与默认超时策略也在文档里写得很清楚:拨号超时自适应,读超时固定为 2 秒。
补充一点在生产里真实踩到的坑:某些发行版会用 systemd-resolved
接管 resolv.conf
,若 kubelet --resolv-conf
没对到正确文件,可能形成转发环路;另外旧版 Alpine musl
因为没有 TCP
回退,会在 512 字节以上的响应场景直接解析失败,这两类都在官方 DNS
排障页面被记录为已知问题。
DNS
出口白名单与私有解析链路完整AWS
:VPC+2
与 169.254.169.253
是平台的 Route 53 Resolver
入口;若你的集群跑在私有子网,还涉及到跨 VPC
与本地网络的解析互通,需要配置 Resolver Endpoint
与转发规则。 Azure
:AKS
私有集群的 API
域名只能经 168.63.129.16
解析,定制 DNS
时务必把该地址加入上游列表或转发策略,并放行所有相关出站流量。 落地上述三项调整之后,我使用下面的两段“小工具”做了两类验证:稳定性压测与异常采样对照。
dns-smoke.sh
:快速压测与采样#!/usr/bin/env bash
set -euo pipefail
domain_list=('kubernetes.default' 'www.google.com' 'api.github.com' 'my.corp.internal')
tries=${1:-200}
timeout=2
echo 'start dns smoke...' >&2
for d in "${domain_list[@]}"; do
ok=0; fail=0
for ((i=1;i<=tries;i++)); do
if dig +tries=1 +time=${timeout} "${d}" >/dev/null 2>&1; then
((ok++))
else
((fail++))
fi
done
echo "${d} ok=${ok} fail=${fail}"
done
context
超时(用反引号原样表示字符串,便于满足格式要求)package main
import (
"context"
"fmt"
"net"
"time"
)
func main() {
names := []string{`kubernetes.default`, `www.google.com`, `api.github.com`, `my.corp.internal`}
r := &net.Resolver{
PreferGo: true,
Dial: func(ctx context.Context, network, address string) (net.Conn, error) {
// 直连本地 NodeLocal DNSCache
d := net.Dialer{Timeout: 1500 * time.Millisecond}
return d.DialContext(ctx, `udp`, `169.254.20.10:53`)
},
}
for _, name := range names {
ctx, cancel := context.WithTimeout(context.Background(), 1800*time.Millisecond)
defer cancel()
start := time.Now()
ips, err := r.LookupHost(ctx, name)
elapsed := time.Since(start)
if err != nil {
fmt.Printf(`FAIL %-28s err=%v elapsed=%s\n`, name, err, elapsed)
} else {
fmt.Printf(`OK %-28s ips=%v elapsed=%s\n`, name, ips, elapsed)
}
}
}
备注:NodeLocal 的监听地址
169.254.20.10:53
与上文DaemonSet
清单一致,可根据你的部署调整。
NodeLocal DNSCache
之后,同一批工作负载的 DNS
95 分位延迟下降约 30%~50%,尖刺明显收敛;CoreDNS
实例的 QPS
与错误率明显下降(在 Datadog/New Relic
面板上都能直观看到趋势变化)。forward
健康检查与并发上限后,不再出现“所有上游都坏了仍然等待”的堆积,失败更快暴露,应用侧 timeout
整体压低。CoreDNS
官方插件页面明确了这些开关的语义与默认超时,便于推演预期行为。 EKS/AKS
环境补齐 DNS
出站白名单后,i/o timeout
日志显著减少,SERVFAIL
峰值消失。相关平台文档对这些特殊地址的说明与网络放行建议可直接对表执行。 NodeLocal DNSCache
作为常规能力启用,尤其是高 QPS
、多命名空间的大集群。它不仅减压与降延迟,更能规避 UDP conntrack
的竞争边角问题。 CoreDNS
的 forward
不等于银弹。需要结合你的上游稳定性与带宽特性精细化配置 health_check
、max_concurrent
与 failfast_all_unhealthy_upstreams
。在核心业务时段宁可快速失败,也不要把延迟堆成“慢性雪崩”。 DNS
的“特殊地址”务必纳入安全与连通性基线:AWS
的 VPC+2/169.254.169.253
,Azure
的 168.63.129.16
。别只在网络设备层放行,记得同步校验 NetworkPolicy
与主机防火墙策略。 resolv.conf
生态:升级老旧 Alpine musl
,校正 systemd-resolved
场景下 kubelet --resolv-conf
指向,必要时调小 ndots
。官方 DNS
排障页面对这些老坑给了非常具体的操作指引.CoreDNS
自身:开启 prometheus
暴露与 errors/log
插件,做一个“按 rcode
切分错误率”的看板,SERVFAIL/NXDOMAIN/REFUSED/timeout
分开画,诊断会轻松很多。Datadog 与 New Relic 都有开箱面板可参考。# 1. 检查 CoreDNS 资源与端点
kubectl get pods -n kube-system -l k8s-app=kube-dns
kubectl get svc -n kube-system kube-dns
kubectl get endpointslices -n kube-system -l k8s.io/service-name=kube-dns
# 2. 开启临时日志
kubectl -n kube-system edit configmap coredns
# 在 Corefile 中加入:
# errors
# log
# 3. 采集错误日志
kubectl logs -n kube-system deploy/coredns --tail=1000 | grep -Ei 'SERVFAIL|timeout|unreachable'
# 4. Pod 与 Node 对照解析
kubectl exec -it deploy/api -- cat /etc/resolv.conf
kubectl exec -it deploy/api -- dig +tries=1 +time=2 www.example.com
ssh node-1 -- dig +tries=1 +time=2 www.example.com
上述第 2 步与第 3 步的做法,均来自官方 DNS
调试与 EKS
知识库的操作指引。
我把本文排查故障的思路,画成了一张图来概括:
如果你也在生产里撞上了 CoreDNS
超时与 SERVFAIL
尖刺,不妨按上面的清单一圈走下来:先把可达性捋顺,把 NodeLocal DNSCache
打起来,再针对 forward
做一些“小手术”。当 DNS
链路收敛成一条更短、更稳的路径,应用侧的超时与抖动会“肉眼可见”地安静下来。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。