DNS最佳实践

DNS是Kubernetes集群中至关重要的基础服务之一,在客户端设置不合理、集群规模较大等情况下DNS容易出现解析超时、解析失败等现象。本文介绍Kubernetes集群中DNS的最佳实践,帮助您避免此类问题。

前提条件

本文目录

有关CoreDNS的更多信息,请参见CoreDNS官方文档

优化域名解析请求

DNS域名解析请求是Kubernetes最高频的网络行为之一,其中很多请求是可以优化和避免的。您可以通过以下方式优化域名解析请求:

  • (推荐)使用连接池:当一个容器应用需要频繁请求另一服务时,推荐使用连接池。连接池可以将请求上游服务的链接缓存在内存中,避免每次访问时域名解析和TCP建连的开销。

  • 使用DNS缓存:

    • (推荐)当您的应用无法改造成通过连接池连接另一服务时,可以考虑在应用侧缓存DNS解析结果,具体操作,请参见使用节点DNS缓存NodeLocal DNSCache

    • 如果NodeLocal DNSCache无法适用的,可以在容器内置NSCD(Name Service Cache Daemon)缓存。关于如何使用NSCD缓存,请参见在Kubernetes集群中使用NSCD

  • 优化resolv.conf文件:由于resolv.conf文件中ndotssearch两个参数的机制作用,容器内配置域名的不同写法决定了域名解析的效率,关于ndotssearch两个参数的机制详情,请参见DNS原理和配置说明

  • 优化域名配置:当容器内应用需要访问某域名时,该域名按以下原则配置,可以最大程度减少域名解析尝试次数,继而减少域名解析耗时。

    • Pod访问同命名空间的Service,优先使用<service-name>访问,其中service-name代指Service名称。

    • Pod跨命名空间访问Service,优先使用<service-name>.<namespace-name>访问,其中namespace-name代指Service所处的命名空间。

    • Pod访问集群外部域名时,优先使用FQDN类型域名访问,这类域名通过常见域名最后加半角句号(.)的方式来指定地址,可以避免search搜索域拼接带来的多次无效搜索,例如需要访问www.aliyun.com,则优先使用FQDN类型域名www.aliyun.com.来访问。

使用合适的容器镜像

Alpine容器镜像内置的musl libc库与标准glibc的实现存在以下差异:

  • 3.3及更早版本Alpine不支持search参数,不支持搜索域,无法完成服务发现。

  • 并发请求/etc/resolv.conf中配置的多个DNS服务器,导致NodeLocal DNSCache优化失效。

  • 并发使用同一Socket请求A和AAAA记录,在旧版本内核上触发Conntrack源端口冲突导致丢包问题。

关于以上问题的更多信息,请参见musl libc

当Kubernetes集群中部署的容器采用了Alpine作为基础镜像时,可能会因为上述musl libc特性而无法正常解析域名,建议尝试更换基础镜像,如Debian、CentOS等。

避免IPVS缺陷导致的DNS概率性解析超时问题

当集群使用IPVS作为kube-proxy负载均衡模式时,您可能会在CoreDNS缩容或重启时遇到DNS概率性解析超时的问题。该问题由社区Linux内核缺陷导致,具体信息,请参见IPVS

您可以通过以下任意方式降低IPVS缺陷的影响:

使用节点DNS缓存NodeLocal DNSCache

在ACK集群中部署NodeLocal DNSCache可以提升服务发现的稳定性和性能,NodeLocal DNSCache通过在集群节点上作为DaemonSet运行DNS缓存代理来提高集群DNS性能。

关于更多NodeLocal DNSCache的介绍及如何在ACK集群中部署NodeLocal DNSCache的具体步骤,请参见使用NodeLocal DNSCache

使用合适的CoreDNS版本

CoreDNS对Kubernetes版本实现了较好的向后兼容,建议您保持CoreDNS版本为较新的稳定版本。ACK组件管理中心提供了CoreDNS的安装、升级、配置能力,您可以关注组件管理中组件状态,若CoreDNS组件显示可升级,请尽快选择业务低峰期进行升级。

CoreDNS v1.7.0以下的版本存在风险隐患,包括且不仅限于以下:

不同版本的Kubernetes集群,推荐的CoreDNS最低版本有所区别。如下表:

Kubernetes版本

推荐的最低CoreDNS版本

v1.14.8以下(停止维护)

v1.6.2

v1.14.8及以上,1.20.4以下

v1.7.0.0-f59c03d-aliyun

1.20.4及以上

v1.8.4.1-3a376cc-aliyun

说明

Kubernetes 1.14.8以下的版本现已停止维护,请尽快升级至更高版本后,升级CoreDNS。

监控CoreDNS运行状态

监控指标

CoreDNS通过标准的Prometheus接口暴露出解析结果等健康指标,发现CoreDNS服务端甚至上游DNS服务器的异常。

阿里云Prometheus监控默认内置了CoreDNS相关的指标监控和告警规则,您可以在容器服务管理控制台开启Prometheus及仪表盘功能。具体操作,请参见CoreDNS组件监控

若您是自建Prometheus监控Kubernetes集群,可以在Prometheus观测相关指标,并对重点指标设置告警。具体操作,请参见CoreDNS Prometheus官方文档

运行日志

在DNS异常发生的情况下,CoreDNS日志有助于您快速诊断异常根因。建议您开启CoreDNS域名解析日志和其SLS日志采集,具体操作,请参见分析和监控CoreDNS日志

Kubernetes事件投递

在v1.9.3.6-32932850-aliyun及以上版本的CoreDNS中,您可以开启k8s_event插件以将CoreDNS关键日志以Kubernetes事件的形式投递到事件中心。关于k8s_event插件,请参见k8s_event

全新部署的CoreDNS会默认开启该功能。如果您是从低版本CoreDNS升级到v1.9.3.6-32932850-aliyun及以上版本的,您需要手动修改配置文件以开启该功能。

  1. 执行如下命令,打开CoreDNS配置文件。

    kubectl -n kube-system edit configmap/coredns
  2. 新增kubeAPI和k8s_event插件。

    apiVersion: v1
    data:
      Corefile: |
        .:53 {
            errors
            health {
                lameduck 15s
            }
    
            // 新增开始(请忽略其他差异)。
            kubeapi
            k8s_event {
              level info error warning // 将info、error、warning状态关键日志投递。
            }
            // 新增结束。
    
            kubernetes cluster.local in-addr.arpa ip6.arpa {
                pods verified
                fallthrough in-addr.arpa ip6.arpa
            }
            // 下方略。
        }
  3. 检查CoreDNS Pod运行状态和运行日志,运行日志中出现reload字样后说明修改成功。

合理调整集群CoreDNS部署状态

合理调整CoreDNS副本数

建议您在任何情况下设置CoreDNS副本数应至少为2,且副本数维持在一个合适的水位以承载整个集群的解析。

CoreDNS所能提供的域名解析QPS与CPU消耗成正相关,开启缓存的情况下,单个CPU可以支撑10000+ QPS的域名解析请求。不同类型的业务对域名请求的QPS需求存在较大差异,您可以观察每个CoreDNS副本的峰值CPU使用量,如果其在业务峰值期间占用CPU大于一核,建议您对CoreDNS进行副本扩容。无法确定峰值CPU使用量时,可以保守采用副本数和集群节点数1:8的比值来部署,即每扩容8个集群节点,增加一个CoreDNS副本,但副本数不应大于10。针对100节点以上的集群,推荐使用节点DNS缓存NodeLocal DNSCache。具体操作,请参见使用节点DNS缓存NodeLocal DNSCache

说明

UDP报文缺少重传机制,在CoreDNS副本停止过程中,CoreDNS任意副本缩容或重启可能会导致接收到的UDP报文丢失,触发整个集群域名解析超时或异常。当集群节点存在IPVS UDP缺陷导致的丢包风险时,CoreDNS任意副本缩容或重启可能会导致长达五分钟的整个集群域名解析超时或异常。关于IPVS缺陷导致解析异常的解决方案,请参见IPVS缺陷导致解析异常

合理分配CoreDNS副本运行的位置

建议您在部署CoreDNS副本时,应将CoreDNS副本打散在不同可用区、不同集群节点上,避免单节点、单可用区故障。CoreDNS默认配置了按节点的弱反亲和性,可能会因为节点资源不足导致部分或全部副本部署在同一节点上,如果遇到这种情况,请删除Pod重新触发其调度来调整。

