kubernetes域名解析服务CoreDNS
k8s dns组件历史简介
kubernetes的DNS组件历史有三个,分别是skydns、kube-dns和coredns;在k8s 1.3版本之前使用的是skydns,之后的版本到1.17及之间的版本都是使用的kube-dns,1.18开始至今主要使用coredns;这些dns组件在k8s当中,主要作用就是解析k8s中service name所对应的IP地址,即在k8s内部做service name解析,使得我们在k8s内部访问各自服务名称能够拿到对应服务后面pod的IP地址;当然这些dns组件除了可以解析内部service name,它也能解析互联网域名;
CoreDNS简介
CoreDNS是一个可扩展的DNS服务器,支持标准DNS,标准(但不广泛) 采用基于TLS的DNS和基于gRPC协议的非标准DNS。它的主要特点是插件系统,它允许您执行任何操作来响应可以在Go中编码的DNS查询。Miek Gieben在2016年编写了CoreDNS 的初始版本,在此之前他还写过SkyDNS,以及一个用Go语言编写的DNS函数库Go DNS。与它的继任者CoreDNS一样,SkyDNS的主要目的也是支持服务发现。Miek非常欣赏一个基于Go的网络服务器架构 Caddy,所以他就选用Caddy构建了CoreDNS。CoreDNS继承了Caddy的主要优点是简单的配置语法、强大的基于插件的架构以及 Go 语言。在k8s中,CoreDNS作为一个服务发现的配置中心,我们在K8s中创建的Service和Pod都会在其中自动生成相应的DNS记录。k8s服务发现的特性,使CoreDNS很适合作为企业云原生环境的DNS服务器,保障企业容器化和非容器化业务服务的稳定运行。
CoreDNS项目托管在github地址:https://github.com/coredns/coredns;
官方网站:https://coredns.io;
插件介绍:https://coredns.io/plugins/;
CoreDNS运行原理与插件

提示:如上图所示,CoreDNS是基于Caddy框架实现模块化设计,每个插件实现不同的功能,如forward插件实现转发功能,etcd插件实现去etcd中获取zone信息,file插件实现在某个文件中获取zone信息, Kubernetes插件实现集群服务发现;

