zoukankan      html  css  js  c++  java
  • 使用node local dns来提升ClusterDNS服务质量

    在 Kubernetes 集群会碰到这个间歇性 5 延迟的问题,Weave works 发布了一篇博客 Racy conntrack and DNS lookup timeouts 详细介绍了问题的原因。

    简单来说,由于 UDP 是无连接的,内核 netfilter 模块在处理同一个 socket 上的并发 UDP 包时就可能会有三个竞争问题。以下面的 conntrack 和 DNAT 工作流程为例:
    
    由于 UDP 的 connect 系统调用不会立即创建 conntrack 记录,而是在 UDP 包发送之后才去创建,这就可能会导致下面三种问题:
    
    两个 UDP 包在第一步 nf*conntrack*in 中都没有找到 conntrack 记录,所以两个不同的包就会去创建相同的 conntrack 记录(注意五元组是相同的)。
    一个 UDP 包还没有调用 get*unique*tuple 时 conntrack 记录就已经被另一个 UDP 包确认了。
    两个 UDP 包在 ipt*do*table 中选择了两个不同端点的 DNAT 规则。
    所有这三种场景都会导致最后一步 _*nf*conntrack_confirm 失败,从而一个 UDP 包被丢弃。由于 GNU C 库和 musl libc 库在查询 DNS 时,都会同时发出 A 和 AAAA DNS 查询,由于上述的内核竞争问题,就可能会发生其中一个包被丢掉的问题。丢弃之后客户端会超时重试,超时时间通常是 5 秒。

    简介: NodeLocal DNSCache在集群的上运行一个dnsCache daemonset来提高clusterDNS性能和可靠性。在ACK集群上的一些测试表明:相比于纯coredns方案,nodelocaldns + coredns方案能够大幅降低DNS查询timeout的频次,提升服务稳定性,能够扛住1倍多的QPS。

    NodeLocal DNSCache通过在集群上运行一个dnsCache daemonset来提高clusterDNS性能和可靠性。在ACK集群上的一些测试表明:相比于纯coredns方案,nodelocaldns + coredns方案能够大幅降低DNS查询timeout的频次,提升服务稳定性。

    本文将介绍如何在ACK集群上部署node local dns。

    部署nodelocaldns

    nodelocaldns通过添加iptables规则能够接收节点上所有发往169.254.20.10的dns查询请求,把针对集群内部域名查询请求路由到coredns;把集群外部域名请求直接通过host网络发往集群外部dns服务器。

    # 下载部署脚本
    $ curl https://node-local-dns.oss-cn-hangzhou.aliyuncs.com/install-nodelocaldns.sh
    # 部署。确保kubectl能够连接集群
    $ bash install-nodelocaldns.sh

    定制业务容器dnsConfig

    为了使业务容器能够使用nodelocaldns,需要将nameserver配置为169.254.20.10,而不是ClusterDNS。定制dnsConfig有以下几点需要注意到:

    1. dnsPolicy: None。不使用ClusterDNS。
    2. 配置searches,保证集群内部域名能够被正常解析。
    3. 适当降低ndots值。当前ACK集群ndots值默认为5,降低ndots值有利于加速集群外部域名访问。如果业务容器没有使用带多个dots的集群内部域名,建议将值设为2。
    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"]
        searches:
        - default.svc.cluster.local
        - svc.cluster.local
        - cluster.local
        options:
        - name: ndots
          value: "2"
    如何避免这个问题

    要避免 DNS 延迟的问题,就要设法绕开上述三个问题,所以就有下面几种方法:

    ①. 禁止并发 DNS 查询,比如在 Pod 配置中开启 single-request-reopen 选项强制 A 查询和 AAAA 查询使用相同的 socket:

    dnsConfig:
      options:
        - name: single-request-reopen

    ②. 禁用 IPv6 从而避免 AAAA 查询,比如可以给 Grub 配置 ipv6.disable=1 来禁止 ipv6(需要重启节点才可以生效)。

    通过大神写go 测试dns解析时间脚本进行了测试

    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() {
        // os.Args = append(os.Args, "-host", "www.baidu.com", "-c", "200", "-d", "30", "-l", "5000")
    
        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
    error count:%d
    ", count, errCount)
        fmt.Printf("request time:min(%dms) max(%dms) avg(%dms) timeout(%dn)
    ", min, max, sum/count, timeoutCount)
    }

    使用方法为:

    ./dns -host {service}.{namespace} -c 200 -d 30
    
    比如  ./dns  -host   redis.senyint  -c 200 -d 30

    阿里云的方式内部解析还是5秒左右,没用,   使用①+nodelocaldns方式解析时间控制在800ms左右

    参考摘自:

    https://developer.aliyun.com/article/709471

    https://blog.csdn.net/qq_23435961/article/details/106659069

    https://zhuanlan.zhihu.com/p/145127061

  • 相关阅读:
    word上怎么打钩
    POI操作excel常用方法总结
    web.xml中Filter过滤器标签说明
    IDEA使用操作说明(自己总结)
    windows 64位上oracle 11g安装
    List<T>与List<?>的区别
    java分页之假分页
    CDN之Web Cache
    HTTP之缓存技术
    HTTP之Cookie和Session
  • 原文地址:https://www.cnblogs.com/fengjian2016/p/13234596.html
Copyright © 2011-2022 走看看