CoreDNS所运行的集群节点应避免CPU、内存用满的情况,否则会影响域名解析的QPS和响应延迟。当集群节点条件允许时,可以考虑使用自定义参数将CoreDNS调度至独立的集群节点上,以提供稳定的域名解析服务。关于CoreDNS调度至独立的集群节点的方式,请参见使用自定义参数完成CoreDNS独占部署。

手动扩容副本数

当集群节点数长时间较为固定时,您可以通过以下命令扩容CoreDNS副本数。

kubectl scale --replicas={target} deployment/coredns -n kube-system
说明

将目标副本数{target}设置成目标值。

自动扩容副本数(cluster-autoscaler)

当集群节点数不断增长时,您可以通过以下YAML部署集群水平伸缩器cluster-proportional-autoscaler动态扩容副本数量:

---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: dns-autoscaler
  namespace: kube-system
  labels:
    k8s-app: dns-autoscaler
spec:
  selector:
    matchLabels:
      k8s-app: dns-autoscaler
  template:
    metadata:
      labels:
        k8s-app: dns-autoscaler
    spec:
      serviceAccountName: admin
      containers:
      - name: autoscaler
        image: registry.cn-hangzhou.aliyuncs.com/acs/cluster-proportional-autoscaler:1.8.4
        resources:
          requests:
            cpu: "200m"
            memory: "150Mi"
        command:
        - /cluster-proportional-autoscaler
        - --namespace=kube-system
        - --configmap=dns-autoscaler
        - --nodelabels=type!=virtual-kubelet
        - --target=Deployment/coredns
        - --default-params={"linear":{"coresPerReplica":64,"nodesPerReplica":8,"min":2,"max":100,"preventSinglePointFailure":true}}
        - --logtostderr=true
        - --v=9

上述使用线程伸缩策略中,CoreDNS副本数的计算公式为replicas = max (ceil (cores × 1/coresPerReplica), ceil (nodes × 1/nodesPerReplica) ),且CoreDNS副本数受到maxmin限制。线程伸缩策略参数如下。

{
      "coresPerReplica": 64,
      "nodesPerReplica": 8,
      "min": 2,
      "max": 100,
      "preventSinglePointFailure": true
}

基于CPU负载指标自动扩容副本数(HPA)

由于HPA会频繁触发CoreDNS副本缩容,建议您不要使用容器水平扩缩容(HPA),如果您的场景下必须依赖于HPA,请参考以下基于CPU负载的策略配置:

---
apiVersion: autoscaling/v2beta1
kind: HorizontalPodAutoscaler
metadata:
  name: coredns-hpa
  namespace: kube-system
spec:
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: coredns
  minReplicas: 2
  maxReplicas: 10
  metrics:
  - type: Resource
    resource:
      name: cpu
      targetAverageUtilization: 50
说明

关于HPA使用方式的更多信息,请参见容器水平伸缩(HPA)

使用自定义参数完成CoreDNS独占节点部署

  1. 登录容器服务管理控制台,在左侧导航栏单击集群

  2. 集群列表页面,单击目标集群名称,然后在左侧导航栏,选择节点管理 > 节点

  3. 节点页面,单击标签与污点管理

  4. 标签与污点管理页面,选中目标节点,单击添加标签

    说明

    节点数应大于CoreDNS副本数,避免单个节点上运行多个CoreDNS副本。

  5. 添加对话框中,设置以下参数,然后单击确定

    • 名称:node-role.kubernetes.io/coredns

    • :true

  6. 在集群管理页左侧导航栏中,选择运维管理 > 组件管理,然后搜索CoreDNS

  7. 单击CoreDNS卡片的配置,在配置对话框,单击NodeSelector右侧的,设置以下参数,然后单击确定

    • Key:node-role.kubernetes.io/coredns

    • Value:true

    CoreDNS会重新调度至指定标签的节点上。

合理配置CoreDNS

容器服务ACK仅提供CoreDNS的默认配置,您应关注配置中的各个参数,优化其配置,以使CoreDNS可以为您的业务容器正常提供DNS服务。CoreDNS的配置非常灵活,具体操作,请参见DNS原理和配置说明CoreDNS官方文档

