1. 概述
作为服务发现机制的基本功能,在 Kubernetes 集群中需要能够通过服务名对服务进行访问,这就需要一个 Kubernetes 集群范围内的 DNS 服务来完成从服务名到ClusterIP 地址的解析。Kubernetes 在较新的版本中默认使用 CoreDNS 作为 DNS 服务。
2. 部署 CoreDNS 服务
部署 CoreDNS 服务时需要创建3个资源对象:ConfigMap、Deployment 和Service。在启用了 RBAC 的 Kubernetes 集群中,还可以通过设置ServiceAccount、ClusterRole、ClusterRoleBinding 来对 CoreDNS 容器进行权限设置。
2.1. 部署 ConfigMap
示例:
apiVersion: v1
data:
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 {
max_concurrent 1000
}
cache 30
loop
reload
loadbalance
}
kind: ConfigMap
metadata:
name: coredns
namespace: kube-system
YAML2.2. CoreDNS 的配置说明
CoreDNS 的主要功能是通过插件系统实现的。CoreDNS 实现了一种链式插件结构,将DNS 的逻辑抽象成了一个个插件,能够灵活组合使用,在进行域名解析时这些插件将以从上到下的顺序依次执行。
常用的插件介绍如下:
- loadbalance:提供基于 DNS 的负载均衡功能。
- loop:检测在 DNS 解析过程中出现的简单循环问题。
- cache:提供前端缓存功能。
- health:对 Endpoint 进行健康检查。
- kubernetes:从 Kubernetes 中读取 Zone 数据。
- etcd:从 etcd 中读取 Zone 数据,可用于自定义域名记录。
- file:从 RFC1035 格式文件中读取 Zone 数据。
- hosts:使用 /etc/hosts 文件或者其他文件读取 Zone 数据,可用于自定义城名记录。
- auto:从磁盘中自动加载区域文件。
- reload:定时自动重新加载 Corefile 配置文件的内容。
- forward:转发域名查询到上游 DNS 服务器中。
- prometheus:为 Prometheus 系统提供采集性能指标数据的 URL。
- pprof:在 URL 路径 /debug/pprof 下提供运行时的性能数据。
- log:对 DNS 查询进行日志记录。
- errors:对错误信息进行日志记录。
etcd 和 hosts 插件都可以用于自定义域名记录。
使用 etcd 插件自定义域名示例:
- 修改 CoreDNS ConfigMap
apiVersion: v1
data:
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
}
# 开始配置etcd插件
etcd {
stubzones
# 前缀键路径
path /skydns
# etcd 集群端点地址
endpoint 192.168.2.151:2379 192.168.2.152:2379 192.168.2.153:2379
upstream /etc/resolv.conf
# 配置访问etcd证书,注意顺序一定要正确(无证书删除此配置即可)
tls /etc/etcd-tls/client.crt /etc/etcd-tls/client.key /etc/etcd-tls/ca.crt
fallthrough
}
prometheus :9153
forward . /etc/resolv.conf {
max_concurrent 1000
}
cache 30
loop
reload
loadbalance
}
kind: ConfigMap
metadata:
creationTimestamp: "2025-07-14T01:32:50Z"
name: coredns
namespace: kube-system
resourceVersion: "2049822"
uid: f8fd4439-ae87-4854-a73e-e6825ce8beec
YAML- 如果 etcd 访问需要证书,需要将证书挂载到容器
# 创建 secret
kubectl create secret generic coredns-etcd-certs \
--namespace=kube-system \
--from-file=ca.crt=/etc/kubernetes/pki/etcd/ca.crt \
--from-file=client.crt=/etc/kubernetes/pki/etcd/healthcheck-client.crt \
--from-file=client.key=/etc/kubernetes/pki/etcd/healthcheck-client.key
# 修改 CoreDNS Deployment 挂载证书
kubectl -n kube-system get deployment coredns -o yaml > coredns.yaml
# 在 spec.template.spec 下添加以下内容:
volumes:
- name: etcd-certs
secret:
secretName: coredns-etcd-certs
defaultMode: 420
# 在 containers 部分的 volumeMounts 中添加:
volumeMounts:
- name: etcd-certs
mountPath: /etc/etcd-tls
readOnly: true
Bash- 向 etcd 写入 DNS 记录
# etcd 中的键需遵循 SkyDNS 路径格式(CoreDNS 兼容):
/skydns/com/mydomain/[service]/[record-name] = DNS 记录值
# 添加 A 记录
sudo ETCDCTL_API=3 etcdctl --endpoints=https://127.0.0.1:2379 --cacert=/etc/kubernetes/pki/etcd/ca.crt --cert=/etc/kubernetes/pki/etcd/server.crt --key=/etc/kubernetes/pki/etcd/server.key put /skydns/com/mydomain/service1 '{"host":"10.0.0.1","ttl":60}'
Bash- 验证 DNS 解析
dig @10.96.0.10 service1.mydomain.com # 10.96.0.10 CoreDNS service IP
; <<>> DiG 9.16.23-RH <<>> @10.96.0.10 service1.mydomain.com
; (1 server found)
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 23411
;; flags: qr aa rd; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 1
;; WARNING: recursion requested but not available
;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 4096
; COOKIE: c8ceafcd7212a598 (echoed)
;; QUESTION SECTION:
;service1.mydomain.com. IN A
;; ANSWER SECTION:
service1.mydomain.com. 30 IN A 10.0.0.1 # 解析到 IP 地址
;; Query time: 3 msec
;; SERVER: 10.96.0.10#53(10.96.0.10)
;; WHEN: Sun Aug 17 01:01:53 CST 2025
;; MSG SIZE rcvd: 99
Bash使用 hosts 插件自定义域名示例:
- 修改 CoreDNS ConfigMap
apiVersion: v1
data:
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
}
# 开始配置hosts插件
hosts {
# 格式:IP 域名 [别名...]
10.0.0.1 service1.mydomain.com
10.0.0.2 service2.mydomain.com api.mydomain.com
# 支持从文件加载(需挂载)
# file /etc/coredns/custom-hosts
fallthrough # 若未匹配,继续向下传递查询
}
prometheus :9153
forward . /etc/resolv.conf {
max_concurrent 1000
}
cache 30
loop
reload
loadbalance
}
kind: ConfigMap
metadata:
creationTimestamp: "2025-07-14T01:32:50Z"
name: coredns
namespace: kube-system
resourceVersion: "2049822"
uid: f8fd4439-ae87-4854-a73e-e6825ce8beec
YAML- 重启 CoreDNS
kubectl rollout restart deployment/coredns -n kube-system
Bashforward 插件用于配置上游 DNS 服务器,在 CoreDNS 配置中,forward . /etc/resolv.conf 的 /etc/resolv.conf 指的是 CoreDNS 容器内部的 /etc/resolv.conf 文件,默认情况下,容器内的 /etc/resolv.conf 通常由容器运行时自动生成,并继承宿主机的 DNS 配置(或根据用户配置覆盖)。
2.3. 部署 Deployment
示例:
apiVersion: apps/v1
kind: Deployment
metadata:
labels:
k8s-app: kube-dns
name: coredns
namespace: kube-system
spec:
progressDeadlineSeconds: 600
replicas: 2
revisionHistoryLimit: 10
selector:
matchLabels:
k8s-app: kube-dns
strategy:
rollingUpdate:
maxSurge: 25%
maxUnavailable: 1
type: RollingUpdate
template:
metadata:
creationTimestamp: null
labels:
k8s-app: kube-dns
spec:
affinity:
podAntiAffinity:
preferredDuringSchedulingIgnoredDuringExecution:
- podAffinityTerm:
labelSelector:
matchExpressions:
- key: k8s-app
operator: In
values:
- kube-dns
topologyKey: kubernetes.io/hostname
weight: 100
containers:
- args:
- -conf
- /etc/coredns/Corefile
image: registry.k8s.io/coredns/coredns:v1.11.3
imagePullPolicy: IfNotPresent
livenessProbe:
failureThreshold: 5
httpGet:
path: /health
port: 8080
scheme: HTTP
initialDelaySeconds: 60
periodSeconds: 10
successThreshold: 1
timeoutSeconds: 5
name: coredns
ports:
- containerPort: 53
name: dns
protocol: UDP
- containerPort: 53
name: dns-tcp
protocol: TCP
- containerPort: 9153
name: metrics
protocol: TCP
readinessProbe:
failureThreshold: 3
httpGet:
path: /ready
port: 8181
scheme: HTTP
periodSeconds: 10
successThreshold: 1
timeoutSeconds: 1
resources:
limits:
memory: 170Mi
requests:
cpu: 100m
memory: 70Mi
securityContext:
allowPrivilegeEscalation: false
capabilities:
add:
- NET_BIND_SERVICE
drop:
- ALL
readOnlyRootFilesystem: true
terminationMessagePath: /dev/termination-log
terminationMessagePolicy: File
volumeMounts:
- mountPath: /etc/coredns
name: config-volume
readOnly: true
dnsPolicy: Default
nodeSelector:
kubernetes.io/os: linux
priorityClassName: system-cluster-critical
restartPolicy: Always
schedulerName: default-scheduler
securityContext: {}
serviceAccount: coredns
serviceAccountName: coredns
terminationGracePeriodSeconds: 30
tolerations:
- key: CriticalAddonsOnly
operator: Exists
- effect: NoSchedule
key: node-role.kubernetes.io/control-plane
volumes:
- configMap:
defaultMode: 420
items:
- key: Corefile
path: Corefile
name: coredns
name: config-volume
YAML其中 replicas 副本数量需要根据服务规模设置,也可以使用 HPA 自动扩缩。对 Pod 的资源限制也应该根据实际情况调整。
2.4. 部署 Service
示例:
apiVersion: v1
kind: Service
metadata:
annotations:
prometheus.io/port: "9153"
prometheus.io/scrape: "true"
labels:
k8s-app: kube-dns
kubernetes.io/cluster-service: "true"
kubernetes.io/name: CoreDNS
name: kube-dns
namespace: kube-system
spec:
clusterIP: 10.96.0.10
clusterIPs:
- 10.96.0.10
internalTrafficPolicy: Cluster
ipFamilies:
- IPv4
ipFamilyPolicy: SingleStack
ports:
- name: dns
port: 53
protocol: UDP
targetPort: 53
- name: dns-tcp
port: 53
protocol: TCP
targetPort: 53
- name: metrics
port: 9153
protocol: TCP
targetPort: 9153
selector:
k8s-app: kube-dns
sessionAffinity: None
type: ClusterIP
YAML2.5. 修改每个 Node 上的 kubelet 启动参数
--cluster-dns=10.96.0.10
:指定 CoreDNS 的 clusterIP 地址。--cluster-domain=cluster.local
:指定 k8s 集群域名地址,在 CoreDNS 的 Corefile 配置中,会引用集群域名,例如 kubernetes cluster.local。
3. Service 和 Pod 的 DNS 域名相关特性
3.1. Service 的 DNS 域名
对于 Service 来说,Kubernetes 会为其设置一个如下格式的 DNS 域名:
<service-name>.<namespace-name>.sve.<cluster-domain>
# cluster-domain是集群配置的域后缀名称。如 --cluster-domain=cluster.local
TeX解析 Service IP 示例:
# 这里使用 CoreDNS 的 Service 示例
dig @10.96.0.10 kube-dns.kube-system.svc.cluster.local
; <<>> DiG 9.16.23-RH <<>> @10.96.0.10 kube-dns.kube-system.svc.cluster.local
; (1 server found)
;; global options: +cmd
;; Got answer:
;; WARNING: .local is reserved for Multicast DNS
;; You are currently testing what happens when an mDNS query is leaked to DNS
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 25578
;; flags: qr aa rd; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 1
;; WARNING: recursion requested but not available
;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 4096
; COOKIE: 68ae0df35d89eb84 (echoed)
;; QUESTION SECTION:
;kube-dns.kube-system.svc.cluster.local. IN A
;; ANSWER SECTION:
kube-dns.kube-system.svc.cluster.local. 30 IN A 10.96.0.10
;; Query time: 1 msec
;; SERVER: 10.96.0.10#53(10.96.0.10)
;; WHEN: Sun Aug 17 11:36:58 CST 2025
;; MSG SIZE rcvd: 133
Bash因为 HedlessService 没有 ClusterIP 地址,所以 DNS 解析的结果会是后端 Pod 的 IP 地址列表。另外,当 HedlessService 关联了 StatefulSet 的 Pod 时,还会为每个 Pod 指定一个域名,格式为:
<pod-name>.<service-name>.<namespace-name>.sve.<cluster-domain>
TeX另外,Kubernetes 系统也会为 Service 的每个命名端口号(port.name)都创建一个 SRV 记录,格式如下:
_<port-name>._<port-protoco1>.<service-name>.<namespace-name>.sve.<cluster-domain>
TeX示例:
dig @10.96.0.10 _dns-tcp._tcp.kube-dns.kube-system.svc.cluster.local
; <<>> DiG 9.16.23-RH <<>> @10.96.0.10 _dns-tcp._tcp.kube-dns.kube-system.svc.cluster.local
; (1 server found)
;; global options: +cmd
;; Got answer:
;; WARNING: .local is reserved for Multicast DNS
;; You are currently testing what happens when an mDNS query is leaked to DNS
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 17902
;; flags: qr aa rd; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 1
;; WARNING: recursion requested but not available
;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 4096
; COOKIE: d9f0d9c34a96462e (echoed)
;; QUESTION SECTION:
;_dns-tcp._tcp.kube-dns.kube-system.svc.cluster.local. IN A
;; ANSWER SECTION:
_dns-tcp._tcp.kube-dns.kube-system.svc.cluster.local. 30 IN A 10.96.0.10 # 同样能解析到 Service 的 IP 地址
;; Query time: 1 msec
;; SERVER: 10.96.0.10#53(10.96.0.10)
;; WHEN: Sun Aug 17 11:48:22 CST 2025
;; MSG SIZE rcvd: 161
# HedlessService 同理,也会解析到后端 Pod 的 IP 地址列表
Bash3.2. Pod 的 DNS 域名
对所有 Pod,Kubernetes 会为其设置一个如下格式的 DNS 域名:
<pod-ip>.<namespace>.pod.<cluster-domain>
TeX对于通过 Service 关联的 Pod,Kubernetes 会为每个 Pod 都以其 IP 地址和 Service名称设置一个 DNS 域名,格式如下:
<pod-ip>.<service-name>.<namespace-name>.sve.<cluster-domain>
# 通过 Service 关联的 Pod 这两个 DNS 域名是同时存在的
TeX示例:
$ kubectl get -n dev-env pod -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
python-dev-env-69b4b5f6f4-928dw 1/1 Running 3 (69m ago) 46h 10.244.3.54 k8s-node-01 <none> <none>
$ kubectl get -n dev-env svc -o wide
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE SELECTOR
python-dev-service NodePort 10.102.210.249 <none> 22:30022/TCP 46h app=python-dev
# 解析 pod 格式的域名
$ dig @10.96.0.10 10-244-3-54.dev-env.pod.cluster.local
; <<>> DiG 9.16.23-RH <<>> @10.96.0.10 10-244-3-54.dev-env.pod.cluster.local
; (1 server found)
;; global options: +cmd
;; Got answer:
;; WARNING: .local is reserved for Multicast DNS
;; You are currently testing what happens when an mDNS query is leaked to DNS
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 45879
;; flags: qr aa rd; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 1
;; WARNING: recursion requested but not available
;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 4096
; COOKIE: bfba189f40bf842c (echoed)
;; QUESTION SECTION:
;10-244-3-54.dev-env.pod.cluster.local. IN A
;; ANSWER SECTION:
10-244-3-54.dev-env.pod.cluster.local. 30 IN A 10.244.3.54
;; Query time: 2 msec
;; SERVER: 10.96.0.10#53(10.96.0.10)
;; WHEN: Sun Aug 17 12:18:38 CST 2025
;; MSG SIZE rcvd: 131
# 解析 svc 格式的域名
$ dig @10.96.0.10 10-244-3-54.python-dev-service.dev-env.svc.cluster.local
; <<>> DiG 9.16.23-RH <<>> @10.96.0.10 10-244-3-54.python-dev-service.dev-env.svc.cluster.local
; (1 server found)
;; global options: +cmd
;; Got answer:
;; WARNING: .local is reserved for Multicast DNS
;; You are currently testing what happens when an mDNS query is leaked to DNS
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 34686
;; flags: qr aa rd; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 1
;; WARNING: recursion requested but not available
;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 4096
; COOKIE: 583873fc05da7573 (echoed)
;; QUESTION SECTION:
;10-244-3-54.python-dev-service.dev-env.svc.cluster.local. IN A
;; ANSWER SECTION:
10-244-3-54.python-dev-service.dev-env.svc.cluster.local. 30 IN A 10.244.3.54
;; Query time: 1 msec
;; SERVER: 10.96.0.10#53(10.96.0.10)
;; WHEN: Sun Aug 17 12:19:16 CST 2025
;; MSG SIZE rcvd: 169
Bash3.3. Pod 自定义固定域名
由上面的示例可知,这些域名都与 Pod 的 IP 地址有关,但在实际场景通常 IP 地址都不是固定的,所以无法实现通过一个固定域名解析到相应的 Pod IP 地址。
除了上一节提到,使用 HedlessService 关联 StatefulSet 的 Pod 来使每个 Pod 拥有一个固定域名外,我们还可以使用 hostname 和 subdomain 字段来为 Pod 配置域名。
单独创建 Pod:
# individual-pods-example.yaml
apiVersion: v1
kind: Pod
metadata:
name: busybox1
labels:
name: busybox # 这个标签两个 pod 需要一样,方便后续统一选中
spec:
hostname: busybox-1
subdomain: default-subdomain # 这里的名字就是一会要创建出的无头服务的名字
containers:
- image: busybox:1.28
command:
- sleep
- '3600'
name: busybox
---
apiVersion: v1
kind: Pod
metadata:
name: busybox2
labels:
name: busybox # 这个标签两个 pod 需要一样,方便后续统一选中
spec:
hostname: busybox-2
subdomain: default-subdomain # 这里的名字就是一会要创建出的无头服务的名字
containers:
- image: busybox:1.28c
command:
- sleep
- '3600'
name: busybox
YAML为上面的 Pod 创建一个 Headless Service:
# headless-service.yaml
apiVersion: v1
kind: Service
metadata:
name: default-subdomain # 名字与 subdomain 字段保持一致
spec:
selector:
name: busybox
clusterIP: None
ports:
- name: test
port: 1234
targetPort: 1234 # 随意指定,因为被选中的 Pod 中也没监听端口
YAML验证:
$ kubectl get pod -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
busybox1 1/1 Running 0 7m22s 10.244.4.96 k8s-node-02 <none> <none>
busybox2 1/1 Running 0 7m22s 10.244.4.97 k8s-node-02 <none> <none>
$ dig @10.96.0.10 busybox-1.default-subdomain.default.svc.cluster.local
;; ANSWER SECTION:
busybox-1.default-subdomain.default.svc.cluster.local. 30 IN A 10.244.4.96
$ dig @10.96.0.10 busybox-2.default-subdomain.default.svc.cluster.local
;; ANSWER SECTION:
busybox-2.default-subdomain.default.svc.cluster.local. 30 IN A 10.244.4.97
Bash3.4. Pod 的 DNS 策略
在 Kubernetes 中,Pod 的 DNS 解析行为由 dnsPolicy
字段控制,它决定了 Pod 如何获取 DNS 配置以及如何解析域名。
Pod 的 dnsPolicy 可以设置为以下四种模式:
- ClusterFirst(默认):优先使用集群 DNS(如 CoreDNS),无法解析的域名转发到上游 DNS。
示例:
apiVersion: v1
kind: Pod
metadata:
name: nginx
spec:
containers:
- name: nginx
image: nginx
dnsPolicy: ClusterFirst # 默认值,可省略
YAML- Default:继承 Pod 所在节点的 /etc/resolv.conf。
- 这种方式其实是让 kubelet 来决定使用何种 DNS 策略。而 kubelet 默认的方式,就是使用宿主机的 /etc/resolv.conf,kubelet 是可以灵活来配置使用什么文件来进行 DNS 策略的,我们完全可以使用 kubelet 的参数
–resolv-conf=/etc/resolv.conf
来决定你的 DNS 解析文件地址。
- 这种方式其实是让 kubelet 来决定使用何种 DNS 策略。而 kubelet 默认的方式,就是使用宿主机的 /etc/resolv.conf,kubelet 是可以灵活来配置使用什么文件来进行 DNS 策略的,我们完全可以使用 kubelet 的参数
示例:
apiVersion: v1
kind: Pod
metadata:
name: nginx
spec:
containers:
- name: nginx
image: nginx
dnsPolicy: Default
YAML- ClusterFirstWithHostNet:即使 Pod 使用
hostNetwork: true
,仍优先使用集群 DNS。
示例:
apiVersion: v1
kind: Pod
metadata:
name: nginx
spec:
hostNetwork: true # 使用主机网络
dnsPolicy: ClusterFirstWithHostNet
containers:
- name: nginx
image: nginx
YAML- None:不自动配置 DNS,需通过
dnsConfig
手动指定。
示例:
apiVersion: v1
kind: Pod
metadata:
name: nginx
spec:
dnsPolicy: None
dnsConfig:
nameservers:
- 8.8.8.8 # 自定义 DNS 服务器
searches:
- mydomain.com # 自定义搜索域
options:
- name: ndots
value: "2"
YAML4. LocalDNS 配置
集群规模大的情况,CoreDNS 可能会出现超时 5s 的情况(dns 请求超时,客户端默认策略时等待 5s 自动重试,如果重试成功,我们看到的现象就是 dns 请求有 5s的延时),这对我们影响很大,因为 k8s 集群都依赖它来实现域名解析。
超时的原因:无论你用的是 iptables 模式还是 ipvs 模式,它们的底层都会用到 conntrack 内核模块来组织 dns 的 udp 查询包,高并发场景,会出现 conntrack 冲突问题,导致一些 dns 请求包被丢掉,从而导致超时。
为了解决这些问题,k8s 引入了 Node 本地 DNS 缓存(Node Local DNS Cache)。
4.1. 部署 LocalDNS
直接获取官方的资源清单:
# 官方下载地址:
wget https://github.com/kubernetes/kubernetes/raw/master/cluster/addons/dns/nodelocaldns/nodelocaldns.yaml
# 该资源清单文件中需要配置的几个变量说明
__PILLAR__DNS__SERVER__ # 表示 CoreDNS Service 的 ClusterIP
__PILLAR__LOCAL__DNS__ # 设置 LocalDNS 本地的 IP,默认为 169.254.20.10
__PILLAR__DNS__DOMAIN__ # 表示集群域,默认就是 cluster.local
Bash安装脚本:
# 定义变量
kubedns=10.96.0.10 # CoreDNS Service 的 ClusterIP
domain=cluster.local # 表示集群域,默认就是 cluster.local
localdns=169.254.20.10 # 设置 LocalDNS 本地的 IP,默认为 169.254.20.10
wget https://github.com/kubernetes/kubernetes/raw/master/cluster/addons/dns/nodelocaldns/nodelocaldns.yaml
# 修改 nodelocaldns.yaml 文件
# 如果 kube-proxy 运行在 IPTABLES 模式
# sed -i "s/__PILLAR__LOCAL__DNS__/$localdns/g; s/__PILLAR__DNS__DOMAIN__/$domain/g; s/__PILLAR__DNS__SERVER__/$kubedns/g" nodelocaldns.yaml
# 如果 kube-proxy 运行在 IPVS 模式
sed -ri "s/__PILLAR__LOCAL__DNS__/$localdns/g; s/__PILLAR__DNS__DOMAIN__/$domain/g; s/,?__PILLAR__DNS__SERVER__//g; s/__PILLAR__CLUSTER__DNS__/$kubedns/g" nodelocaldns.yaml
# 镜像地址修改
# sed -ri 's@registry.k8s.io/dns/k8s-dns-node-cache:1.23.0@registry.cnhangzhou.aliyuncs.com/egon-k8s-test/nodelocaldns:1.23.0@g' nodelocaldns.yaml
# 部署
# 需要注意的是这里使用 DaemonSet 部署 node-local-dns 使用了 hostNetwork=true ,会占用宿主机的 8080 端口,所以需要保证该端口未被占用。
kubectl apply -f nodelocaldns.yaml
Bash查看:
kubectl get pods -n kube-system -l k8s-app=node-local-dns
NAME READY STATUS RESTARTS AGE
node-local-dns-76cfc 1/1 Running 0 106s
node-local-dns-7nzsl 1/1 Running 0 106s
node-local-dns-98dhj 1/1 Running 0 106s
node-local-dns-jvr9l 1/1 Running 0 106s
node-local-dns-mqtcj 1/1 Running 0 106s
Bash如果 kube-proxy 组件使用的是 ipvs 模式的话我们还需要修改每个节点上的
kubelet 的 –cluster-dns 参数,将其指向 169.254.20.10。
vim /var/lib/kubelet/config.yaml
clusterDNS:
- 169.254.20.10 # 原值为10.96.0.10
systemctl restart kubelet # 然后重启kubelet
BashDaemonset 会在每个节点创建一个网卡来绑这个 IP,Pod 向本节点这个 IP 发 DNS 请求,缓存没有命中的时候才会再代理到上游集群 DNS 进行查询。
# 宿主机上执行
$ ip a
...
5: nodelocaldns: <BROADCAST,NOARP> mtu 1500 qdisc noop state DOWN group default
link/ether be:2d:83:60:e4:99 brd ff:ff:ff:ff:ff:ff
inet 169.254.20.10/32 scope global nodelocaldns
valid_lft forever preferred_lft forever
Bashiptables 模式下 Pod 还是向原来的集群 DNS 请求,节点上有这个 IP 监听,会被本机拦截,再请求集群上游 DNS,所以不需要更改 –cluster-dns 参数。如果担心线上环境修改 –cluster-dns 参数会产生影响,我们也可以直接在新部署的 Pod 中通过
dnsConfig 配置使用新的 localdns 的地址来进行解析。
测试:
# 用 coredns 来解析
$ dig @10.96.0.10 busybox-1.default-subdomain.default.svc.cluster.local
;; ANSWER SECTION:
busybox-1.default-subdomain.default.svc.cluster.local. 30 IN A 10.244.4.96
# 用 nodelocaldns 来解析
$ dig @169.254.20.10 busybox-1.default-subdomain.default.svc.cluster.local
;; ANSWER SECTION:
busybox-1.default-subdomain.default.svc.cluster.local. 30 IN A 10.244.4.96
Bash对于部署 nodelocaldns 之前启动的 Pod 要想在 Pod 内部使用 node-local-dns 则需要重建。
# 使用前面创建的 busybox-1 Pod 示例
$ kubectl exec -it busybox1 -- sh
/ # cat /etc/resolv.conf
search default.svc.cluster.local svc.cluster.local cluster.local
nameserver 10.96.0.10 # 还是指向 CoreDNS
options ndots:5
# 重建 Pod
$ kubectl exec -it busybox1 -- sh
/ # cat /etc/resolv.conf
search default.svc.cluster.local svc.cluster.local cluster.local
nameserver 169.254.20.10 # 指向了 Node Local DNS
options ndots:5
Bash4.2. Node Local DNS 解析流程
部署 Node Local DNS Cache 采用的是 DaemonSet 控制器,会确保每个节点都有一个实例,来提升集群 DNS 的性能和可靠性 Node Local DNS Cache会在每个物理节点上缓存 DNS 解析,这可以缩短 DNS 查找的延迟时间、使 DNS 查找时间更加一致,以及减少发送到 CoreDNS 的 DNS 查询次数。
Node Local DNS 的解析流程:
Pod 的 /etc/resolv.conf
配置指向 NodeLocal DNS(169.254.20.10
)。
+-------------------+ +-------------------+ +-------------------+
| Pod 1 | | Pod 2 | | Pod N |
| DNS请求: | | DNS请求: | | DNS请求: |
| ->169.254.20.10 | | ->169.254.20.10 | | ->169.254.20.10 |
+-------------------+ +-------------------+ +-------------------+
| | |
v v v
+-----------------------------------------------------------------------+
| NodeLocal DNS (DaemonSet) |
| 监听: 169.254.20.10:53 |
| 作用: |
| 1. 检查本地缓存 |
| 2. 未命中则转发请求 |
+-----------------------------------------------------------------------+
|
v
+-----------------------------------------------------------------------+
| 上游 DNS (如 CoreDNS) |
+-----------------------------------------------------------------------+
TeX