使用 NodeLocalDNS 提升集群 DNS 性能和可靠性

本文主要分享如何使用 NodeLocal DNSCache 来提升集群中的 DNS 性能以及可靠性,包括部署、使用配置以及原理分析,最终通过压测表明使用后带来了高达 50% 的性能提升。

1.背景

什么是 NodeLocalDNS

NodeLocal DNSCache 是一套 DNS 本地缓存解决方案。NodeLocal DNSCache 通过在集群节点上运行一个 DaemonSet 来提高集群 DNS 性能和可靠性

为什么需要 NodeLocalDNS

处于 ClusterFirst 的 DNS 模式下的 Pod 可以连接到 kube-dns 的 serviceIP 进行 DNS 查询,通过 kube-proxy 组件添加的 iptables 规则将其转换为 CoreDNS 端点,最终请求到 CoreDNS Pod。

通过在每个集群节点上运行 DNS 缓存,NodeLocal DNSCache 可以缩短 DNS 查找的延迟时间、使 DNS 查找时间更加一致,以及减少发送到 kube-dns 的 DNS 查询次数。

在集群中运行 NodeLocal DNSCache 有如下几个好处:

  • 如果本地没有 CoreDNS 实例,则具有最高 DNS QPS 的 Pod 可能必须到另一个节点进行解析,使用 NodeLocal DNSCache 后,拥有本地缓存将有助于改善延迟
  • 跳过 iptables DNAT 和连接跟踪将有助于减少 conntrack 竞争并避免 UDP DNS 条目填满 conntrack 表(上面提到的 5s 超时问题就是这个原因造成的)
  • 从本地缓存代理到 kube-dns 服务的连接可以升级到 TCP,TCP conntrack 条目将在连接关闭时被删除,而 UDP 条目必须超时(默认 nfconntrackudp_timeout 是 30 秒)
  • 将 DNS 查询从 UDP 升级到 TCP 将减少归因于丢弃的 UDP 数据包和 DNS 超时的尾部等待时间,通常长达 30 秒(3 次重试+ 10 秒超时)

2. 如何使用 NodeLocalDNS

NodeLocalDNS 部署

要安装 NodeLocal DNSCache 也非常简单,直接获取官方的资源清单即可:

Ruby 复制代码
wget -c https://raw.githubusercontent.com/kubernetes/kubernetes/master/cluster/addons/dns/nodelocaldns/nodelocaldns.yaml

默认使用的镜像为registry.k8s.io/dns/k8s-dns-node-cache如果无法拉取镜像,可以替换成国内的 docker.io/dyrnq/k8s-dns-node-cache

Bash 复制代码
cp nodelocaldns.yaml nodelocaldns.yaml.bak

sed -i 's#registry\.k8s\.io/dns/k8s-dns-node-cache#docker\.io/dyrnq/k8s-dns-node-cache#g' nodelocaldns.yaml

该资源清单文件中包含几个变量,各自含义如下:

  • __PILLAR__DNS__DOMAIN__:表示集群域,默认为 cluster.local,它是用于解析 Kubernetes 集群内部服务的域名后缀。
  • __PILLAR__LOCAL__DNS__:表示 DNSCache 本地的 IP,也就是 NodeLocalDNS 要使用的 IP,默认为 169.254.20.10
  • __PILLAR__DNS__SERVER__ :表示 kube-dns 这个 Service 的 ClusterIP,一般默认为 10.96.0.10。通过kubectl get svc -n kube-system -l k8s-app=kube-dns -o jsonpath='{$.items[*].spec.clusterIP}' 命令获取

下面两个变量则不需要关系,NodeLocalNDS Pod 会自动配置,对应的值来源于 kube-dns 的 ConfigMap 和定制的 Upstream Server 配置。直接执行如下所示的命令即可安装:

  • __PILLAR__CLUSTER__DNS__: 表示集群内查询的上游 DNS 服务器,一般也指向 kube-dns 的 service IP,默认为 10.96.0.10。
  • __PILLAR__UPSTREAM__SERVERS__:表示为外部查询的上游服务器,如果没有专门的自建 DNS 服务的话,也可以填 kube-dns 的 service ip。