您也可以通过容器智能运维中定时巡检和故障诊断功能完成CoreDNS配置文件的检查。当容器智能运维检查结果中提示CoreDNS Configmap配置异常时,请逐个检查以上项目。

合理调整CoreDNS的资源Request和Limit

CoreDNS的资源消耗具有以下特点:

  • CoreDNS启动、配置文件重新加载,APIServer连接、重连的过程中,CPU和内存占用会有增加。

  • CoreDNS的CPU占用和其承载的域名QPS呈正相关。

  • CoreDNS的内存占用和集群规模、Service数量呈正相关。

部署CoreDNS时采用的默认资源Request和Limit如下表,建议您根据集群实际运行情况及时做出调整,具体操作,请参见运维管理-组件管理-网络-CoreDNS-参数配置。

资源类型

Request/Limit

默认值

备注

CPU

Request

100m

Limit

不设置

调低该值会影响CoreDNS能提供的域名解析QPS。

内存

Request

100 Mi

Limit

2 Gi

调低该值可能会有内存不足OOM风险。

说明

低于1.8.4版本的CoreDNS默认设置可能会与上表有所差异,请进行调整。

关闭kube-dns服务的亲和性配置

亲和性配置可能导致CoreDNS不同副本间存在较大负载差异,建议按以下步骤关闭:

控制台操作方式

  1. 登录容器服务管理控制台
  2. 在控制台左侧导航栏中,单击集群

  3. 集群列表页面中,单击目标集群名称或者目标集群右侧操作列下的详情

  4. 在集群管理页左侧导航栏中,选择网络 > 服务

  5. 在kube-system命名空间下,单击服务kube-dns右侧的YAML编辑

    • 如果发现sessionAffinity字段为None,则无需进行以下步骤。

    • 如果发现sessionAffinityClientIP,则进行以下步骤。

  6. 删除sessionAffinitysessionAffinityConfig及所有子键,然后单击更新

    # 删除以下所有内容。
    sessionAffinity: ClientIP
      sessionAffinityConfig:
        clientIP:
          timeoutSeconds: 10800
  7. 再次单击服务kube-dns右侧的YAML编辑,校验sessionAffinity字段是否为None,为None则Kube-DNS服务变更成功。

命令行方式

  1. 执行以下命令查看kube-dns服务配置信息。

    kubectl -n kube-system get svc kube-dns -o yaml
    • 如果发现sessionAffinity字段为None,则无需执行以下步骤。

    • 如果发现sessionAffinityClientIP,则执行以下步骤。

  2. 执行以下命令打开并编辑名为kube-dns的服务。

    kubectl -n kube-system edit service kube-dns
  3. 删除sessionAffinity相关设置(sessionAffinitysessionAffinityConfig及所有子键),并保存退出。

    # 删除以下所有内容。
    sessionAffinity: ClientIP
      sessionAffinityConfig:
        clientIP:
          timeoutSeconds: 10800
  4. 修改完成后,再次运行以下命令查看sessionAffinity字段是否为None,为None则Kube-DNS服务变更成功。

    kubectl -n kube-system get svc kube-dns -o yaml

关闭Autopath插件

部分早期版本的CoreDNS开启了Autopath插件,该插件在一些极端场景下会导致解析结果出错,请确认其是否处于开启状态,并编辑配置文件将其关闭。更多信息,请参见Autopath

说明

关闭Autopath插件后,客户端发起的域名解析请求QPS最高会增加3倍,解析单个域名耗时最高增加3倍,请关注CoreDNS负载和业务影响。

  1. 执行kubectl -n kube-system edit configmap coredns命令,打开CoreDNS配置文件。

  2. 删除autopath @kubernetes一行后保存退出。

  3. 检查CoreDNS Pod运行状态和运行日志,运行日志中出现reload字样后说明修改成功。

配置CoreDNS优雅退出

说明

CoreDNS刷新配置过程中,可能会占用额外内存。修改CoreDNS配置项后,请观察Pod运行状态,如果出现Pod内存不足的情况,请及时修改CoreDNS Deployment中容器内存限制,建议将内存调整至2 GB。

