k3s集群使用本地DNS缓存(node local dns)

背景

K3s集群内集成了CoreDNS作为kube-dns,默认IP为10.43.0.10。当Pod的dns类型为ClusterFirst/ClusterFirstWithHostNet时,在容器内可以通过<service名>.<namespace名>.svc.cluster.local域名访问集群内的service(后面几段可以省略),而不再需要使用cluster ip或node port。

笔者的K3s集群有跨省份的多个节点,节点之间通过tailscale虚拟局域网通信。tailscale使用UDP协议传输数据,而运营商对跨省的UDP流量往往会采取QoS限流策略。

笔者观察到的现象是,当K3s的CoreDNS实例部署在省份A时,那么处于省份B的部分Pod在解析集群内service域名时,容易出现解析速度慢、甚至解析失败的情况,这点很是让笔者头疼。

和AI探讨方案后,AI提出可以使用K8s的NodeLocal DNSCache(以下简称localdns)来建立本地DNS缓存,保证域名解析的稳定性。但默认的localdns配置并不兼容K3s,笔者经过一番摸索得出了兼容k3s集群的localdns配置,用本文作为记录。

localdns配置

K8s默认的localdns配置可在Github上查看。内容较多,但对于K3s场景来说只需要有两个关键配置。

ConfigMap配置

localdns使用的也是CoreDNS,所以在ConfigMap里编写Corefile即可。主体逻辑如下:

  • 监听169.254.20.10:53,处理发来的DNS请求,并开启日志/缓存等功能
  • 如果请求域名是cluster.local.结尾,则转发到K3s自身的CoreDNS10.43.0.10
  • 否则将请求转发到公共DNS119.29.29.29
1
2
3
4
5
6
7
8
9
apiVersion: v1
kind: ConfigMap
metadata:
name: node-local-dns
namespace: kube-system
labels:
addonmanager.kubernetes.io/mode: Reconcile
data:
Corefile: ".:53 {\n bind 169.254.20.10\n\n\tforward cluster.local. 10.43.0.10 {\n force_tcp\n }\n forward . 119.29.29.29\n\n reload\n log\n loop\n errors\n cache 30\n prometheus :9253\n}"

DaemonSet配置

要在每台机器上部署一个localdns实例,使用如下DaemonSet配置即可。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
apiVersion: apps/v1
kind: DaemonSet
metadata:
name: node-local-dns
namespace: kube-system
labels:
k8s-app: node-local-dns
kubernetes.io/cluster-service: "true"
addonmanager.kubernetes.io/mode: Reconcile
spec:
updateStrategy:
rollingUpdate:
maxUnavailable: 10%
selector:
matchLabels:
k8s-app: node-local-dns
template:
metadata:
labels:
k8s-app: node-local-dns
annotations:
prometheus.io/port: "9253"
prometheus.io/scrape: "true"
spec:
priorityClassName: system-node-critical
serviceAccountName: node-local-dns
hostNetwork: true
dnsPolicy: Default # Don't use cluster DNS.
tolerations:
- key: "CriticalAddonsOnly"
operator: "Exists"
- effect: "NoExecute"
operator: "Exists"
- effect: "NoSchedule"
operator: "Exists"
containers:
- name: node-cache
image: registry.k8s.io/dns/k8s-dns-node-cache:1.26.4
resources:
requests:
cpu: 25m
memory: 5Mi
args: [ "-localip", "169.254.20.10", "-conf", "/etc/Corefile" ]
securityContext:
capabilities:
add:
- NET_ADMIN
ports:
- containerPort: 53
name: dns
protocol: UDP
- containerPort: 53
name: dns-tcp
protocol: TCP
- containerPort: 9253
name: metrics
protocol: TCP
volumeMounts:
- mountPath: /run/xtables.lock
name: xtables-lock
readOnly: false
- name: config-volume
mountPath: /etc/coredns
volumes:
- name: xtables-lock
hostPath:
path: /run/xtables.lock
type: FileOrCreate
- name: config-volume
configMap:
name: node-local-dns
items:
- key: Corefile
path: Corefile.base

确认部署状态

可通过kubectl get pods -n kube-system命令确认localdns部署状态

1
2
3
4
5
6
7
NAME                                      READY   STATUS    RESTARTS   AGE
...省略
node-local-dns-56v44 1/1 Running 0 45s
node-local-dns-7tnrs 1/1 Running 0 41s
node-local-dns-j74kr 1/1 Running 0 45s
node-local-dns-pk9kt 1/1 Running 0 45s
node-local-dns-vghvz 1/1 Running 0 45s

K3s配置

修改文件/etc/rancher/k3s/config.yaml,在kubelet配置中设置cluster-dns

1
2
3
kubelet-arg:
# 省略其它
- "--cluster-dns=169.254.20.10"

然后重启K3s服务

1
2
3
sudo systemctl daemon-reload
sudo systemctl restart k3s # 主节点
sudo systemctl restart k3s-agent # 从节点

接下来创建的Pod,当dns类型为ClusterFirst/ClusterFirstWithHostNet时,就会使用本地的localdns解析service域名,而不是默认的10.43.0.10


k3s集群使用本地DNS缓存(node local dns)
https://www.yooo.ltd/2025/06/08/k3s-with-nodelocaldns/
作者
OrangeWolf
发布于
2025年6月8日
许可协议