提示:外部DNS请求到达CoreDNS后,根据插件调用链依次处理DNS请求。
coredns在k8s之上的部署清单
apiVersion: v1
kind: ServiceAccount
metadata:
name: coredns
namespace: kube-system
labels:
kubernetes.io/cluster-service: "true"
addonmanager.kubernetes.io/mode: Reconcile
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
labels:
kubernetes.io/bootstrapping: rbac-defaults
addonmanager.kubernetes.io/mode: Reconcile
name: system:coredns
rules:
- apiGroups:
- ""
resources:
- endpoints
- services
- pods
- namespaces
verbs:
- list
- watch
- apiGroups:
- ""
resources:
- nodes
verbs:
- get
- apiGroups:
- discovery.k8s.io
resources:
- endpointslices
verbs:
- list
- watch
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
annotations:
rbac.authorization.kubernetes.io/autoupdate: "true"
labels:
kubernetes.io/bootstrapping: rbac-defaults
addonmanager.kubernetes.io/mode: EnsureExists
name: system:coredns
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: system:coredns
subjects:
- kind: ServiceAccount
name: coredns
namespace: kube-system
---
apiVersion: v1
kind: ConfigMap
metadata:
name: coredns
namespace: kube-system
labels:
addonmanager.kubernetes.io/mode: EnsureExists
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 {
forward . 223.6.6.6 {
max_concurrent 1000
}
cache 600
loop
reload
loadbalance
}
myserver.online {
forward . 172.16.16.16:53
}
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: coredns
namespace: kube-system
labels:
k8s-app: kube-dns
kubernetes.io/cluster-service: "true"
addonmanager.kubernetes.io/mode: Reconcile
kubernetes.io/name: "CoreDNS"
spec:
# replicas: not specified here:
# 1. In order to make Addon Manager do not reconcile this replicas parameter.
# 2. Default is 1.
# 3. Will be tuned in real time if DNS horizontal auto-scaling is turned on.
replicas: 2
strategy:
type: RollingUpdate
rollingUpdate:
maxUnavailable: 1
selector:
matchLabels:
k8s-app: kube-dns
template:
metadata:
labels:
k8s-app: kube-dns
spec:
securityContext:
seccompProfile:
type: RuntimeDefault
priorityClassName: system-cluster-critical
serviceAccountName: coredns
affinity:
podAntiAffinity:
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 100
podAffinityTerm:
labelSelector:
matchExpressions:
- key: k8s-app
operator: In
values: ["kube-dns"]
topologyKey: kubernetes.io/hostname
tolerations:
- key: "CriticalAddonsOnly"
operator: "Exists"
nodeSelector:
kubernetes.io/os: linux
containers:
- name: coredns
image: harbor.ik8s.cc/baseimages/coredns:1.9.4
imagePullPolicy: IfNotPresent
resources:
limits:
memory: 256Mi
cpu: 200m
requests:
cpu: 100m
memory: 70Mi
args: [ "-conf", "/etc/coredns/Corefile" ]
volumeMounts:
- name: config-volume
mountPath: /etc/coredns
readOnly: true
ports:
- containerPort: 53
name: dns
protocol: UDP
- containerPort: 53
name: dns-tcp
protocol: TCP
- containerPort: 9153
name: metrics
protocol: TCP
livenessProbe:
httpGet:
path: /health
port: 8080
scheme: HTTP
initialDelaySeconds: 60
timeoutSeconds: 5
successThreshold: 1
failureThreshold: 5
readinessProbe:
httpGet:
path: /ready
port: 8181
scheme: HTTP
securityContext:
allowPrivilegeEscalation: false
capabilities:
add:
- NET_BIND_SERVICE
drop:
- all
readOnlyRootFilesystem: true
dnsPolicy: Default
volumes:
- name: config-volume
configMap:
name: coredns
items:
- key: Corefile
path: Corefile
---
apiVersion: v1
kind: Service
metadata:
name: kube-dns
namespace: kube-system
annotations:
prometheus.io/port: "9153"
prometheus.io/scrape: "true"
labels:
k8s-app: kube-dns
kubernetes.io/cluster-service: "true"
addonmanager.kubernetes.io/mode: Reconcile
kubernetes.io/name: "CoreDNS"
spec:
selector:
k8s-app: kube-dns
clusterIP: 10.100.0.2
ports:
- name: dns
port: 53
protocol: UDP
- name: dns-tcp
port: 53
protocol: TCP
- name: metrics
port: 9153
protocol: TCP
提示:上述主要定义了coredns在k8s之上的授权和准入控制以及coredns pods、插件和service相关部署清单;这里需要说明一点,coredns作为k8s的域名解析服务以及服务发现组件,在k8s上它无需对k8s资源做任何修改操作,只拥有读取和watch权限;即只要对应pod信息或service信息发生变动都会触发coredns来获取数据,从而实现对应pods或service资源变动能及时反馈在coredns的a记录中;
CoreDNS在k8s之上实现服务发现逻辑
我知道在k8s集群之上,所有k8s资源信息(如,configmap,secret,service等),pod状态信息都存放在etcd数据库中;而连接etcd只有apiserver才可以,所以coredns要想实现在k8s之上服务发现,首先要连接到k8s集群;即通过k8sapiserver连接到etcd的中获取对应service和pod的相关信息,生成dns a记录;那coredns是怎么连接到k8s集群之上的呢?在部署coredns插件时,我们会在k8s上先创建一个sa(ServiceAccount)资源,即服务账号,然后创建一个Cluster Role,这个clusterrole主要就是对coredns做准入控制和授权、最后创建一个clusterrolebingding将clusterrole和serviceaccout做绑定;有了上述的三个资源,对应coredns就有权限通过apiserver连接至etcd集群获取service和对应pod等资源的相关信息;服务发现主要是基于对etcd中pod信息和service信息的watch机制实现的,应用上述授权后,k8s上的pod或service资源变动都会触发coredns来更新数据;对于k8s集群来说,其他任何第三方客户端来连接k8s集群都是通过apiserver对外提供的服务来实现的;在k8s上apiserver提供服务的接口有两种,一种是基于监听宿主机端口通过验证客户端证书的方式,这种一般常用于集群外部客户端连接管理k8s集群使用;除此之外,k8s集群内部会也会有一个kubernetes的服务,它是以k8s service资源的方式提供服务,即在k8s内部通过cluster IP 的方式生成iptables或ipvs规则来转发内部客户端连接k8s的请求至对应master主机apiserver监听的端口之上;如下