接下来将对应变量替换为真实值,具体如下:

Bash 复制代码
kubedns=`kubectl get svc kube-dns -n kube-system -o jsonpath={.spec.clusterIP}`
domain=cluster.local
localdns=169.254.20.10

echo kubedns=$kubedns, domain=$domain, localdns=$localdns

需要注意的是:根据 kube-proxy 运行模式不同,要替换的参数也不同,使用以下命令查看 kube-proxy 所在模式

Bash 复制代码
kubectl -n kube-system get cm kube-proxy -oyaml|grep mode

如果kube-proxy在 iptables 模式下运行, 则运行以下命令创建

Bash 复制代码
cp nodelocaldns.yaml nodelocaldns-iptables.yaml
sed -i "s/__PILLAR__LOCAL__DNS__/$localdns/g; 
        s/__PILLAR__DNS__DOMAIN__/$domain/g; 
        s/__PILLAR__DNS__SERVER__/$kubedns/g" nodelocaldns-iptables.yaml

node-local-dns Pod 会设置 PILLAR__CLUSTER__DNSPILLAR__UPSTREAM__SERVERS

如果 kube-proxy 在 ipvs 模式下运行, 则运行以下命令创建

Bash 复制代码
cp nodelocaldns.yaml nodelocaldns-ipvs.yaml

sed -i "s/__PILLAR__LOCAL__DNS__/$localdns/g; 
        s/__PILLAR__DNS__DOMAIN__/$domain/g; 
        s/,__PILLAR__DNS__SERVER__//g; 
        s/__PILLAR__CLUSTER__DNS__/$kubedns/g" nodelocaldns-ipvs.yaml

node-local-dns Pod 会设置 PILLAR__UPSTREAM__SERVERS

然后就是将替换后的 yaml apply 到集群里:

Bash 复制代码
#kubectl apply -f nodelocaldns-iptables.yaml
kubectl apply -f nodelocaldns-ipvs.yaml

会创建以下对象

Bash 复制代码
serviceaccount/node-local-dns created
service/kube-dns-upstream created
configmap/node-local-dns created
daemonset.apps/node-local-dns created
service/node-local-dns created

创建完成后,就能看到每个节点上都运行了一个pod,这里只有一个节点,所以就运行了一个

Bash 复制代码
[root@caas ~]# kubectl -n kube-system get po
NAME                         READY   STATUS    RESTARTS   AGE
node-local-dns-m8ktq         1/1     Running     0         8s

需要注意的是这里使用 DaemonSet 部署 node-local-dns 使用了 hostNetwork=true,会占用宿主机的 8080 端口,所以需要保证该端口未被占用。

NodeLocalDNS 配置

上一步部署好 NodeLocal DNSCache, 但是还差了很重要的一步,配置 pod 使用 NodeLocal DNSCache 作为优先的 DNS 服务器。

有以下几种方式:

  • 方式一:修改 kubelet 中的 dns nameserver 参数,并重启节点 kubelet。存在业务中断风险,不推荐使用此方式
    • 测试时可以用这个方式,比较简单
  • 方式二:创建 Pod 时手动指定 DNSConfig,比较麻烦,不推荐。
  • 方式三:借助 DNSConfig 动态注入控制器在 Pod 创建时配置 DNSConfig 自动注入,推荐使用此方式。
    • 需要自己实现一个 webhook,相当于把方式二自动化了,

方式一:修改 kubelet 参数

kubelet通过--cluster-dns--cluster-domain 两个参数来全局控制Pod DNSConfig。

  • cluster-dns :部署Pod时,默认采用的DNS服务器地址,默认只引用了kube-dns的 ServiceIP,需要增加一个 NodeLocalDNS 的 169.254.20.10 。
  • cluster-domain :部署 Pod 时,默认采用的 DNS 搜索域,保持原有搜索域即可,一般为cluster.local

/etc/systemd/system/kubelet.service.d/10-kubeadm.conf 配置文件中需要增加 一个 --cluster-dns 参数,设置值为NodeLocalDNS 的 169.254.20.10。

注意是在原有的前面增加一个 --cluster-dns,不是把原本的改了。

这样 Pod 中就会有两个 dns nameserver,如果新增的这个失效了,也可以使用旧的。

