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所在名称空间下搜索,如果我们查询的域名跨名称空间,需要手动给出对应名称空间名称,即包含服务名,名称空间的域名;