提示:在k8s内部通过访问10.100.0.1这个地址的443端口,就能实现将请求转发各master之上的apiserver监听的6443端口上;

coredns插件功能描述
errors:该插件主要用于将错误信息标准输出;
health:该插件主要用于在CoreDNS的http://localhost:8080/health端口提供CoreDNS服务的健康报告;
ready:该插件监听在tcp的8181端口,当coredns的插件都已准备就绪时,访问该接口会返回200状态码ok;
kubernetes:该插件主要用于coredns连接k8s集群获取pods,namespaces,services,endpoints等信息,以及CoreDNS 将基于 kubernetes service name进行DNS查询并返回查询记录给客户端;该插件也是实现在k8s之上服务发现的关键; kubernetes cluster.local这个配置就是coredns收到cluster.local的查询都会转发至kubenetes进行查询;
prometheus:该插件主要用于收集CoreDNS的度量指标数据以Prometheus的key-value的格式在http://localhost:9153/metrics 的url上提供;
forward:该插件主要用于将那些不是k8s集群内部的其他任何域名的查询请求转发到预定义的dns服务器上;
cache:该插件主要用于启用service解析缓存,单位为秒;
loop:该插件主要用于检测域名解析是否有死循环,如果发现解析是死循环,则强制终止coredns进程,让k8s重建coredns pod;
reload:该插件主要用于检测corefile是否更改,在重新编辑configmap配置文件后,默认2分钟会自动加载对应configmap配置文件;
loadbalance:该插件主要定义轮训DNS域名解析, 如果一个域名存在多个记录则轮训解析;
扩展:基于nodeLocal DNScache相关说明

提示:本地dns缓存的主要作用是大规模集群pod或service资源的频繁改动导致dns查询而带来的性能损耗;它的工作原理就是在pod进行dns解析时,会首先联系localdns,如果缓存命中,则返回,然后对应pod拿着返回的信息去和对应IP地址通信,只有当localdns本地没有缓存,此时localdns才会向上转发;简单讲这种方式主要用来分担coredns压力和提高pod查询效率缩短查询路径;这里需要注意一点,在k8s中,虽然使用的是coredns,但是coredns对应服务名称为kube-dns,这个是历史遗留,其他第三方插件都是使用这个名称,所以就保留了这个kube-dns的服务名称;
pod基于coredns进行域名解析
解析kubenetes服务

提示:在pod内部执行解析kubenetes服务,首先通过/etc/resolv.conf文件中的搜索域将对应域名全部补齐,然后再将这个全域名名称再coredns中进行查询,最终coredns通过连接apiserver到etcd中检索kubernetes服务IP地址返回给nslookup客户端;
解析kube-dnsIP地址

提示:默认情况下,pod的dns搜索域只会在pod所在名称空间下搜索,如果我们查询的域名跨名称空间,需要手动给出对应名称空间名称,即包含服务名,名称空间的域名;

CoreDNS是一个可扩展的DNS服务器,支持标准DNS,标准(但不广泛) 采用基于TLS的DNS和基于gRPC协议的非标准DNS。它的主要特点是插件系统,它允许您执行任何操作来响应可以在Go中编码的DNS查询。Miek Gieben在2016年编写了CoreDNS 的初始版本,在此之前他还写过SkyDNS,以及一个用Go语言编写的DNS函数库Go DNS。与它的继任者CoreDNS一样,SkyDNS的主要目的也是支持服务发现。Miek非常欣赏一个基于Go的网络服务器架构 Caddy,所以他就选用Caddy构建了CoreDNS。CoreDNS继承了Caddy的主要优点是简单的配置语法、强大的基于插件的架构以及 Go 语言。在k8s中,CoreDNS作为一个服务发现的配置中心,我们在K8s中创建的Service和Pod都会在其中自动生成相应的DNS记录。k8s服务发现的特性,使CoreDNS很适合作为企业云原生环境的DNS服务器,保障企业容器化和非容器化业务服务的稳定运行。
浙公网安备 33010602011771号