Bash 复制代码
vi /etc/systemd/system/kubelet.service.d/10-kubeadm.conf 
# 增加 --cluster-dns
--cluster-dns=169.254.20.10 --cluster-dns=<kube-dns ip> --cluster-domain=<search domain>

然后重启 kubelet 使其生效

Bash 复制代码
sudo systemctl daemon-reload
sudo systemctl restart kubelet

方式二:自定义 Pod dnsConfig

通过 dnsConfig 字段自定义 Pod dns 配置,nameservers 中除了指定 NodeLocalDNS 之外还指定了 KubeDNS,这样即使 NodeLocalDNS 异常也不影响 Pod 中的 DNS 解析。

YAML 复制代码
apiVersion: v1
kind: Pod
metadata:
  name: alpine
  namespace: default
spec:
  containers:
  - image: alpine
    command:
      - sleep
      - "10000"
    imagePullPolicy: Always
    name: alpine
  dnsPolicy: None
  dnsConfig:
    nameservers: ["169.254.20.10","10.96.0.10"]
    searches:
    - default.svc.cluster.local
    - svc.cluster.local
    - cluster.local
    options:
    - name: ndots
      value: "3"
    - name: attempts
      value: "2"
    - name: timeout 
      value: "1"
  • dnsPolicy:必须为None
  • nameservers:配置成 169.254.20.10 和 kube-dns 的 ServiceIP 地址。
  • searches:设置搜索域,保证集群内部域名能够被正常解析。
  • ndots:默认为 5,可以适当降低 ndots 以提升解析效率。

方式三:Webhook 自动注入 dnsConfig

DNSConfig 动态注入控制器可用于自动注入DNSConfig至新建的Pod中,避免您手工配置Pod YAML进行注入。本应用默认会监听包含node-local-dns-injection=enabled标签的命名空间中新建Pod的请求,您可以通过以下命令给命名空间打上Label标签。

部署后,只需要给 Namespace 打上 node-local-dns-injection=enabled label 即可,Webhook 检测就会自动给该 Namespace 下所有 Pod 配置 DNSConfig。

先挖个坑,下一篇做一个简单实现。

3. 压测

接下来进行压测,看一下性能提升。

这里使用修改 kubelet 参数方式暂时让 Pod 都使用 NodeLocalDNS,便于测试

测试环境:

1 master 1 worker 的 k8s 集群,节点规则统一 4C8G,空闲状态,未运行其他负载。

可以参考 Kubernetes教程(十一)---使用 KubeClipper 通过一条命令快速创建 k8s 集群 快速创建一个集群。

压测脚本

使用下面这个文件进行性能测试

Go 复制代码
// main.go
package main

import (
    "context"
    "flag"
    "fmt"
    "net"
    "sync/atomic"
    "time"
)

var host string
var connections int
var duration int64
var limit int64
var timeoutCount int64

func main() {
    flag.StringVar(&host, "host", "", "Resolve host")
    flag.IntVar(&connections, "c", 100, "Connections")
    flag.Int64Var(&duration, "d", 0, "Duration(s)")
    flag.Int64Var(&limit, "l", 0, "Limit(ms)")
    flag.Parse()

    var count int64 = 0
    var errCount int64 = 0
    pool := make(chan interface{}, connections)
    exit := make(chan bool)
    var (
       min int64 = 0
       max int64 = 0
       sum int64 = 0
    )

    go func() {
       time.Sleep(time.Second * time.Duration(duration))
       exit <- true
    }()

endD:
    for {
       select {
       case pool <- nil:
          go func() {
             defer func() {
                <-pool
             }()
             resolver := &net.Resolver{}
             now := time.Now()
             _, err := resolver.LookupIPAddr(context.Background(), host)
             use := time.Since(now).Nanoseconds() / int64(time.Millisecond)
             if min == 0 || use < min {
                min = use
             }
             if use > max {
                max = use
             }
             sum += use
             if limit > 0 && use >= limit {
                timeoutCount++
             }
             atomic.AddInt64(&count, 1)
             if err != nil {
                fmt.Println(err.Error())
                atomic.AddInt64(&errCount, 1)
             }
          }()
       case <-exit:
          break endD
       }
    }
    fmt.Printf("request count:%d\nerror count:%d\n", count, errCount)
    fmt.Printf("request time:min(%dms) max(%dms) avg(%dms) timeout(%dn)\n", min, max, sum/count, timeoutCount)
}

