K8s离线环境CoreDNS v1.8.0安装与避坑指南

发布时间:2026/11/6 2:04:13
K8s离线环境CoreDNS v1.8.0安装与避坑指南

简介:面向 Kubernetes 集群部署与运维场景的 CoreDNS v1.8.0 镜像离线包,主要服务于需要在 k8s v1.21.2 环境中搭建或修复集群 DNS 服务的工程师,尤其适合内网、离线环境或镜像拉取受限时使用,可解决集群初始化阶段 DNS 组件无法正常拉取的问题。压缩包共 8 个文件,以 4 个 json 清单与配置描述文件、2 个 tar 层数据包及 2 个 version 版本标识文件为主,整体约 40.62MB,结构符合容器镜像分层存储规范,便于直接导入本地镜像仓库或节点。已有 409 人学习下载,具备一定参考基础。借助该包可快速完成 CoreDNS 镜像的离线加载与版本核对,配合清单文件确认镜像层完整性,减少因网络问题导致的部署阻塞,适合作为 k8s 集群 DNS 组件部署与排错的配套素材。

1. 从一次 CoreDNS 镜像拉不下来说起:k8s 集群里最容易被忽略的组件

去年帮一个团队排查 k8s 集群里 DNS 解析间歇性超时的问题,翻遍 Service、Endpoint、NetworkPolicy 都没毛病,最后定位到 CoreDNS 的 Pod 一直卡在 ImagePullBackOff——节点在内网,官方镜像仓库根本连不上。这种场景太常见了:k8s 集群能起来,业务也能跑,唯独 DNS 这个“黑匣子”组件装不好,表现就是服务间调用偶发 5 秒超时、headless Service 解析不到、StatefulSet 的 Pod 之间互相找不到。CoreDNS v1.8.0 是 k8s 1.19~1.21 时代配套的经典版本,很多离线环境和老集群至今还在用它,而coredns_v1.8.0.tar.gz这种离线包正是为“节点不能出网”的场景准备的。这篇笔记按我实际装过一遍的顺序写:先列环境、再导镜像、配 Corefile、改 Deployment,最后验证解析链路,中间把踩过的坑一并记下来。

2. 装前把环境列清楚:版本、节点、镜像三件事

2.1 版本对齐:CoreDNS 和 k8s 的配套关系

CoreDNS 不是随便挑个版本就能往集群里塞的。k8s 各版本对 CoreDNS 有推荐配套,v1.8.0 大致对应 k8s 1.19~1.21 这一段,再往后的集群官方推荐已经推到 1.8.4、1.8.6 甚至 1.9.x。版本错配的典型症状不是起不来,而是“起来了但解析行为不对”——比如某些 k8s 版本依赖 CoreDNS 的kubernetes插件返回特定格式的 SRV 记录,老版本插件行为有差异,业务侧表现为 gRPC 服务发现失败。

我一般先确认集群版本,再决定要不要用这个包:

# 查看集群服务端版本,决定 CoreDNS 配套版本 kubectl version --short # 查看当前集群里 CoreDNS 的镜像和副本状态 kubectl -n kube-system get deploy coredns -o wide kubectl -n kube-system get pods -l k8s-app=kube-dns -o wide

第一条命令输出里的 Server Version 是判断依据;后两条用来确认集群里是否已经有 CoreDNS 在跑。如果已经有,这次操作本质是“替换镜像版本”,不是全新安装,后面改 Deployment 时要保留原有的 tolerations、affinity 等字段,别整段覆盖。

提示:如果集群版本明显高于 1.21,用 v1.8.0 之前先想清楚——不是不能跑,而是官方配套已经变了,遇到解析异常时排查成本会高很多。

2.2 节点与运行时:镜像要导到每一台可能调度的节点

离线包的核心价值在镜像。coredns_v1.8.0.tar.gz解开后通常是docker save出来的镜像 tar,导入方式取决于节点用的是 Docker 还是 containerd。这一步最容易翻车的地方是:只往 master 导了镜像,结果 CoreDNS 被调度到 worker 节点,照样 ImagePullBackOff。

先确认运行时类型:

# 判断节点容器运行时,输出含 docker:// 还是 containerd:// kubectl get nodes -o wide # 在目标节点上确认运行时命令是否存在 which docker ctr nerdctl 2>/dev/null

kubectl get nodes -o wide的 CONTAINER-RUNTIME 列直接告诉你答案。Docker 节点用docker load,containerd 节点用ctr -n k8s.io images import,注意命名空间必须是k8s.io,否则 kubelet 看不到导入的镜像——这是 containerd 环境里最高频的坑之一。

