ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

CoreDNS上游DNS配置指南:从原理到最佳实践

CoreDNS上游DNS配置指南:从原理到最佳实践 1. 先搞清楚CoreDNS什么时候会把请求甩给上游1.1 一次Pod域名解析的完整链路先别急着改配置我建议每个维护K8s集群的人都先把这条链路在脑子里过一遍应用在Pod里发起请求读取/etc/resolv.conf里面写着nameserver指向一个ClusterIP——也就是CoreDNS的Service地址。请求到CoreDNS之后它先判断这个域名是不是集群内部域名比如后缀是cluster.local的Service名、Pod名这类。判断命中内部逻辑直接走内存里的记录返回。判断不命中就会进入转发逻辑也就是我们这篇文章的主角上游DNS服务器。所以“为CoreDNS配置上游DNS”这件事本质是告诉CoreDNS集群里查不到的域名你替我转发给谁。转发配置写在CoreDNS的Corefile里具体是forward这一段。默认安装的K8s集群Corefile长这样.:53 { errors health ready kubernetes cluster.local in-addr.arpa ip6.arpa { pods insecure fallthrough in-addr.arpa ip6.arpa } prometheus :9153 forward . /etc/resolv.conf cache 30 loop reload loadbalance }注意这一行forward . /etc/resolv.conf。意思是所有不属于集群内部的域名都转发给/etc/resolv.conf文件里写着的DNS。这个文件是CoreDNS容器自己启动时挂进去的通常继承自宿主机。1.2 默认配置的“坑”在哪里问题就出在这个“继承自宿主机”上。我见过很多集群刚装好时Pod解析外网域名也正常大家就没管上游配置。但慢慢会出现两种情况非常典型第一种宿主机用的是systemd-resolved/etc/resolv.conf指向127.0.0.53。这个地址在宿主机上能用但CoreDNS容器里访问127.0.0.53访问的是容器自己的回环地址根本不通。表现为Pod里偶尔解析超时、隔一段时间又自己好了因为systemd-resolved的缓存或者多副本调度导致时通时不通。第二种宿主机配了多个DNS比如公司内网DNS在前、公网DNS在后。CoreDNS按顺序转发遇到第一个不可达的超时才去请求下一个一个解析请求可能要等好几秒。线上业务一旦出现域名解析缓慢排查到CoreDNS这里常常就是上游配置不可控。所以给CoreDNS配置明确、可靠的上游DNS不是“锦上添花”而是集群稳定性的基础动作。尤其当你开始管理不止一套集群、或者集群要接入企业内部域名体系时这个配置就是刚需。1.3 forward插件里的几个关键参数要配置好上游先要了解CoreDNSforward插件的几个核心参数它们直接决定转发行为的可靠程度参数作用我常用的值max_concurrent限制同时进行中的转发请求数超过后丢弃并返回SERVFAIL防止上游被打爆根据集群规模一般1000~5000policy上游选择策略random随机round_robin轮询sequential顺序两个以上上游推荐randomhealth_check对上游做健康检查的间隔默认0.5s保持默认即可prefer_udp优先用UDP协议与上游通信公网DNS适用内网视情况开except排除某些域名不走这个转发组配合多上游分组时有用这里我特别想强调max_concurrent。很多同学配置上游只会写IP其他参数一概不写。平时没事一旦某个上游DNS出现慢查询CoreDNS的goroutine会疯狂堆积最后整个Pod内存暴涨、OOM重启。加上max_concurrent后超出阈值的请求快速失败反而保护了CoreDNS本身。这个参数在高并发场景下比你想的重要得多。2. 上游DNS怎么选公共DNS、内网DNS还是云厂商VPC地址2.1 纯公网场景明确指定公共DNS如果你的集群只跑公网应用没有内部私有域名那么上游选择很简单写一个或两个公共DNS。常见的公共DNS有223.5.5.5 / 223.6.6.6阿里DNS119.29.29.29腾讯DNS114.114.114.114114DNS8.8.8.8 / 8.8.4.4Google DNS注意国内网络环境下可能不稳定这里有个容易忽略的点不要照抄别人的配置。我在国内机房维护的集群用8.8.8.8做上游偶尔会有解析超时因为UDP包出国链路不稳定。后来换成223.5.5.5问题明显减少。选公共DNS的第一原则是从你的集群节点网络到目标DNS的延迟要低丢包要少。2.2 有内网域名的场景公共DNS解决不了的事企业内部一般都有私有域名体系比如gitlab.company.local、mysql.internal。这些域名在公网DNS上不存在如果CoreDNS把所有请求都转给公共DNS内网域名就会解析失败。这时候上游配置必须包含内网DNS。但要注意一个关键问题如果把内网DNS和公网DNS写在同一组forward里内网域名会被公网DNS返回NXDOMAIN解析直接失败。因为forward是按顺序或随机挑一个上游不会因为你查的是内网域名就自动选内网DNS。解法有三种我按推荐程度排一下只配置内网DNS做上游让内网DNS自己具备公网递归能力。大多数企业的内网DNS本身就是递归DNS可以同时解析内网和公网域名。这是最省心的方案Corefile里只写一组上游。用不同的Zone分别指定上游内网域名走内网DNS其他域名走公网DNS。Corefile需要写两组forward用不同的域名Zone区分。在CoreDNS里配置hosts插件把少量内网域名直接写死。适合内网域名数量很少的场景比如就一两个。我的建议是优先方案1因为它最简单可靠。但如果你无法控制内网DNS的递归行为那就用方案2下面第三章会给具体配置。2.3 公有云上部署K8s优先用VPC提供的DNS如果你把K8s集群部署在云厂商的VPC网络里有个更好的选择VPC内部的DNS服务器地址。每家云厂商都有自己的VPC DNS通常可以在VPC详情页查到。这个地址有天然优势物理距离近解析延迟低支持VPC内部实例的私网域名解析比如RDS、Redis的内网地址本身自带公网递归能力内外网域名都能解析不占用公网带宽很多人在云上部署集群图省事直接把上游写成forward . /etc/resolv.conf这样其实也能工作因为云服务器/etc/resolv.conf里写的就是VPC DNS。但我个人更建议显式写出来因为一旦你迁移节点、重建镜像/etc/resolv.conf可能变化配置就不可控了。显式写成forward . 100.100.2.136 100.100.2.138把IP换成你所在VPC的实际DNS地址即可。2.4 选型总结对照表场景推荐上游原因纯公网应用公共DNS如223.5.5.5解析快、配置简单有内网域名内网DNS需支持公网递归同时覆盖内外网解析公有云VPC内VPC DNS地址低延迟、支持私网记录混合复杂域多组forward按Zone区分精细控制但配置复杂度高选型原则就三条从节点网络可达、延迟低、覆盖你需要的所有域名记录。写在这三原则之外的DNS都是给自己埋雷。3. 实操修改Corefile的完整流程与配置示例3.1 查看当前CoreDNS配置动手之前先看一眼现状。执行kubectl -n kube-system get configmap coredns -o yaml输出里有Corefile:这一段就是你当前生效的配置。同时看一下CoreDNS的部署方式和Pod状态kubectl -n kube-system get pods -l k8s-appkube-dns正常情况下应该是多个副本或配合autoscaler动态扩缩容。看完这两条命令你就可以放心改了。3.2 修改ConfigMap三种配置示例修改ConfigMap的方式有两种一种是kubectl edit一种是先导出再kubectl apply。我习惯用kubectl edit改完直接保存效率高kubectl -n kube-system edit configmap coredns示例A全部走指定公共DNS最简单粗暴把forward .那行直接替换forward . 223.5.5.5 119.29.29.29 { max_concurrent 2000 health_check 0.5s }这里我加了max_concurrent和显式的health_check比裸写两个IP要稳。前一个参数防止上游慢查询拖垮CoreDNS后一个参数确保上游挂掉时能快速移除。示例B全部走内网DNS推荐如果内网DNS具备公网递归能力这是最优解forward . 10.20.0.2 10.20.0.3 { max_concurrent 2000 health_check 0.5s }10.20.0.2和10.20.0.3是你内网DNS的地址。这样写之后内网域名、公网域名都走内网DNS网关一套配置解决问题。示例C按域名Zone区分上游前面提到的混合方案适合内网DNS不支持公网递归或者你想精细控制的情况.:53 { errors health ready kubernetes cluster.local in-addr.arpa ip6.arpa { pods insecure fallthrough in-addr.arpa ip6.arpa } prometheus :9153 cache 30 loop reload loadbalance # 内网域名走内网DNS forward company.local 10.20.0.2 10.20.0.3 { policy random } # 其他域名走公共DNS forward . 223.5.5.5 119.29.29.29 { max_concurrent 2000 health_check 0.5s } }注意顺序CoreDNS匹配域名时更具体的Zone优先所以company.local的转发会先命中。这种配置下company.local结尾的域名走内网DNS其他域名走公网DNS互不干扰。3.3 保存生效与验证Corefile修改保存后配置更新有两条路径第一如果你的ConfigMap配置里还有reload插件默认有CoreDNS会每隔30秒自动检测配置变化并热加载不需要重启Pod。等一会儿配置就自动生效。第二你手动滚动重启CoreDNS Pod强制加载新配置kubectl -n kube-system rollout restart deployment/coredns改完配置之后验证是必须的。我一般分两步验证第一步从集群里起一个临时Pod用nslookup测解析kubectl run -it --rm test-dns --imagebusybox:1.28 --restartNever -- nslookup kubernetes.default.svc.cluster.local kubectl run -it --rm test-dns --imagebusybox:1.28 --restartNever -- nslookup www.baidu.com第一个是内网Service解析第二个是公网域名解析。两个都能返回正确IP说明配置基本没问题。第二步看CoreDNS日志有没有异常kubectl -n kube-system logs -l k8s-appkube-dns --tail20如果看到SERVFAIL、connection refused这类报错通常就是上游DNS地址不可达或网络不通。3.4 别忘了检查kubelet的clusterDNS参数这里有一个很多教程不会强调、但真实环境里经常踩的关联点Pod里的/etc/resolv.conf指向的nameserver是kubelet启动参数--cluster-dns决定的跟CoreDNS的Service IP必须一致。默认kubeadm安装时--cluster-dns会自动设为CoreDNS Service的IP通常是10.96.0.10。但如果你不是用kubeadm装的集群或者手动改过CoreDNS Service的ClusterIP就可能出现一种诡异情况你改了CoreDNS上游配置但Pod里请求根本不发到CoreDNS而是发到一个没人监听的IP上解析一直超时。排查方法很简单# 查看CoreDNS的Service ClusterIP kubectl -n kube-system get svc kube-dns # 到任意节点看kubelet进程参数 ps aux | grep kubelet | grep cluster-dns两个值必须一致。如果不一致优先保证 kubelet 的--cluster-dns指向CoreDNS Service的IP修改后重启kubelet。这一步不做上游配置改得再对也是白搭。4. 改了配置不生效我踩过的几个坑与排查链路4.1 坑一reload插件被移除配置改了但没生效有一次我帮朋友排查一个集群他告诉我改了CoreDNS的上游配置等了半小时也没生效手动重启Pod之后过一阵又变回去了。我看了一眼ConfigMap配置确实改对了但Corefile里没有reload插件。这个插件跟他用的“高可用部署方案”有关系——某些第三方部署方式会精简Corefile把reload去掉。没有reload插件CoreDNS就不会自动感知ConfigMap变化改配置必须手动重启Pod。而且在某些部署方案中CoreDNS的ConfigMap被kubernetes的某个Controller持续“纠正”你改了它又给你还原。所以排查的第一件事不是看配置内容对不对而是看这个配置有没有被什么东西托管、有没有被自动还原。排查链路改完ConfigMap等30秒再kubectl get cm coredns -o yaml看内容是否还是你改的。看Corefile里有没有reload插件。没有就滚动重启。如果是托管部署的需要去对应的部署配置里改而不是直接改集群里的ConfigMap。4.2 坑二内网域名解析到公网IP这个坑我在2.2节提到过但它在实际排查中太常见了值得单独拎出来讲。症状Pod里解析内网域名比如db.internal返回的IP是公网IP连不通或者有时通有时不通。原因上游只配置了公网DNS。内网DNS记录只有内网DNS服务器才知道公网DNS查询不到返回NXDOMAIN然后应用收到解析失败如果这个内网域名恰好在公网存在同名记录就直接返回了公网IP。处理方式在前面已经给了方案要么只配置内网DNS做上游要么用不同Zone分组转发。这里我要补充一个判断技巧# 在集群外、内网环境里执行 dig db.internal 内网DNS地址 dig db.internal 公网DNS地址如果你的内网DNS能正常返回内网IP而公网DNS返回公网IP或NXDOMAIN那问题就100%是上游选错了。别在CoreDNS配置里绕圈子直接换上游。4.3 坑三上游DNS可达性验证与防火墙拦截生产环境里CoreDNS和你选定的内网DNS之间不一定是通的。很多企业网络策略比较严格DNS走的是UDP 53端口中间任何防火墙、安全组策略漏配都会导致解析超时。验证方法要分层第一层从CoreDNS Pod内部测试上游DNSkubectl -n kube-system exec -it coredns-pod-name -- nslookup www.baidu.com 10.20.0.2这个命令绕过CoreDNS的配置直接向10.20.0.2发起DNS查询。如果能返回IP说明网络层通如果超时说明CoreDNS Pod到上游DNS的网络路径有问题。第二层如果网络不通检查节点安全组/防火墙是否放行UDP 53内网DNS服务器是否开启了“仅允许特定网段查询”跨VPC、跨机房场景下路由是否可达我遇到过一种比较隐蔽的情况内网DNS做了来源IP限制只允许特定网段的服务器查询。CoreDNS Pod的出口IP是节点IP节点IP不在白名单里查询就会被丢弃。表现为宿主机上dig正常Pod里解析超时。排查到这一层就不是K8s配置的问题了而是要去内网DNS服务器上加白名单。4.4 坑四转发环路解析请求陷入死循环这个坑新手不太容易遇到但一旦遇到就是事故级别。症状CoreDNS Pod的CPU持续飙升日志里刷一堆SERVFAIL整个集群域名解析逐渐瘫痪。原因上游DNS地址写成了CoreDNS自己的Service ClusterIP、或者某个节点的kubelet配置又把这个地址指回了集群。举例来说你配置forward . 10.96.0.10而10.96.0.10恰好是CoreDNS Service的IP请求就出现了A→B→A→B的无限循环。更隐蔽的是有些人在宿主机/etc/resolv.conf里把nameserver指向了CoreDNS的节点端口然后CoreDNS上游又用/etc/resolv.conf也会形成环路。排查判断也不难看CoreDNS日志如果同一个查询反复出现、时间戳还特别密集基本就是环路。解法是搞清楚每个环节的DNS流向确保上游地址指向的是“集群之外”的DNS服务器。4.5 坑五ConfigMap变更被Informer缓存延迟有时候改完ConfigMap立即去CoreDNS Pod里查发现还是老配置。这不一定是没生效可能是缓存延迟。CoreDNS的reload机制是每30秒扫描一次配置如果你刚改完马上看看到旧配置是正常的。稳妥的做法是改完等一分钟再验证。另外如果你用的是kubectl apply方式更新ConfigMap注意别把kube-system下其他字段误删。我见过有人导出整个namespace的yaml再apply把别的资源搞挂的。改ConfigMap还是老老实实用kubectl edit。5. 验证、监控与进阶从“能解析”到“解析得又稳又快”5.1 怎么科学验证配置真的生效了前面给的nslookup验证只证明了“能解析”。但“能解析”不等于“配置真的生效”。我有一个更严格的验证方式在CoreDNS Pod里直接查询一个内网域名观察返回结果。比如kubectl -n kube-system exec -it coredns-pod-name -- nslookup db.internal 127.0.0.1同时再执行一次kubectl -n kube-system exec -it coredns-pod-name -- nslookup www.baidu.com 127.0.0.1两次查询都通过CoreDNS自身发起能确认CoreDNS进程对内外网域名的处理逻辑是否符合预期。光在业务Pod里测有时候请求被别的缓存层拦截了测不出来真实效果。进阶一点的验证是看Prometheus指标。CoreDNS默认暴露了:9153的metrics接口里面有几个关键指标coredns_forward_requests_total转发请求总数coredns_forward_responses_total上游响应总数按rcode区分coredns_forward_request_duration_seconds转发请求耗时分布如果配置了多组上游还可以通过to标签区分是发给哪组上游的。我一般会做一个简单的看板把rcodeSERVFAIL的比例和耗时P99指标放到监控里一旦SERVFAIL比例上升立刻能发现。5.2 进阶配置特定域名走特定上游的完整写法前面示例C给了内网域名和公网域名分流的配置这里我再给一个更复杂的场景集群需要同时解析多个私有域名分别对应不同内网DNS。.:53 { errors health ready kubernetes cluster.local in-addr.arpa ip6.arpa { pods insecure fallthrough in-addr.arpa ip6.arpa } prometheus :9153 cache 30 loop reload loadbalance # 研发环境私有域名 forward dev.internal 10.20.0.2 { policy random } # 生产环境私有域名 forward prod.internal 10.30.0.2 { policy random } # 默认公网解析 forward . 223.5.5.5 { max_concurrent 2000 health_check 0.5s } }这个配置的好处是不同环境的私有域名解析互不影响而且每一组的相对独立某个上游挂了只会影响对应Zone的域名。缺点是随着Zone变多Corefile会越来越长维护成本上升。所以除非必要我一般建议优先用内网DNS统一递归的方案少在CoreDNS里堆Zone。5.3 性能调优缓存、并发、健康检查解析稳定之后下一步就是性能。CoreDNS有几个参数对性能影响很大cache插件的TTL策略。默认cache 30表示缓存阳性结果30秒。如果你的上游DNS响应慢可以适当调大比如cache 60。但注意TTL调大意味着域名记录变更后集群内生效时间变长。发布系统如果依赖短TTL切换IP缓存调大反而误事。max_concurrent参数前面已经强调过建议一定加上。数值设置多少需要观察实际流量。我一般先设2000然后看CoreDNS的goroutine数和拒绝率如果频繁触发SERVFAIL可以调大但超过5000就要想想是不是上游容量不够了。health_check参数默认0.5s一次健康检查。如果你用内网DNS这个频率没问题。但如果你用的上游是公网DNS且链路有一定丢包率0.5s的检查可能频繁误报。可以把间隔调到1s减少无意义探测。还有一点容易被忽略CoreDNS默认启用的loadbalance插件会对A记录做随机排序实现集群内负载均衡。如果你依赖DNS轮询做负载均衡这个插件一定要保留别手误删了。5.4 autopath插件的边界什么时候该关掉它默认生成的Corefile里没有autopath但有些高可用部署方案会加上。autopath的作用是优化Pod内/etc/resolv.conf里search域导致的DNS查询放大问题挺有用。但它有一个边界如果你配置了基于客户端IP的上游策略autopath可能破坏这个策略。举个例子内网DNS按请求来源IP判断该返回哪个环境的记录。CoreDNS开启autopath后会用“真实客户端IP”去查询上游但上游的IP白名单里只放行了CoreDNS Pod所在节点的IP这时候部分查询会被拒绝。表现是业务Pod里解析有时快有时慢完全没有规律。如果遇到这种问题建议在Corefile里把autopath临时注释掉对比一下解析成功率。一个通常会忽略的点是确认你的上游DNS是否对来源IP有策略限制。如果有autopath就需要谨慎开启或者调整上游策略。5.5 多集群场景的配置一致性管理最后说一个很多人后期才会遇到的问题你管理的集群从1套变成3套、5套每套集群的CoreDNS配置如果不一致排障时会非常痛苦。我现在的基本做法是把CoreDNS的Corefile用Git管理每套集群的差异只保留在上游DNS地址这一个变量上其他配置完全一致。这样以后升级集群、迁移节点CoreDNS配置可以直接复用不用每次重新排查一遍。另外一个运维习惯每次改CoreDNS配置都要记录“改了哪一行、为什么改、影响哪个集群”。这个习惯帮我避过好几次坑——比如某次我发现生产集群解析变慢去翻记录发现前一天刚把上游从内网DNS换成了公共DNS问题根源一眼就定位了回滚也只花了一分钟。如果你现在只管理一套集群可能觉得这些“工程化”的东西没必要。但说真的从第一天就养成配置版本化的习惯成本极低收益极高。等集群数量一多你就知道这有多香了。
返回列表