首先配置好 golang 环境,然后直接构建上面的测试应用:

Go 复制代码
go build -o testdns main.go

构建完成后生成一个 testdns 的二进制文件

跨节点 DNS 性能测试

首先测试跨节点 DNS 性能测试,因为随着集群规模扩大,CoreDNS 副本数和节点数很明显不能做到 1:1,因此大部分 DNS 请求都是跨节点的,这个性能也更能反映正常情况下的 DNS 性能。

一般推荐是 1:8,即 8 个节点对应 1 个 CoreDNS Pod

首先将 CoreDNS 副本数调整为 1,便于测试。

Bash 复制代码
kubectl -n kube-system scale deploy coredns --replicas=1

这样就是两个节点对应一个 CoreDNS Pod,就可以测试跨节点 DNS 解析性能了。

Bash 复制代码
[root@dns-1 go]# kubectl get node
NAME    STATUS   ROLES           AGE   VERSION
dns-1   Ready    control-plane   48m   v1.27.4
dns-2   Ready    <none>          48m   v1.27.4
[root@dns-1 go]# kubectl -n kube-system get po -owide -l k8s-app=kube-dns
NAME                       READY   STATUS    RESTARTS   AGE   IP             NODE    NOMINATED NODE   READINESS GATES
coredns-5d78c9869d-l7vgv   1/1     Running   0          12m   172.25.173.4   dns-1   <none>           <none>

当前 CoreDNS 在 dns-1 节点,那我们把测试 Pod 指定调度到 dns-2 节点。

通过 overrides 直接指定 nodeName,让 Pod 和 CoreDNS 分散到不同节点。

YAML 复制代码
kubectl run busybox3 --image=busybox:latest --restart=Never --overrides='{ "spec": { "nodeName": "dns-2" } }' -- sleep 10000

然后我们将这个二进制文件拷贝到 Pod 中去进行测试:

Nginx 复制代码
kubectl cp testdns busybox:/

拷贝完成后进入这个测试的 Pod 中去:

Bash 复制代码
kubectl exec -it busybox -- /bin/sh

然后我们执行 testdns 程序来进行压力测试,比如执行 200 个并发,持续 30 秒:

Bash 复制代码
# 对地址 kube-dns.kube-system 进行解析

/ # ./testdns -host kube-dns.kube-system -c 200 -d 30 -l 5000
lookup kube-dns.kube-system on 10.96.0.10:53: no such host
lookup kube-dns.kube-system on 10.96.0.10:53: no such host
lookup kube-dns.kube-system on 10.96.0.10:53: no such host
lookup kube-dns.kube-system on 10.96.0.10:53: no such host
lookup kube-dns.kube-system on 10.96.0.10:53: no such host
lookup kube-dns.kube-system on 10.96.0.10:53: no such host
lookup kube-dns.kube-system on 10.96.0.10:53: no such host
lookup kube-dns.kube-system on 10.96.0.10:53: no such host
lookup kube-dns.kube-system on 10.96.0.10:53: no such host
lookup kube-dns.kube-system on 10.96.0.10:53: no such host
lookup kube-dns.kube-system on 10.96.0.10:53: no such host
lookup kube-dns.kube-system on 10.96.0.10:53: no such host
lookup kube-dns.kube-system on 10.96.0.10:53: no such host
lookup kube-dns.kube-system on 10.96.0.10:53: no such host
lookup kube-dns.kube-system on 10.96.0.10:53: no such host
lookup kube-dns.kube-system on 10.96.0.10:53: no such host
lookup kube-dns.kube-system on 10.96.0.10:53: no such host
lookup kube-dns.kube-system on 10.96.0.10:53: no such host
lookup kube-dns.kube-system on 10.96.0.10:53: no such host
lookup kube-dns.kube-system on 10.96.0.10:53: no such host
lookup kube-dns.kube-system on 10.96.0.10:53: no such host
lookup kube-dns.kube-system on 10.96.0.10:53: no such host
lookup kube-dns.kube-system on 10.96.0.10:53: no such host
request count:131063
error count:23
request time:min(1ms) max(15050ms) avg(39ms) timeout(624n)