2.3 解包与镜像清单核对

动手前先把包解开看清楚里面有什么,别急着 load:

# 解包并查看内容,确认是镜像 tar 还是带 yaml 的整套文件 tar -tzf coredns_v1.8.0.tar.gz # 解压到工作目录 mkdir -p /opt/coredns-offline && tar -xzf coredns_v1.8.0.tar.gz -C /opt/coredns-offline ls -lh /opt/coredns-offline

tar -tzf只列目录不解压,用来快速判断包结构;确认无误再解压。如果包里同时带了 Deployment yaml 和 ConfigMap,后面配置 Corefile 时可以直接参考,但不要盲目 apply——离线包里的 yaml 往往带着打包者自己的副本数、资源限制,直接套到你的集群上可能资源不够或副本过多。

3. 导入镜像与替换 Deployment:把 v1.8.0 塞进集群

3.1 Docker 运行时节点的镜像导入

Docker 节点最省事,docker load之后镜像 tag 就还原了:

# 在每一台可能运行 CoreDNS 的节点上执行 docker load -i /opt/coredns-offline/coredns_v1.8.0.tar # 确认镜像已导入,tag 应为 coredns/coredns:1.8.0 docker images | grep coredns

-i指定输入文件。load 完成后务必用docker images核对 tag——有些离线包在打包时改过 tag,比如打成coredns:latest或带私有仓库前缀,这时候要么重新 tag,要么改 Deployment 里的 image 字段,两边必须对上。我一般倾向于重新 tag 成官方名字,保持和上游 yaml 一致,减少后续维护时的困惑。

3.2 containerd 运行时节点的导入方式

containerd 节点没有 docker 命令,用 ctr:

# 注意 -n k8s.io,这是 kubelet 使用的命名空间 ctr -n k8s.io images import /opt/coredns-offline/coredns_v1.8.0.tar # 核对导入结果 ctr -n k8s.io images ls | grep coredns

-n k8s.io不能省,省了镜像会进 default 命名空间,kubelet 完全看不见,表现就是“明明导入了还是拉不到”。如果节点上装了 nerdctl,也可以用nerdctl -n k8s.io load -i,效果一样。导入后如果 tag 不对,用ctr -n k8s.io images tag修正。

3.3 修改 Deployment 的镜像与拉取策略

镜像到位后改 Deployment。关键是imagePullPolicy必须设成IfNotPresent或Never,否则 kubelet 还是会尝试联网拉取:

# 直接改镜像,保留其余字段不动 kubectl -n kube-system set image deploy/coredns \ coredns=coredns/coredns:1.8.0 # 把拉取策略改成 IfNotPresent,避免离线环境反复重试 kubectl -n kube-system patch deploy coredns --type=json -p='[ {"op":"replace","path":"/spec/template/spec/containers/0/imagePullPolicy","value":"IfNotPresent"} ]'

set image的语法是容器名=镜像,容器名要从kubectl get deploy coredns -o yaml里确认,默认叫coredns。patch 用 JSON Patch 精确改一个字段,比edit安全,不会误改其他配置。改完观察滚动更新:

kubectl -n kube-system rollout status deploy/coredns --timeout=120s kubectl -n kube-system get pods -l k8s-app=kube-dns -o wide

rollout status会阻塞到更新完成或超时,超时说明新 Pod 没起来,接着看 describe 和 logs。

3.4 Corefile 配置:v1.8.0 的插件顺序有讲究

CoreDNS 的行为全在 Corefile 里。v1.8.0 的默认配置大致是kubernetes、prometheus、forward、cache、loop、reload、loadbalance这一套,插件顺序影响处理链路,不能随意调换:

# 查看当前 Corefile kubectl -n kube-system get configmap coredns -o yaml # 编辑 Corefile(改完保存即生效,reload 插件会热加载) kubectl -n kube-system edit configmap coredns

一个能跑通的典型 Corefile 长这样:

.:53 { errors health { lameduck 5s } ready kubernetes cluster.local in-addr.arpa ip6.arpa { pods insecure fallthrough in-addr.arpa ip6.arpa ttl 30 } prometheus :9153 forward . /etc/resolv.conf cache 30 loop reload loadbalance }

kubernetes插件的pods insecure决定 Pod 记录是否返回,ttl 30控制记录缓存时间,调小能更快感知 Endpoint 变化但会增加 apiserver 压力;forward . /etc/resolv.conf把集群外域名转给节点 DNS,离线环境里如果节点 DNS 不可达,外部域名解析会超时,这时候要改成内网 DNS 地址;loop插件用来检测转发环路,缺了它遇到环路会直接把 CPU 打满。改完 ConfigMap 后reload插件大约 30 秒内热加载,不用重启 Pod。

