Kubernetes ingress-nginx 4 层 tcp 代理,无限重试不存在的地址,高达百万次

Tags: kubernetes_problem 

本篇目录

说明

在 kubernetes 集群中部署了 ingress-nginx 0.25 ,并在名为 tcp-services 的 config-map 配置了 tcp 4 层代理:

data:
  "55556": beta-xxxx/xxx-cloud-server:5555

但是 service “beta-xxxx/xxx-cloud-server” 对应的容器没有开启 5555 端口,访问 ingress-nginx 的 55556 端口时,ingress-nginx 持续打印错误日志:

2019/09/17 03:46:20 [error] 2454#2454: *3584946 connect() failed (111: Connection refused) while connecting to upstream, client: 10.10.122.117, server: 0.0.0.0:55556, upstream: "10.12.39.132:5555", bytes from/to client:0/0, bytes from/to upstream:0/0
2019/09/17 03:46:20 [error] 2454#2454: *3584946 connect() failed (111: Connection refused) while connecting to upstream, client: 10.10.122.117, server: 0.0.0.0:55556, upstream: "10.12.26.199:5555", bytes from/to client:0/0, bytes from/to upstream:0/0
2019/09/17 03:46:20 [error] 2454#2454: *3584946 connect() failed (111: Connection refused) while connecting to upstream, client: 10.10.122.117, server: 0.0.0.0:55556, upstream: "10.12.39.132:5555", bytes from/to client:0/0, bytes from/to upstream:0/0

从日志可以判断,nginx-inress 在重复的、无间歇地连接没有提供服务的端口。

调查

ingress-nginx 0.25 基于 openresty/1.15.8.1,对应的 tcp 4 层代理配置如下:

server {
        upstream upstream_balancer {
                server 0.0.0.1:1234; # placeholder

                balancer_by_lua_block {
                        tcp_udp_balancer.balance()
                }
        }

        preread_by_lua_block {
                ngx.var.proxy_upstream_name="xxxx-server-5555";
        }

        listen                  55555;

        proxy_timeout           600s;
        proxy_pass              upstream_balancer;
}

因为代理过程是用 lua 脚本控制的,需要界定无限重试的行为是 openresty/nginx 自身的行为,还是 lua 脚本导致的行为。

在 nginx 1.12.2 中试验

先用 nginx version: nginx/1.12.2 试验一下。

参照 Module ngx_stream_proxy_module 中的例子,在 nginx 中配置 4 层代理,指向一个没有被监听的端口:

stream{
        server {
                proxy_timeout           600s;
                listen                  127.0.0.1:12345;
                proxy_pass              127.0.0.1:8080;

        }
}

访问 127.0.0.1:12345

$ curl 127.0.0.1:12345
curl: (56) Recv failure: Connection reset by peer

只有一行错误日志,一次重试:

2019/09/17 14:32:07 [error] 25923#0: *2 connect() failed (111: Connection refused) while connecting to upstream, client: 127.0.0.1, server: 127.0.0.1:12345, upstream: "127.0.0.1:8080", bytes from/to c    lient:0/0, bytes from/to upstream:0/0

在 openresty 1.15.8.1 中试验(非 lua 方式)

先用非 lua 方式,在配置文件中添加下面配置:

stream{
        upstream upstream_balancer{
                server                  127.0.0.1:8080;
        }
        server {
                proxy_timeout           600s;
                listen                  127.0.0.1:12346;
                proxy_pass              upstream_balancer;
        }
}

结果和 nginx 相同,只有一次重试。

在 openresty 1.15.8.1 中试验(lua 方式)

参照 tcp_udp_balancer.lua 使用 lua 脚本实现转发,依旧转发到不存在的端口:

stream{
        upstream upstream_balancer{
                server 0.0.0.1:1234; # placeholder
                balancer_by_lua_block {
                   local ngx_balancer = require("ngx.balancer")
                   ngx_balancer.set_more_tries(1)
                   local ok, err = ngx_balancer.set_current_peer("127.0.0.1","8080")
                   if not ok then
                      ngx.log(ngx.ERR, string.format("error while setting current upstream : %s", err))
                   end
                }
        }
        server {
                proxy_timeout           600s;
                listen                  127.0.0.1:12346;
                proxy_pass              upstream_balancer;
        }
}

这时候访问,curl 会一直等待:

$ curl 127.0.0.1:12346

^C

同时 error 日志中有大量日志,多达百万条:

openresty-1.15.8.1无限重试

将 ngx_balancer.set_more_tries(1) 修改为 ngx_balancer.set_more_tries(0) 则没有问题,但是 set_more_tries 接口的参数是 count 的意思,指定的重试次数而不是设置无限重试。

确定这是 openresty-1.15.8.1 的 bug:

使用 lua 脚本设置 tcp 转发规则时,如果 peer 地址不存在,会无限重试。

解决方法

openresty/lua-nginx-module #1545openresty/lua-nginx-module #1546 中提到这个问题,讨论的结果是不认为这是 bug,可以用 proxy_next_upstream_tries 参数解决这个问题:

stream {
        upstream upstream_balancer{
                server 0.0.0.1:1234; # placeholder
                balancer_by_lua_block {
                   local ngx_balancer = require("ngx.balancer")
                   ngx_balancer.set_more_tries(1)
                   local ok, err = ngx_balancer.set_current_peer("127.0.0.1","8080")
                   if not ok then
                      ngx.log(ngx.ERR, string.format("error while setting current upstream : %s", err))
                   end
                }
        }
        server {
                proxy_timeout              600s;
                listen                     127.0.0.1:12346;
                proxy_pass                 upstream_balancer;
                proxy_next_upstream_tries  3;                  # 最多重试三次
        }
}

查看 ingress-nginx 0.25 模板文件发现这应该是 ingress-nginx 的 bug,tcp services 和 udp services 中需要配置 proxy_next_upstream_tries:

    # TCP services
    {{ range $tcpServer := .TCPBackends }}
    server {
        preread_by_lua_block {
            ngx.var.proxy_upstream_name="tcp-{{ $tcpServer.Backend.Namespace }}-{{ $tcpServer.Backend.Name }}-{{ $tcpServer.Backend.Port }}";
        }

        ... 省略 ...
        # In case of errors try the next upstream server before returning an error
        proxy_next_upstream_timeout             0;
        proxy_next_upstream_tries               3;
    }
    {{ end }}

镜像制作方法: 编译与镜像制作

参考

  1. 李佶澳的博客
  2. Kubernetes 基于 openresty 的 ingress-nginx 的状态和配置查询
  3. Module ngx_stream_proxy_module
  4. ingress-nginx/rootfs/etc/nginx/lua/tcp_udp_balancer.lua
  5. set_more_tries
  6. Bug set_more_tries cause infinite retrying when no valid servers and proxy_next_proxy_tries=0
  7. fix(set_more_tries): fix infinite retry when no available servers and…
  8. proxy_next_upstream_tries
  9. balancer.set_more_tries(count) not work in openresty 1.15.8.2

kubernetes_problem

  1. kubernetes ingress-nginx 启用 upstream 长连接,需要注意,否则容易 502
  2. kubernetes ingress-nginx 的 canary 影响指向同一个 service 的所有 ingress
  3. ingress-nginx 启用 tls 加密,配置了不存在的证书,导致 unable to get local issuer certificate
  4. https 协议访问,误用 http 端口,CONNECT_CR_SRVR_HELLO: wrong version number
  5. Kubernetes ingress-nginx 4 层 tcp 代理,无限重试不存在的地址,高达百万次
  6. Kubernetes 集群中个别 Pod 的 CPU 使用率异常高的问题调查
  7. Kubernetes 集群 Node 间歇性变为 NotReady 状态: IO 负载高,延迟严重
  8. Kubernetes的nginx-ingress-controller刷新nginx的配置滞后十分钟导致504
  9. Kubernetes的Nginx Ingress 0.20之前的版本,upstream的keep-alive不生效
  10. Kubernetes node 的 xfs文件系统损坏,kubelet主动退出且重启失败,恢复后无法创建pod
  11. Kubernetes的Pod无法删除,glusterfs导致docker无响应,集群雪崩
  12. Kubernetes集群node无法访问service: kube-proxy没有正确设置cluster-cidr
  13. Kubernetes集群node上的容器无法ping通外网: iptables snat规则缺失导致
  14. Kubernetes问题调查: failed to get cgroup stats for /systemd/system.slice
  15. Kubelet1.7.16使用kubeconfig时,没有设置--require-kubeconfig,导致node不能注册
  16. Kubelet从1.7.16升级到1.9.11,Sandbox以外的容器都被重建的问题调查
  17. Kubernetes: 内核参数rp_filter设置为Strict RPF,导致Service不通
  18. Kubernetes使用过程中遇到的一些问题与解决方法
  19. Kubernetes集群节点被入侵挖矿,CPU被占满
  20. kubernetes的node上的重启linux网络服务后,pod无法联通
  21. kubernetes的pod因为同名Sandbox的存在,一直无法删除
  22. kubelet升级,导致calico中存在多余的workloadendpoint,node上存在多余的veth设备
  23. 使用petset创建的etcd集群在kubernetes中运行失败
  24. Kubernetes 容器启动失败: unable to create nf_conn slab cache
  25. 未在calico中创建hostendpoint,导致开启隔离后,在kubernetes的node上无法访问pod
  26. calico分配的ip冲突,pod内部arp记录丢失,pod无法访问外部服务
  27. kubernetes的dnsmasq缓存查询结果,导致pod偶尔无法访问域名
  28. k8s: rbd image is locked by other nodes
  29. kuberntes的node无法通过物理机网卡访问Service

推荐阅读

Copyright @2011-2019 All rights reserved. 转载请添加原文连接,合作请加微信lijiaocn或者发送邮件: [email protected],备注网站合作

友情链接:  系统软件  程序语言  运营经验  水库文集  网络课程  微信网文  发现知识星球