我们可以看到平均耗时为 39ms 左右,这个性能是比较差的,而且还有部分解析失败的条目。

同节点 DNS 性能测试

重新创建 busybox pod,指定调度到和 CoreDNS 同一个节点,测试同节点 DNS 解析性能。

理论上同节点性能会比跨节点提升不少

然后创建一个 Busybox Pod 用于测试,通过 overrides 直接指定 nodeName,让 Pod 和 CoreDNS 分散到不同节点。

YAML 复制代码
kubectl delete pod busybox

kubectl run busybox --image=busybox:latest --restart=Never --overrides='{ "spec": { "nodeName": "dns-1" } }' -- sleep 10000

然后我们将这个二进制文件拷贝到 Pod 中去进行测试:

Nginx 复制代码
kubectl cp testdns busybox:/

拷贝完成后进入这个测试的 Pod 中去:

Bash 复制代码
kubectl exec -it busybox -- /bin/sh

然后我们执行 testdns 程序来进行压力测试,比如执行 200 个并发,持续 30 秒:

Bash 复制代码
# 对地址 kube-dns.kube-system 进行解析

/ # ./testdns -host kube-dns.kube-system -c 200 -d 30 -l 5000
request count:217030
error count:0
request time:min(1ms) max(5062ms) avg(26ms) timeout(311n)

我们可以看到大部分平均耗时都是在 26ms 左右,相比之前的 40ms,提升了接近 50%,而且也没有出现超时、失败的情况。

NodeLocalDNS 测试

直接启动 Pod

YAML 复制代码
kubectl delete pod busybox
kubectl run busybox --image=busybox:latest --restart=Never -- sleep 10000

然后我们将这个二进制文件拷贝到 Pod 中去进行测试:

Nginx 复制代码
kubectl cp testdns busybox:/

拷贝完成后进入这个测试的 Pod 中去:

Bash 复制代码
kubectl exec -it busybox -- /bin/sh

然后我们执行 testdns 程序来进行压力测试,比如执行 200 个并发,持续 30 秒:

把 Pod 中的 DNS Nameserver 指向 169.254.20.10(即 NodeLocalDNS 地址),然后再次测试

Bash 复制代码
vi /etc/resolv.conf

增加以下内容

Bash 复制代码
nameserver 169.254.20.10

然后再次测试

Bash 复制代码
/ # ./testdns -host kube-dns.kube-system -c 200 -d 30 -l 5000
request count:224103
error count:0
request time:min(1ms) max(5057ms) avg(24ms) timeout(333n)

可以看到,平均耗时都是 24ms,比跨节点的 39ms 提升 50%,和同节点的 26ms 接近,这样说明跨节点 DNS 解析有大量性能损失。

NodeLocalDNS 和同节点对比依旧存在一些提升,因为:

  • 访问 CoreDNS 使用的是 service 的 clusterIP 10.96.0.10 最终会进过 iptables / ipvs 等规则转发到后端 CoreDNS Pod 中
  • 而访问 NodeLocalDNS 则是使用的 link-local ip 169.254.20.10,不会经过 iptables / ipvs 规则跳转,直接就会进入 NodeLocalDNS Pod。

因此,有略微的性能提升。

4.NodeLocal DNSCache 工作原理

这部分主要分析 NodeLocal DNSCache 工作原理。

工作原理分析

NodeLocalDNS 实际就是在每个节点上加了一个缓存,类似于 CDN,把 中心 CoreDNS 看做源站的话,node-local-dns 就是运行在不同区域的缓存。

Pod 优先从本地 NodeLocalDNS 做 DNS 解析,有数据则直接返回,否则 NodeLocalDNS 再找 KubeDNS 解析,然后本地把数据缓存下来。

具体流程正如 阿里云文档) 中的这个图所示:

首先控制面,创建 Pod 时 Admission Webhook 会自动注入 DNSConfig,已经注入 DNSConfig 和 未注入 DNSConfig 的 Pod 会拥有不同的情况。

