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

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

问题描述

  1. SLB 7层负载均衡(nginx)流量会出现不定期的突跌,每次突跌持续10s左右;同时,每次突跌必然发生在 12点 或者  0点;
  2. 查看SLB实例流量图,发现 部分实例 在12点 和 0点 流量突增几十倍;

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

分析过程

  1. 观察每台nginx流量,发现当前运行负载比较低,远远小于阈值;CPU/MEM/NET各项指标都不高;
  2. 通过抓包发现大量的 syn 包被丢弃重传;

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

  1. 在 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 / {
            return 200 "10.232.6.3:80";
        }
    }
 
    server {
        listen 1.1.1.2:80;
        location / {
            return 200 "10.232.6.3:80";
        }
    }
 
    server {
        listen 1.1.1.3:80;
        location / {
            return 200 "10.232.6.3:80";
        }
    }
 
    ...
 
    server {
        listen 80;
        location / {
            return 200 "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[i].addr6, &sin6->sin6_addr, 16) == 0) {
                    break;
                }
            }
 
            hc->addr_conf = &addr6[i].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[i].addr == sin->sin_addr.s_addr) {
                    break;
                }
            }
 
            hc->addr_conf = &addr[i].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 住的问题。

SLB 7层负载均衡“HUNG”问题追查的更多相关文章

  1. Azure上七层负载均衡APP Gateway

    Azure的SLB和ILB是最常用的4层负载均衡工具.但有些场景是7层的负载均衡,SLB和ILB就无能为力了. Azure上已经推出了APP Gateway的服务,就是7层负载均衡的负载均衡器. 如上 ...

  2. 当Kubernets遇上阿里云 -之七层负载均衡(一).

    我们知道Kubernetes的service只能实现基于4层的负载均衡,无法提供7层之上的许多特性,诸如基于URL的负载均衡,SSL支持,三方授权等等:Ingress可以实现七层负载均衡的许多功能,唯 ...

  3. nginx 七层负载均衡

    [tcp] nginx 七层负载均衡 nginx负载均衡概述 当我们的Web服务器直接面向用户,往往要承载大量并发请求,单台服务器难以负荷,我使用多台Web服务器组成集群,前端使用Nginx负载均衡, ...

  4. 13、Nginx七层负载均衡

    1.Nginx负载均衡基本概述 1.1为什么需要使用负载均衡 当我们的Web服务器直接面向用户,往往要承载大量并发请求,单台服务器难以负荷,我使用多台WEB服务器组成集群,前端使用Nginx负载均衡, ...

  5. Linux架构之Nginx 七层负载均衡

    第50章 Nginx七层负载均衡 一.Nginx负载均衡基本概述 1)为什么要使用负载均衡 当我们的Web服务器直接面向用户,往往要承载大量并发请求,单台服务器难以负荷.使用多台Web服务器组成集群, ...

  6. 第十五章 nginx七层负载均衡

    一.Nginx负载均衡 1.为什么做负载均衡 当我们的Web服务器直接面向用户,往往要承载大量并发请求,单台服务器难以负荷,我使用多台Web服务器组成集群,前端使用Nginx负载均衡,将请求分散的打到 ...

  7. 阿里云slb和ucloud负载均衡ulb添加ssl证书将http服务https化的配置详解

    阿里云和ucloud服务器配置ssl证书将http服务https化的配置详解 项目背景: 苹果App于2017年1月1日将启用App Transport Security安全功能,即强制App通过HT ...

  8. Windows Azure支持七层负载均衡--Application Gateway

    一直以来Windows Azure的负载均衡(Loadbalancer)功能一直被客户诟病,无法其竞争对手(特别是国内的云厂商)匹敌. Windows Azure的负载均衡器是四层的,前期的版本不支持 ...

  9. 大型网站系统架构实践(四)http层负载均衡之haproxy实践篇(一)

    方案 上篇文章讲到了负载均衡的相关理论知识,这篇文章我打算讲讲实践方法以及实践中遇到的问题 方案:haproxy http层负载均衡 安装一个haproxy服务,两个web服务 haproxy:192 ...

随机推荐

  1. aaS软件的必要特征分析,一定是多租户特性吗

    本篇文章讲述了SaaS软件的必要特征一定是多租户特性?对于许多小型企业来说,SaaS是采用先进技术的最好途径,它消除了企业购买.构建和维护基础设施和应用程序的需要 课课家教育平台提醒各位:本篇文章纯干 ...

  2. IIS https绑定主机头方法 [可实现禁止直接通过IP访问及“IIS中绑定多个https(但需要多个IP地址)”]

    步骤: 打开:%systemroot%\SYSTEM32\inetsrv\config\ 打开:applicationHost.config 查找:<binding protocol=" ...

  3. nodejs一个函数实现消息队列中间件

    消息队列中间件(Message Queue)相信大家不会陌生,如Kafka.RabbitMQ.RocketMQ等,已经非常成熟,在大大小小的公司和项目中也已经广泛使用. 有些项目中,如果是只使用初步的 ...

  4. DOM笔记(十二):又谈原型对象

    因为之前谢过一篇关于原型对象的笔记:浅谈JavaScript中的原型模式.现在我又重新看到这个话题,对原型有了进一步的理解,所以,又要谈谈原型对象. 一.理解原型对象 创建的每一个函数都有一个prot ...

  5. 继续折腾LNK 2005错误

    这次是因为要把一个很久的老项目改成使用Unicode字符集,又一次遇到了LNK 2005错误 先说说怎么把老项目改成Unicode字符集吧,首先要有足够的信心能把项目改好,比如我这次改的项目,也不算很 ...

  6. assert函数和捕获异常

    assert函数: C语言和C++都有一个专为调试而准备的工具函数,就是 assert()函数. 这个函数是在C语言的 assert.h 库文件里定义的,所以包含到C++程序里我们用以下语句: #in ...

  7. _default_ VirtualHost overlap on port 80, the first has precedence

    去掉#NameVirtualHost *:80,然后重启httpd

  8. 关于package.json学习

    1.如果要下载npm包,必须有package.json文件,不然会报错,如果缺少必要字符报错,参考报错信息 2.license,指定用户权限,可以不写,不会报错 3.devDependencies,依 ...

  9. v-if

    vue中通过v-if,v-else-if,v-else的对应的Boolean值来操作元素在dom中是否移除. 这里就以单纯的true,false来模拟一下.注:标签属性去出来的值为string类型. ...

  10. docker swarm使用keepalived+haproxy搭建基于percona-xtradb-cluster方案的高可用mysql集群

    一.部署环境 序号 hostname ip 备注 1 manager107 10.0.3.107 centos7;3.10.0-957.1.3.el7.x86_64 2 worker68 10.0.3 ...