开发者社区> 问答> 正文

SLB 7层负载均衡“HUNG”问题追查




最近接到博客园的反馈,SLB 7层负载均衡的实例会不定期出现流量突跌的情况,突跌持续10s左右;同时,SLB自身监控也观察到了相同的现象;


针对该问题,我们进行了持续追查,最终定位到是nginx配置的原因;在此,分享一下分析排查过程,希望对大家使用nginx有所帮助;


问题描述


1 SLB 7层负载均衡(nginx)流量会出现不定期的突跌,每次突跌持续10s左右;同时,每次突跌必然发生在 12点 或者  0点;


2 查看SLB实例流量图,发现 部分实例 在12点 和 0点 流量突增几十倍;


两个时间点吻合,初步推断是突增流量导致nginx异常,从而导致流量下跌;


分析过程


3 观察每台nginx流量,发现当前运行负载比较低,远远小于阈值;CPU/MEM/NET各项指标都不高;


4 通过抓包发现大量的 syn 包被丢弃重传;


从上述现象,怀疑是网络问题,但从协议栈/网卡/交换机多个层面排查,没有发现网络异常;


5 在 Nginx 的机器上 curl 服务的统计接口时也出现了请求被 hang 住的情况;- (突破点)


抓包发现即使是本机发起的请求也会出现 syn 包丢弃重传,从而基本可以确定不是网络的问题,而是我们ngnix有问题。


查看linux协议栈源码,引起 syn 包被丢弃的原因可能有以下两点:


1. Accept backlog(接收队列)满了
2.内存分配不出来了
内核代码如下:


int tcp_v4_conn_request(struct sock *sk, struct sk_buff *skb)
{

...

   if (sk_acceptq_is_full(sk) && inet_csk_reqsk_queue_young(sk) > 1) {
       NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_LISTENOVERFLOWS);
       goto drop;
   }

   req = inet_reqsk_alloc(&tcp_request_sock_ops);
   if (!req)
       goto drop;

...
}
机器内存是够用的,只能是 accept backlog 满掉了,


但是问题在于我们是给每一个 virtual ip 配置一个单独的 server { listen vip; } 的, 怎么会在 backlog 满的时候影响到其他业务的 virtual ip 呢?


我们再回到我们的 Nginx 的配置文件来:


http{
    server{
        listen 1.1.1.1:80;
        location /{
           return200"10.232.6.3:80";
       }
   }

    server{
        listen 1.1.1.2:80;
        location /{
           return200"10.232.6.3:80";
       }
   }

    server{
        listen 1.1.1.3:80;
        location /{
           return200"10.232.6.3:80";
        }
   }

    ...

    server{
        listen 80;
        location /{
           return200"0.0.0.0:80";
       }
   }
}
深入了解 Nginx 的同学看到这里或许也就了然了。


但是我们拥有非常多的 virtual ip server 在配置文件中,一开始也并没有注意到最后一条 listen 80 的配置(该配置用于 nginx健康检查 和 状态统计)。


原因定位:Nginx 处理 bind listen 的时候会对监听的所有 ip:port 做一次规整合并,也就是由于最后一条 listen 80 导致 Nginx 在 listen 的时候只 bind 了一个 0.0.0.0:80 端口, 之后请求进入 Nginx 的时候会通过 ip 再来查找其对应的 virtual server。这也就导致了我们前面看到的结果,当有瞬时的大流量进来时引起 accept backlog 被占满,从而也影响了其他 virtual ip 的服务。


我们也可以在 Nginx 源码里看到这点:


void
ngx_http_init_connection(ngx_connection_t *c)
{

   ...

   port = c->listening->servers;

   if (port->naddrs > 1) {

       /*
         * there are several addresses on this port and one of them
         * is an "*:port" wildcard so getsockname() in ngx_http_server_addr()
         * is required to determine a server address
         */

       if (ngx_connection_local_sockaddr(c, NULL, 0) != NGX_OK) {
           ngx_http_close_connection(c);
           return;
       }

       switch (c->local_sockaddr->sa_family) {

#if (NGX_HAVE_INET6)
       case AF_INET6:
           sin6 = (struct sockaddr_in6 *) c->local_sockaddr;

           addr6 = port->addrs;

           /* the last address is "*" */

           for (i = 0; i < port->naddrs - 1; i++) {
               if (ngx_memcmp(&addr6.addr6, &sin6->sin6_addr, 16) == 0) {
                   break;
               }
           }

           hc->addr_conf = &addr6.conf;

           break;
#endif

       default: /* AF_INET */
           sin = (struct sockaddr_in *) c->local_sockaddr;

           addr = port->addrs;

           /* the last address is "*" */

           for (i = 0; i < port->naddrs - 1; i++) {
               if (addr.addr == sin->sin_addr.s_addr) {
                   break;
               }
           }

           hc->addr_conf = &addr.conf;

           break;
       }

   } else {

       switch (c->local_sockaddr->sa_family) {

#if (NGX_HAVE_INET6)
       case AF_INET6:
           addr6 = port->addrs;
           hc->addr_conf = &addr6[0].conf;
           break;
#endif

       default: /* AF_INET */
           addr = port->addrs;
            hc->addr_conf = &addr[0].conf;
           break;
       }
   }

   /* the default server configuration for the address:port */
   hc->conf_ctx = hc->addr_conf->default_server->ctx;

   ...
}
这里是在建立连接结构体时去查找所属 server,可以清晰的看到针对一个 listening 会有多个 server,也就是说这些 server 公用了一个 listen socket,以及 backlog。


问题解决


原因定位了,解决可以有多种方法;


我们采取的措施是把 listen 80 这条配置加上本地内网IP  listen 172.168.1.1:80,这样Nginx 会对每个 virtual ip 进行一次 bind 和 listen,从而做到了实例间的隔离,各个 virtual ip 之间不会互相影响;


也就不再出现当有一个 virtual ip 瞬时流量过大时导致整个服务看起来像是 hung 住的问题。


展开
收起
huiweiliu 2014-07-23 15:53:40 13022 0
8 条回答
写回答
取消 提交回答
  • ReSLB7层负载均衡“HUNG”问题追查
    谢谢了,留个记号啦www.lehehe1.com/bzlc.asp
    2015-07-17 11:40:59
    赞同 展开评论 打赏
  • 支持
    2014-07-24 18:09:40
    赞同 展开评论 打赏
  • ReSLB7层负载均衡“HUNG”问题追查
    SLB是最前端的产品,这个问题之前还真没注意。博客园的发现问题精神 以及阿里云从这么深层次的挖掘问题原因 真不容易,真心为客户服务,赞一个。
    2014-07-23 18:05:21
    赞同 展开评论 打赏
  • ReSLB7层负载均衡“HUNG”问题追查
    对于这种排查问题的执着性和责任感赞一个!
    2014-07-23 17:13:02
    赞同 展开评论 打赏
  • ReSLB7层负载均衡“HUNG”问题追查
    技术帖,拜读了
    2014-07-23 16:12:55
    赞同 展开评论 打赏
  • ReSLB7层负载均衡“HUNG”问题追查
    2014-07-23 16:11:42
    赞同 展开评论 打赏
  • ReSLB7层负载均衡“HUNG”问题追查
    技术帖,拜读了
    2014-07-23 16:10:08
    赞同 展开评论 打赏
  • ReSLB7层负载均衡“HUNG”问题追查
    2014-07-23 16:10:00
    赞同 展开评论 打赏
滑动查看更多
问答排行榜
最热
最新

相关电子书

更多
《应用型负载均衡ALB产品解读》 立即下载
阿里云网络-SLB负载均衡产品介绍 立即下载
负载均衡SLB热点问题解答 立即下载