具体如下:

1)已注入 DNS 本地缓存的Pod,默认会通过 NodeLocal DNSCache 监听于节点上的IP(169.254.20.10)解析域名。

Pod 内的 DNS 配置如下:

Bash 复制代码
/ # cat /etc/resolv.conf
search default.svc.cluster.local svc.cluster.local cluster.local
nameserver 169.254.20.10
nameserver 10.96.0.10
options ndots:5

169.254.20.10 为第一个 nameserver,因此会优先使用。

2)NodeLocal DNSCache 本地若无缓存应答解析请求,则会通过 kube-dns 服务请求 CoreDNS 进行解析。

NodeLocalDNS 的 Corefile 中相关配置如下:

YAML 复制代码
    .:53 {
        errors
        cache 30
        reload
        loop
        bind 169.254.20.10 __PILLAR__DNS__SERVER__
        forward . __PILLAR__UPSTREAM__SERVERS__
        prometheus :9253
        }

当无法解析时,会转发到上游服务,也就是 kube-dns。

3)已注入 DNS 本地缓存的 Pod,当无法连通 NodeLocal DNSCache 时,会继而直接通过 kube-dns 服务连接到CoreDNS 进行解析,此链路为备用链路。

Pod 中的 DNS 配置:

YAML 复制代码
/ # cat /etc/resolv.conf
search default.svc.cluster.local svc.cluster.local cluster.local
nameserver 169.254.20.10
nameserver 10.96.0.10
options ndots:5

Kube-dns 对应的 IP 10.96.0.10 也做为第二 nameserver ,因此NodeLocal DNS 异常时 Pod 也能正常进行 DNS 解析。

4)未注入 DNS本地缓存的 Pod,会通过标准的 kube-dns 服务链路连接到 CoreDNS 进行解析。

未注入 DNSConfig 的 Pod 默认 DNS 配置如下:

YAML 复制代码
/ # cat /etc/resolv.conf
search default.svc.cluster.local svc.cluster.local cluster.local
nameserver 10.96.0.10
options ndots:5

自然会直接请求 kube-dns

5)CoreDNS 对于非集群内域名,则会根据当前节点上的 /etc/resolv.conf 转发到外部 DNS 服务器。

Kube-dns 的 Corefile 中相关配置如下:

YAML 复制代码
    .:53 {
        errors
        health {
           lameduck 5s
        }
        // 省略...
        forward . /etc/resolv.conf {
           max_concurrent 1000
        }
    }

省略了其他无关配置,forward . /etc/resolv.conf 表示,遇到无法解析的请求时会根据 /etc/resolv.conf 文件中的配置进行转发。

而 CoreDNS Pod 中的 /etc/resolv.conf 文件又是 Pod 启动时从当前节点 copy 进去的,因此具体转发到哪儿就和 Pod 启动时节点上的 /etc/resolv.conf 配置有关。

为什么是 169.254.20.10 ?

为什么访问 169.254.20.10 这个 IP 就可以访问到 NodeLocalDNS ?

NodeLocalDNS 以 DaemonSet 方式运行,因此会在集群中每个节点上都启动一个 Pod。该 Pod 会为当前节点增加一张网卡,并将 IP 指定为 169.254.20.10

就像下面这样:

Bash 复制代码
47: nodelocaldns: <BROADCAST,NOARP> mtu 1500 qdisc noop state DOWN group default
    link/ether 56:9b:08:18:a6:75 brd ff:ff:ff:ff:ff:ff
    inet 169.254.20.10/32 scope global nodelocaldns
       valid_lft forever preferred_lft forever

NodelocalDNS 会以 hostNetwork 网络模式启动,并在前面新增网卡对应 IP (169.254.20.20)上启动服务。

由于我们前面的配置(修改 kubelet 或者 Pod 的 dnsConfig),Pod 里优先级最高的 DNS 服务器就是 169.254.20.20,因此 Pod 需要 DNS 解析时会优先访问 169.254.20.10,最终请求被同节点的 NodelocalDNS Pod 处理。

