大跨度升级ingress-nginx的版本

背景

当前ingress-nginx的版本比较旧(0.9.0)存在bug,比如:keep-alive不生效,另一方面对于用户呼声较高的新需求:金丝雀发布也缺少支持。因此考虑做大版本升级。经过几个月的测试、灰度、甚至回退最终升级成功。这里记录下遇到的问题。

问题1 升级后nginx主机定期系统级别OOM 宿主机死机

分析过程:

  • 时间特征 分析日志发现内存故障点时间集中在凌晨2点多: 3台ingress-nginx-controller都是在凌晨两点多发生的异常,内存在持续上涨后,短期骤增,cpu使用率上涨、网络流量大幅上涨,

  • 对象特征 通过分析日志,发现在那段时间有大量的5555、8144端口后台连接失败。如图1是,排除5555或8144的端口后没剩下几条异常日志了。 排除后的日志相比极少:

  • 主机特征 通过分析日志异常来源比例,可知分散较为均衡,10.10.28.21是因为中途死机,导致日志未能发送过来,因此占比偏少。

通过上述分析大概可得知:这是某个服务,在特定时间场景下,触发所有ingress-nginx的异常。继续分析日志。

  • nginx的error.log: 2019/09/16 18:31:06 [error] 8590#8590: *2624322 connect() failed (111: Connection refused) while connecting to upstream, client: 10.255.3.2, server: 0.0.0.0:59957, upstream: “10.12.3.3:8144”, bytes from/to client:0/0, bytes from/to upstream:0/0

对比nginx配置文件: server { preread_by_lua_block { ngx.var.proxy_upstream_name="tcp-test-settlement-quickpayment-8144”; } listen 59957; proxy_timeout 600s; proxy_pass upstream_balancer; }

这是tcp服务 kubectl get cm -n ingress-nginx tcp-services -o yaml apiVersion: v1 data: …(略) “59957”: test-settlement/quickpayment:8144 “59987”: test-jinganghuiguiyi/galaxy-server-03:8144 …(略) kind: ConfigMap metadata: annotations: nginx.ingress.kubernetes.io/ingress.class: nginx creationTimestamp: 2018-07-16T06:50:54Z name: tcp-services namespace: ingress-nginx resourceVersion: “1538382215” selfLink: /api/v1/namespaces/ingress-nginx/configmaps/tcp-services uid: 95f0e9f9-88c4-11e8-a98d-5254002f5f6e

  • 手动测试连通性: 随机telnet一下,外部的59989是通的,但有的pod的8144端口未监听,导致nginx错误日志出现upstream failed,但为啥一直在刷?
2019/09/17 03:40:16 [error] 2314#2314: *2891228 connect() failed (111: Connection refused) while connecting to upstream, client: 10.10.239.245, server: 0.0.0.0:59987, upstream: "10.12.44.70:8144", bytes from/to client:0/0, bytes from/to upstream:0/0
2019/09/17 03:40:16 [error] 2314#2314: *2891228 connect() failed (111: Connection refused) while connecting to upstream, client: 10.10.239.245, server: 0.0.0.0:59987, upstream: "10.12.44.70:8144", bytes from/to client:0/0, bytes from/to upstream:0/0
2019/09/17 03:40:16 [error] 2314#2314: *2891228 connect() failed (111: Connection refused) while connecting to upstream, client: 10.10.239.245, server: 0.0.0.0:59987, upstream: "10.12.44.70:8144", bytes from/to client:0/0, bytes from/to upstream:0/0
2019/09/17 03:40:16 [error] 2312#2312: *2977370 connect() failed (111: Connection refused) while connecting to upstream, client: 10.10.239.245, server: 0.0.0.0:59947, upstream: "10.12.40.136:8144", bytes from/to client:0/0, bytes from/to upstream:0/0
2019/09/17 03:40:16 [error] 2314#2314: *2891228 connect() failed (111: Connection refused) while connecting to upstream, client: 10.10.239.245, server: 0.0.0.0:59987, upstream: "10.12.44.70:8144", bytes from/to client:0/0, bytes from/to upstream:0/0
2019/09/17 03:40:16 [error] 2314#2314: *2891228 connect() failed (111: Connection refused) while connecting to upstream, client: 10.10.239.245, server: 0.0.0.0:59987, upstream: "10.12.44.70:8144", bytes from/to client:0/0, bytes from/to upstream:0/0
2019/09/17 03:40:16 [error] 2312#2312: *2977370 connect() failed (111: Connection refused) while connecting to upstream, client: 10.10.239.245, server: 0.0.0.0:59947, upstream: "10.12.40.136:8144", bytes from/to client:0/0, bytes from/to upstream:0/0
2019/09/17 03:40:16 [error] 2314#2314: *2891228 connect() failed (111: Connection refused) while connecting to upstream, client: 10.10.239.245, server: 0.0.0.0:59987, upstream: "10.12.44.70:8144", bytes from/to client:0/0, bytes from/to upstream:0/0

有两种可能,1是外部不断请求,2是内部不断重试,从前端请求流量看并没过多的请求,因此怀疑是陷入不断的重试中了。

测试验证

  • 当前版本0.25.0的ingress-nginx使用的openresty1.15.8,手动部署一个openresty并转发tcp流量到一个不存在的地址,测试发现并未一直重试。
  • 当前版本ingress-nginx还使用了lua,因此使用lua脚本的替代配置文件的upstream-server的方式,结果复现了一直重试的情况。具体如下; 将nginx/lua/tcp_udp_balancer.lua中的设置拷贝到测试用的openresty中
function _M.balance()
  local balancer = get_balancer()
  if not balancer then
    return
  end

  local peer = balancer:balance()
  if not peer then
    ngx.log(ngx.WARN, "no peer was returned, balancer: " .. balancer.name)
    return
  end

  ngx_balancer.set_more_tries(1)

  local ok, err = ngx_balancer.set_current_peer(peer)
  if not ok then
    ngx.log(ngx.ERR, string.format("error while setting current upstream peer %s: %s", peer, err))
  end
end

配置为

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
         }
 }