控制台操作方式

  1. 登录容器服务管理控制台

  2. 在控制台左侧导航栏中,单击集群

  3. 集群列表页面中,单击目标集群名称或者目标集群右侧操作列下的详情

  4. 在集群管理页左侧导航栏中,选择配置管理 > 配置项

  5. 在kube-system命名空间下,单击配置项coredns右侧的YAML编辑

  6. 参考下列的CoreDNS配置文件,保证health插件开启,并调整lameduck参数为15s,然后单击确定

  7. .:53 {
            errors       
            # health插件在不同的CoreDNS版本中可能有不同的设置情形。
            # 情形一:默认未启用health插件。   
            # 情形二:默认启用health插件,但未设置lameduck时间。
            health      
            # 情形三:默认启用health插件,设置了lameduck时间为5s。   
            health {
                lameduck 5s
            }      
            # 对于以上三种情形,应统一修改成以下,调整lameduck参数为15s。
            health {
                lameduck 15s
            }       
            # 其它插件不需要修改,此处省略。
        }

如果CoreDNS Pod正常运行则说明CoreDNS优雅退出的配置更新成功。如果CoreDNS Pod出现异常,可以通过查看Pod事件及日志定位原因。

命令行操作方式

  1. 执行以下命令打开CoreDNS配置文件。

  2. kubectl -n kube-system edit configmap/coredns
  3. 参考下列的Corefile文件,保证health插件开启,并调整lameduck参数为15s

  4. .:53 {
            errors     
            # health插件在不同的CoreDNS版本中可能有不同的设置情形。
            # 情形一:默认未启用health插件。     
            # 情形二:默认启用health插件,但未设置lameduck时间。
            health
            # 情形三:默认启用health插件,设置了lameduck时间为5s。   
            health {
                lameduck 5s
            }
            # 对于以上三种情形,应统一修改成以下,调整lameduck参数为15s。
            health {
                lameduck 15s
            }
            # 其它插件不需要修改,此处省略。
        }
  5. 修改CoreDNS配置文件后保存退出。

  6. 如果CoreDNS正常运行则说明CoreDNS优雅退出的配置更新成功。如果CoreDNS Pod出现异常,可以通过查看Pod事件及日志定位原因。

配置Forward插件与上游VPC DNS服务器的默认协议

NodeLocal DNSCache采用TCP协议与CoreDNS进行通信,CoreDNS会根据请求来源使用的协议与上游DNS服务器进行通信。因此默认情况下,来自业务容器的集群外部域名解析请求会依次经过NodeLocal DNSCache、CoreDNS,最终以TCP协议请求VPC内DNS服务器,即ECS上默认配置的100.100.2.136和100.100.2.138两个 IP。

VPC内DNS服务器对TCP协议支持有限,如果您使用了NodeLocal DNSCache,您需要修改CoreDNS配置,让其总是优先采用UDP协议与上游DNS服务器进行通信,避免解析异常。建议您使用以下方式修改CoreDNS配置文件,即修改命名空间kube-system下名为coredns的ConfigMap。具体操作,请参见管理配置项。在forward插件中指定请求上游的协议为perfer_udp,修改之后CoreDNS会优先使用UDP协议与上游通信。修改方式如下所示:

# 修改前
forward . /etc/resolv.conf
# 修改后
forward . /etc/resolv.conf {
  prefer_udp
}

配置Ready就绪探针插件

大于1.5.0版本的CoreDNS必须配置ready插件以启用就绪探针。

  1. 执行如下命令,打开CoreDNS配置文件。

    kubectl -n kube-system edit configmap/coredns
  2. 检查是否包含ready一行,若无,则增加ready一行,按Esc键、输入:wq!并按Enter键,保存修改后的配置文件并退出编辑模式。

    apiVersion: v1
    data:
     Corefile: |
      .:53 {
        errors
        health {
          lameduck 15s
        }
        ready # 如果没有这一行,请增加本行,注意缩进与Kubernetes保持一致。
        kubernetes cluster.local in-addr.arpa ip6.arpa {
          pods verified
          fallthrough in-addr.arpa ip6.arpa
        }
        prometheus :9153
        forward . /etc/resolv.conf {
          max_concurrent 1000
                prefer_udp
        }
        cache 30
        loop
        log
        reload
        loadbalance
      }
  3. 检查CoreDNS Pod运行状态和运行日志,运行日志中出现reload字样后说明修改成功。