添加这个网卡的具体作用如下:

  1. 本地 DNS 服务: nodelocaldns 在每个节点上运行,通过监听 169.254.20.10 地址提供本地 DNS 服务。这个地址是一个 link-local 地址,仅在本地节点可达。Pod 内的 DNS 查询会被重定向到这个地址,从而实现在节点内解析服务的域名。
  2. 避免 DNS 查询离开节点: 由于 nodelocaldns 提供了节点内的 DNS 解析服务,这张网卡确保 DNS 查询不会离开节点。这对于集群内部的 DNS 查询来说是非常高效的,不需要离开节点就能解析服务的域名。
  3. 降低 DNS 查询延迟: 由于 nodelocaldns 在每个节点上运行,节点内的 DNS 查询可以更快速地完成,而不必经过集群网络。

简单的做一个实验

Bash 复制代码
# 创建一个新的网络接口 mynic
sudo ip link add mynic type dummy

# 分配IP地址给 eth1
sudo ip addr add 1.1.1.1/24 dev mynic

# 启动你的程序,让它监听在指定的IP地址上
# 例如,如果你有一个基于Python的简单HTTP服务器:
python3 -m http.server 9090 --bind 1.1.1.1

# 同一节点打开新终端测试能否访问到
curl 1.1.1.1:9090

是可以直接访问到的,NodeLocalDNS 添加网卡就是这个作用。

至于为什么是 169.254.20.10 这个 IP?

则是因为 169.254.0.0/16 地址范围是专门用于 link-local 通信的。这意味着这些地址仅在同一子网内可用,并且不需要经过路由器来进行通信。

在这个网络内使用 169.254.20.10 而不是.1 .2 这些则是留出几个位置,以避免冲突。

5. 小结

CoreDNS 本身性能差是因为跨节点访问导致的大量性能损耗,同时由于内核 DNAT bug 导致超时等情况。

NodeLocal DNSCache 具有以下优势:

  • 减少了平均 DNS 查找时间
  • 从 Pod 到其本地缓存的连接不会创建 conntrack 表条目。这样可以防止由于 conntrack 表耗尽和竞态条件而导致连接中断和连接被拒绝。

使用 NodeLocalDNS 后性能提升接近 40%, DNS 解析延迟从 39ms 降低到 24ms,且报错次数大幅下降。

NodeLocalDNS 则使用 DaemonSet 方式启动在每个节点都启动一个 Pod,同时使用 hostnetwork + link-local 地址来保证 Pod 中的 DNS 请求只会请求到本地的 NodeLocalDNS Pod,从而避免了跨节点问题,大幅提升性能。

最后 NodeLocalDNS 使用 Link-local 地址也避免了默认情况下使用 service 的 clusterIP 需要 iptables/ipvs 等规则跳转的的问题,在同节点基础上也实现了略微的性能提升。

因此,对于大规模集群,存在高并发的 DNS 请求,推荐使用 NodeLocal DNSCache。

6. 参考

在 Kubernetes 集群中使用 NodeLocal DNSCache

使用NodeLocal DNSCache

DNS 超时问题分析

DNS 压测

lixd/nodelocaldns-admission-webhook

相关推荐
KingRumn4 小时前
Linux信号之标准信号与实时信号
linux·算法
weixin_46686 小时前
K8S-特殊容器
云原生·容器·kubernetes
QT 小鲜肉7 小时前
【Linux命令大全】001.文件管理之git命令(实操篇)
linux·服务器·笔记·git·elasticsearch
半夏知半秋7 小时前
docker常用指令整理
运维·笔记·后端·学习·docker·容器
sishen41997 小时前
嵌入式Linux没有学习方向怎么办,嵌入式Linux怎么学
linux
逆风水手7 小时前
Ansible自动化运维入门指南
linux·运维·自动化·ansible
旖旎夜光9 小时前
Linux(3)(下)
linux·学习
没有bug.的程序员9 小时前
Nacos vs Eureka 服务发现深度对比
jvm·微服务·云原生·容器·eureka·服务发现
小鹿学程序9 小时前
任务一-1.子任务一:基础环境准备
linux·bigdata
岁月的眸9 小时前
【科大讯飞声纹识别和语音内容识别的实时接口实现】
人工智能·go·语音识别