于是搜索openresty相关的issue,找到一篇说明,是说如果用到需设置合理参数,否则就会这样,应避免使用balancer.set_more_tries(1) 或是额外添加proxy_next_upstream_tries 为非0的值。 https://github.com/openresty/lua-nginx-module/pull/1546 https://github.com/openresty/lua-nginx-module/issues/1545 最终,通过在nginx.tmpl中的tcp proxy的配置里加了两行参数: proxy_next_upstream_timeout 0; proxy_next_upstream_tries 3; 当然这两个值可以从nginx的configmap中获得来解决。

问题2 升级后nginx白名单失效

  • 确定allow deny等规则在ingress-nginx的配置中是存在的
  • 检查403日志中的来源IP是否符合预期,发现来源IP都是特定的10.255.0.x 这个IP是ULB向后转发时的serverIP(即云厂商的IP),说明我们没有获取到真实IP,于是查询set_real_ip相关配置,发现是空的:
{{/* Enable the real_ip module only if we use either X-Forwarded headers or Proxy Protocol. */}}
{{/* we use the value of the real IP for the geo_ip module */}}
{{ if or $cfg.UseForwardedHeaders $cfg.UseProxyProtocol }}
{{ if $cfg.UseProxyProtocol }}
real_ip_header      proxy_protocol;
{{ else }}
real_ip_header      {{ $cfg.ForwardedForHeader }};
{{ end }}

real_ip_recursive   on;
{{ range $trusted_ip := $cfg.ProxyRealIPCIDR }}
set_real_ip_from    {{ $trusted_ip }};
{{ end }}
{{ end }}

原来是新版本ingress在处理real_ip_header头时,新增了一个判断,只有启用了$cfg.UseForwardedHeaders或 $cfg.UseProxyProtocol,才会进行set_real_ip_from的设置。由于默认这两个参数都是未设置的,因此ingress不会获取真实IP,导致白名单失效(获取的都是ULB转发过来的10.10的地址)。

解决:在ingress-nginx的cm中新增字段:use-forwarded-headers: “true”

问题3 升级后的节点A流量是其他节点的数倍、日志量也比其他节点的多许多。

起初,只升级了一台ingress-nginx节点,观察了几天发现这台的流量一直是其他节点的数倍,流量图如下: 怀疑有2个可能:

  • ingress-nginx前的ULB分配流量有异常?

  • 新版本ingress-nginx的流量处理差异

    • 对于ULB,由于是第三方产品,一方面联系对方协助排查是否存在异常

    • 自测,在ULB中将一台老版本的nginx也禁用再启用,看看他的流量是否有明确的变化:

      • 如当前1分钟116932条请求
      • 禁用1分钟再启用后的14282少了90%多
      • 再启用后,量又上来。
    • 通过下图的PrometheUS和zabbix获取到数据显示又恢复到之前的量级上了。

  • ULB到各nginx节点的tcp连接数均衡,那么更像是client—nginx层面做了优化。

  • 后来,全部节点都升级完成之后,流量全部均衡

    升级完成后,通过监控可知之前流量更多的那个节点已经恢复了正常:

    更有意思的现象是,nginx的一些指标曲线也不再那么的锋利了,可能是在连接回收方面做了优化