Kubernetes CoreDNS
1、DNS服务概述
coredns github 地址:
https://github.com/kubernetes/kubernetes/blob/master/cluster/addons/dns/coredns/coredns.yaml.base
service 发现是 k8s 中的一个重要机制,其基本功能为:在集群内通过服务名对服务进行访问,即需要完成从服
务名到 ClusterIP 的解析。
k8s 主要有两种 service 发现机制:环境变量和 DNS。没有 DNS 服务的时候,k8s 会采用环境变量的形式,但一
旦有多个 service,环境变量会变复杂,为解决该问题,我们使用 DNS 服务。
DNS 服务在 kubernetes 中经历了三个阶段 ( SkyDNS -> KubeDNS -> CoreDNS ):
-
第一阶段:在 kubernetes 1.2 版本时,dns 服务使用的是由 SkyDNS 提供的,由4个容器组成:
kube2sky、skydns、etcd 和 healthz。etcd 存储 dns 记录;kube2sky 监控 service 变化,生成 dns 记录;
skydns 读取服务,提供查询服务;healthz 提供健康检查。
-
第二阶段:在 kubernetes 1.4 版本开始使用 KubeDNS,有3个容器组成:kubedns、dnsmasq 和
sidecar。kubedns 监控 service 变化,并记录到内存(存到内存提高性能)中;dnsmasq 获取 dns 记录,提供
dns 缓存,提供 dns 查询服务;sidecar 提供健康检查。
-
第三阶段:从 kubernetes >=1.11 版本开始,dns 服务有 CoreDNS 提供,coredns 支持自定义 dns 记录及配
置 upstream dns server,可以统一管理内部 dns 和物理 dns,coredns 只有一个 coredns 容器。下面是
coredns 的架构。
2、CoreDNS配置解析
下面是 coredns 的配置模板:
apiVersion: v1
kind: ConfigMap
metadata:
name: coredns
namespace: namespace-test
data:
Corefile: |
.:53 {
errors
health
ready
kubernetes cluster.local 10.200.0.0/16 {
pods insecure
upstream 114.114.114.114
fallthrough in-addr.arpa ip6.arpa
namespaces namespace-test
}
prometheus :9153
forward . /etc/resolv.conf
cache 30
loop
reload
loadbalance
}
下面是我本机 k8s 环境下的 coredns 配置:
[root@master ~]# kubectl get cm coredns -n kube-system -o yaml
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:
creationTimestamp: "2023-06-23T07:38:37Z"
name: coredns
namespace: kube-system
resourceVersion: "220"
uid: 73ad2161-f676-4695-8a49-94d1de362a16
CoreDNS 的主要功能是通过插件系统实现的。它实现了一种链式插件的结构,将 dns 的逻辑抽象成了一个个插
件。
常见的插件如下:
-
loadbalance
:提供基于 dns 的负载均衡功能 -
loop
:检测在 dns 解析过程中出现的简单循环问题 -
cache
:提供前端缓存功能 -
health
:对 Endpoint 进行健康检查 -
kubernetes
:从 kubernetes 中读取 zone 数据 -
etcd
:从 etcd 读取 zone 数据,可以用于自定义域名记录 -
file
:从文件中读取 zone 数据 -
hosts
:使用 /etc/hosts 文件或者其他文件读取 zone 数据,可以用于自定义域名记录 -
auto
:从磁盘中自动加载区域文件 -
reload
:定时自动重新加载 Corefile 配置文件的内容 -
forward
:转发域名查询到上游 dns 服务器 -
proxy
:转发特定的域名查询到多个其他 dns 服务器,同时提供到多个 dns 服务器的负载均衡功能 -
prometheus
:为 prometheus 系统提供采集性能指标数据的 URL -
pprof
:在URL路径 /debug/pprof 下提供运行是的西能数据 -
log
:对 dns 查询进行日志记录 -
errors
:对错误信息镜像日志记录
3、Pod的DNS策略
3.1 Pod DNS策略
-
Default
: 继承 Pod 所在宿主机的 DNS 设置。 -
ClusterFirst
:优先使用 kubernetes 环境的 dns 服务,将无法解析的域名转发到从宿主机继承的 dns 服务器。
-
ClusterFirstWithHostNet
:和 ClusterFirst 相同,对于以 hostNetwork 模式运行的 Pod 应明确知道使用该策略。
-
None
: 忽略 kubernetes 环境的 dns 配置,通过 spec.dnsConfig 自定义 DNS 配置。
自定义 Dns 配置,可以通过 spec.dnsConfig 字段进行设置,可以设置如下信息:
-
nameservers:一组 dns 服务器的列表,最多可设置3个
-
searchs:一组用于域名搜索的 dns 域名后缀,最多6个
-
options:配置其他可选参数,例如 ndots、timeout等
spec:
dnsPolicy: "None"
dnsConfig:
nameservers:
- 1.2.3.4
searchs:
- xx.ns1.svc.cluster.local
- xx.daemon.com
options:
- name: ndots
values: "2"
pod 被创建后,容器内的 /etc/resolv.conf
会根据这个信息进行配置。
3.2 测试解析结果
之前安装 k8s 集群的时候就已经安装过 CoreDNS,所以这里就不重复讲解安装了。
创建 nslookup 服务:
# cat >busybox.yaml<<EOF
apiVersion: v1
kind: Pod
metadata:
name: busybox
namespace: default
spec:
containers:
- name: busybox
image: busybox:1.28.4
command:
- sleep
- "3600"
imagePullPolicy: IfNotPresent
restartPolicy: Always
EOF
验证:
[root@master coredns]# kubectl create -f busybox.yaml
pod/busybox created
[root@master coredns]# kubectl get pods busybox
NAME READY STATUS RESTARTS AGE
busybox 1/1 Running 0 5s
# 查看容器内resolv文件的配置
[root@master coredns]# kubectl exec busybox -- cat /etc/resolv.conf
nameserver 10.96.0.10
search default.svc.cluster.local svc.cluster.local cluster.local
options ndots:5
ndots:5
:如果查询的域名包含的点 . 不到 5 个,那么进行 DNS 查找,将使用非完全限定名称(或者叫绝对域
名),如果你查询的域名包含点数大于等于 5,那么 DNS 查询,默认会使用绝对域名进行查询。
kubernetes 域名的全称,必须是 service-name.namespace.svc.cluster.local
这种模式。
4、测试CoreDNS
4.1 pod验证
创建并测试解析 kubernetes.default:
[root@master coredns]# kubectl exec busybox -- cat /etc/resolv.conf
nameserver 10.96.0.10
search default.svc.cluster.local svc.cluster.local cluster.local
options ndots:5
[root@master coredns]# kubectl exec -ti busybox -- nslookup kubernetes
Server: 10.96.0.10
Address 1: 10.96.0.10 kube-dns.kube-system.svc.cluster.local
Name: kubernetes
Address 1: 10.96.0.1 kubernetes.default.svc.cluster.local
[root@master coredns]# kubectl exec -ti busybox -- nslookup kubernetes.default
Server: 10.96.0.10
Address 1: 10.96.0.10 kube-dns.kube-system.svc.cluster.local
Name: kubernetes.default
Address 1: 10.96.0.1 kubernetes.default.svc.cluster.local
[root@master coredns]# kubectl exec -ti busybox -- nslookup kubernetes.default.svc
Server: 10.96.0.10
Address 1: 10.96.0.10 kube-dns.kube-system.svc.cluster.local
Name: kubernetes.default.svc
Address 1: 10.96.0.1 kubernetes.default.svc.cluster.local
[root@master coredns]# kubectl exec -ti busybox -- nslookup kubernetes.default.svc.cluster
Server: 10.96.0.10
Address 1: 10.96.0.10 kube-dns.kube-system.svc.cluster.local
nslookup: can't resolve 'kubernetes.default.svc.cluster'
command terminated with exit code 1
[root@master coredns]# kubectl exec -ti busybox -- nslookup kubernetes.default.svc.cluster.local
Server: 10.96.0.10
Address 1: 10.96.0.10 kube-dns.kube-system.svc.cluster.local
Name: kubernetes.default.svc.cluster.local
Address 1: 10.96.0.1 kubernetes.default.svc.cluster.local
4.2 创建Service和Deployment来验证
# cat >dns-Service-Deployment-test001.yaml<<EOF
apiVersion: v1
kind: Service
metadata:
name: nginx-svc-old
labels:
app: nginx-svc
spec:
selector:
app: nginx
ports:
- protocol: TCP
port: 80
targetPort: 80
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-old
spec:
replicas: 1
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx
ports:
- containerPort: 80
EOF
创建:
[root@master coredns]# kubectl apply -f dns-Service-Deployment-test001.yaml
service/nginx-svc-old created
deployment.apps/nginx-old created
[root@master coredns]# kubectl get pod | grep nginx-old
nginx-old-7848d4b86f-bkz8z 1/1 Running 0 19s
[root@master coredns]# kubectl get svc nginx-svc-old
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
nginx-svc-old ClusterIP 10.102.190.25 <none> 80/TCP 30s
直接在宿主机上验证:
[root@master coredns]# nslookup nginx-svc-old.default.svc
Server: 8.8.8.8
Address: 8.8.8.8#53
** server can't find nginx-svc-old.default.svc: NXDOMAIN
发现直接在宿主机上是不能解析域名的,然后用以下 yaml 创建一个 busybox 作为调试工具:
# cat >dns-Deployment-test002.yaml<<EOF
apiVersion: apps/v1
kind: Deployment
metadata:
name: busybox-deployment
spec:
replicas: 1
selector:
matchLabels:
app: busybox
template:
metadata:
labels:
app: busybox
spec:
restartPolicy: Always
containers:
- name: busybox
command:
- sleep
- "3600"
image: busybox:1.28.4
EOF
创建好之后,exec 进入容器,执行测试命令。
[root@master coredns]# kubectl create -f dns-Deployment-test002.yaml
deployment.apps/busybox-deployment created
[root@master coredns]# kubectl get pod | grep busybox-deployment
busybox-deployment-dd5f964bb-6nlkb 1/1 Running 0 15s
[root@master coredns]# kubectl exec -ti busybox-deployment-dd5f964bb-6nlkb -- sh
/ # nslookup nginx-svc-old.default.svc
Server: 10.96.0.10
Address 1: 10.96.0.10 kube-dns.kube-system.svc.cluster.local
Name: nginx-svc-old.default.svc
Address 1: 10.102.190.25 nginx-svc-old.default.svc.cluster.local
/ # nslookup nginx-svc-old.default.svc.cluster.local
Server: 10.96.0.10
Address 1: 10.96.0.10 kube-dns.kube-system.svc.cluster.local
Name: nginx-svc-old.default.svc.cluster.local
Address 1: 10.102.190.25 nginx-svc-old.default.svc.cluster.local
根据 coredns 解析集群内域名原理可知:
服务 a 访问服务 b,对于同一个 Namespace下,可以直接在 pod 中,通过 curl b 来访问。对于跨 Namespace
的情况,服务名后边对应 Namespace 即可,比如 curl b.default。DNS 如何解析,依赖容器内 resolv 文件的配
置。
查看 busybox 容器内的 resolve.conf 文件:
/ # cat /etc/resolv.conf
nameserver 10.96.0.10
search default.svc.cluster.local svc.cluster.local cluster.local
options ndots:5
这个文件中,配置的 DNS Server,一般就是 K8S 中,coredns 的 Service 的 ClusterIP,这个 IP 是虚拟 IP,无法
ping,但可以访问。
[root@master coredns]# kubectl get svc kube-dns -n kube-system
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kube-dns ClusterIP 10.96.0.10 <none> 53/UDP,53/TCP,9153/TCP 137m
10.96.0.10
和 /etc/resolv.conf
文件中的 10.96.0.10
一样。
在容器内发请求时,会根据 /etc/resolv.conf 进行解析流程。选择 nameserver 10.96.0.10
进行解析,然后用
nginx-svc-old ,依次带入 /etc/resolve.conf 中的 search 域,进行DNS查找,分别是:
default.svc.cluster.local
svc.cluster.local
cluster.local
# 本例的查找
nginx-svc-old.default.svc.cluster.local
nginx-svc-old.svc.cluster.local
nginx-svc-old.cluster.local
/ # ping nginx-svc-old
PING nginx-svc-old (10.102.190.25): 56 data bytes
64 bytes from 10.102.190.25: seq=0 ttl=64 time=0.035 ms
64 bytes from 10.102.190.25: seq=1 ttl=64 time=0.138 ms
64 bytes from 10.102.190.25: seq=2 ttl=64 time=0.119 ms
64 bytes from 10.102.190.25: seq=3 ttl=64 time=0.217 ms
64 bytes from 10.102.190.25: seq=4 ttl=64 time=0.185 ms
64 bytes from 10.102.190.25: seq=5 ttl=64 time=0.140 ms
64 bytes from 10.102.190.25: seq=6 ttl=64 time=0.225 ms
^C
--- nginx-svc-old ping statistics ---
7 packets transmitted, 7 packets received, 0% packet loss
round-trip min/avg/max = 0.035/0.151/0.225 ms
/ # ping nginx-svc-old.default
PING nginx-svc-old.default (10.102.190.25): 56 data bytes
64 bytes from 10.102.190.25: seq=0 ttl=64 time=0.041 ms
64 bytes from 10.102.190.25: seq=1 ttl=64 time=0.182 ms
64 bytes from 10.102.190.25: seq=2 ttl=64 time=0.118 ms
64 bytes from 10.102.190.25: seq=3 ttl=64 time=0.117 ms
64 bytes from 10.102.190.25: seq=4 ttl=64 time=0.127 ms
64 bytes from 10.102.190.25: seq=5 ttl=64 time=0.111 ms
64 bytes from 10.102.190.25: seq=6 ttl=64 time=0.169 ms
64 bytes from 10.102.190.25: seq=7 ttl=64 time=0.221 ms
64 bytes from 10.102.190.25: seq=8 ttl=64 time=0.069 ms
64 bytes from 10.102.190.25: seq=9 ttl=64 time=0.158 ms
64 bytes from 10.102.190.25: seq=10 ttl=64 time=0.063 ms
64 bytes from 10.102.190.25: seq=11 ttl=64 time=0.115 ms
64 bytes from 10.102.190.25: seq=12 ttl=64 time=0.083 ms
64 bytes from 10.102.190.25: seq=13 ttl=64 time=0.057 ms
64 bytes from 10.102.190.25: seq=14 ttl=64 time=0.146 ms
64 bytes from 10.102.190.25: seq=15 ttl=64 time=0.118 ms
^C
--- nginx-svc-old.default ping statistics ---
16 packets transmitted, 16 packets received, 0% packet loss
round-trip min/avg/max = 0.041/0.118/0.221 ms
通过一个一个进行查找直到找到为止。所以,我们执行 ping nginx-svc-old,或者执行 ping nginx-svc-
old.default,都可以完成 DNS 请求,这2个不同的操作,会分别进行不同的DNS查找步骤。
根据以上原理,查看到 busybox 内的域名 /etc/resolv.conf 没有问题,nameserver 指向正确的 kube-dns 的
service clusterIP。
4.3 宿主机上解析域名验证
nameserver 关键字,如果没指定 nameserver 就找不到 DNS 服务器,其它关键字是可选的。nameserver 表示
解析域名时使用该地址指定的主机为域名服务器。其中域名服务器是按照文件中出现的顺序来查询的,且只有当第
一个 nameserver 没有反应时才查询下面的 nameserver,一般不要指定超过3个服务器。
查看集群和本地的信息:
[root@master coredns]# kubectl get svc -n kube-system
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kube-dns ClusterIP 10.96.0.10 <none> 53/UDP,53/TCP,9153/TCP 167m
[root@master coredns]# kubectl get pod -n kube-system -o wide | grep dns
coredns-545d6fc579-42cxc 1/1 Running 0 167m 10.244.0.3 master <none> <none>
coredns-545d6fc579-lzgbz 1/1 Running 0 167m 10.244.0.2 master <none> <none>
[root@master coredns]# kubectl exec -ti busybox-deployment-dd5f964bb-6nlkb -- cat /etc/resolv.conf
nameserver 10.96.0.10
search default.svc.cluster.local svc.cluster.local cluster.local
options ndots:5
[root@master coredns]# cat /etc/resolv.conf
# Generated by NetworkManager
nameserver 8.8.8.8
nameserver 14.114.114.114
nameserver 2409:8903:2404:3b71::bf
在宿主上的 /etc/resolv.conf
文件中添加 nameserver :
nameserver 10.96.0.10
nameserver 10.244.0.3
nameserver 10.244.0.2
search default.svc.cluster.local svc.cluster.local cluster.local
最后宿主机上的文件 /etc/resolv.conf
的内容如下:
[root@master coredns]# cat /etc/resolv.conf
# Generated by NetworkManager
nameserver 10.96.0.10
nameserver 10.244.0.3
nameserver 10.244.0.2
search default.svc.cluster.local svc.cluster.local cluster.local
nameserver 8.8.8.8
nameserver 14.114.114.114
nameserver 2409:8903:2404:3b71::bf
测试:
[root@master coredns]# nslookup nginx-svc-old
Server: 10.96.0.10
Address: 10.96.0.10#53
Name: nginx-svc-old.default.svc.cluster.local
Address: 10.102.190.25
[root@master coredns]# nslookup baidu.com
Server: 10.96.0.10
Address: 10.96.0.10#53
Non-authoritative answer:
Name: baidu.com
Address: 39.156.66.10
Name: baidu.com
Address: 110.242.68.66
可以看到现在可以解析了,也不影响外网域名解析,但是最好还是不要指定超过3个服务器。