4. 避坑与排查:装 CoreDNS 时最容易翻车的五件事

4.1 现象:Pod 一直 ImagePullBackOff,节点上明明有镜像

原因通常是三种:镜像导入了但 tag 和 Deployment 里写的不一致;containerd 节点导入时漏了-n k8s.io;imagePullPolicy还是Always。解决顺序是先crictl images或docker images核对 tag,再kubectl describe pod看事件里的完整镜像名,最后确认拉取策略。我遇到过打包时 tag 带docker.io/前缀、Deployment 里写的是短名的情况,两边字符串必须完全一致。

4.2 现象:CoreDNS 起来了,但集群内域名解析全部超时

先看 CoreDNS 日志里有没有plugin/loop报错。loop检测到转发环路会直接让实例不健康,典型原因是节点/etc/resolv.conf指向的 DNS 又把查询转回了 CoreDNS 自己。解决是把forward的目标改成明确的内网 DNS IP,而不是依赖/etc/resolv.conf。另一个常见原因是 NetworkPolicy 把 kube-system 的 53 端口封了,用kubectl exec进业务 Pod 手动nslookup验证。

4.3 现象:滚动更新卡住,新 Pod Pending

多半是节点资源不够或亲和性不满足。v1.8.0 默认 requests 是 100m CPU / 70Mi 内存,小集群里如果节点已经很满,新 Pod 调度不上去,旧 Pod 又因为滚动策略不会先删。用kubectl describe pod看 Events 里的调度失败原因,临时调低 requests 或加节点都能解。离线环境里还要注意:如果集群开了 Pod 安全策略,新镜像的 securityContext 可能不满足。

4.4 现象:解析偶发 5 秒超时,日志里大量 SERVFAIL

这是 k8s 里经典的 conntrack 竞争问题,和 CoreDNS 版本关系不大,但 v1.8.0 时代特别常见。表现是业务侧偶发 5 秒延迟,CoreDNS 日志里能看到重传。缓解手段是给 CoreDNS Pod 加dnsConfig里的options ndots:5调整、或者在内核侧调 conntrack 参数。另一个方向是确认cache插件的 TTL 是否过短导致查询量过大,适当调大能降低压力。

4.5 现象:改完 Corefile 不生效

reload插件有轮询间隔,默认 30 秒,改完立刻测可能还是旧配置。另外 ConfigMap 更新后 kubelet 同步到 Pod 内文件也有延迟,两个延迟叠加,等一分钟再验证比较稳妥。如果超过两分钟还不生效,检查 Corefile 语法——reload插件遇到语法错误会保留旧配置并打日志,kubectl logs里能看到具体行号。

5. 装完怎么验证:从 Pod 到解析链路走一遍

验证不能只看 Pod 是不是 Running,要按链路逐层确认。第一步看副本和就绪状态:

kubectl -n kube-system get pods -l k8s-app=kube-dns -o wide kubectl -n kube-system get endpoints kube-dns

Pod 必须是Running且READY 1/1,Endpoints 里要有对应 IP。第二步从集群内发起真实解析,起一个临时 Pod 测:

# 解析集群内 Service,应返回 ClusterIP kubectl run dns-test --rm -it --image=busybox:1.28 --restart=Never -- \ nslookup kubernetes.default.svc.cluster.local # 解析外部域名,验证 forward 链路 kubectl run dns-test --rm -it --image=busybox:1.28 --restart=Never -- \ nslookup www.example.com

第一条验证kubernetes插件,第二条验证forward插件,两条都通说明主链路没问题。第三步看指标,v1.8.0 的prometheus插件在 9153 端口暴露指标,端口转发出来看几个关键值:

kubectl -n kube-system port-forward svc/kube-dns 9153:9153 & curl -s localhost:9153/metrics | grep -E 'coredns_dns_(requests_total|responses_total)'

requests_total持续增长说明流量进来了,responses_total里rcode="NOERROR"占绝大多数才正常,SERVFAIL 比例高就要回头查 4.4 那条。最后一步是压力下的稳定性:连续解析几十次看有没有偶发超时,我一般用循环跑nslookup二十次,出现一次 5 秒延迟就说明 conntrack 或上游有问题,别急着收工。

从那以后我每次换 CoreDNS 镜像,都强制走一遍“Pod 就绪 → 集群内解析 → 外部解析 → 指标核对 → 循环压测”这五步,少一步都可能在上线后半夜被叫起来。希望帮到你。

本文还有配套的精品资源,点击获取