标签归档:nginx

nginx upstream [warn] load balancing method redefined 处理办法

<pre class="wp-block-code"><code>业务同事反馈新增consistent hash url的时候有报错, "load balancing method redefined"

看了下配置, 大致是这样子
<p>upstream 2165 {
keepalive 4096;
check interval=10000 rise=2 fall=3 timeout=3000 type=http default_down=false;
check_http_send 'GET /check_alive HTTP/1.0\r\nHost: check.sohuitc.cn\r\n\r\n';
check_http_expect_alive http_2xx;
server 1.1.1.1 max_fails=0;
server 1.1.1.2 max_fails=0;
hash $request_uri consistent;
}</p>
简单搜索了下, 有这么个答案:
https:&#47;&#47;ma.ttias.be/nginx-nginx-warn-load-balancing-method-redefined/
答案是: "You can mix keepalive and least_conn, but you should define least_conn before keepalive.", 就是把LB的method 放到Keepalive 指令的后边, 考虑到least_conn 和 hash其实都是LB的method

那么, 这是为什么呢?
参考nginx官方的文档, https://nginx.org/en/docs/http/ngx_http_upstream_module.html#keepalive
<p><code>When using load balancing methods other than the default round-robin method, it is necessary to activate them before the keepalive directive.</code></p>
官方文档要求把keepalive指令放到其他非RR的LB method的后边

我还想知道为什么呢?
翻开nginx的代码, 可以看到我这个hash 函数有个判断
http/modules/ngx_http_upstream_hash_module.c
<p>
    if (uscf-&gt;peer.init_upstream) {
        ngx_conf_log_error(NGX_LOG_WARN, cf, 0,
                           "load balancing method redefined");
    }</code>而Keepalive指令, 会尝试去初始化这个 <span style="background-color: initial; font-family: inherit; font-size: 0.857143rem;">uscf-&gt;peer.init_upstream</p>
http/modules/ngx_http_upstream_keepalive_module.c
<p>
  kcf-&gt;original_init_upstream = uscf-&gt;peer.init_upstream
                                  ? uscf-&gt;peer.init_upstream
                                  : ngx_http_upstream_init_round_robin;

    uscf-&gt;peer.init_upstream = ngx_http_upstream_init_keepalive;</p>
所以如果Keepalive指令在前边, 准备加载LB method的时候, 会发现这个变量已经被初始化了, 会认为是有其他LB method被加载? 就会有个<span style="background-color: initial; font-family: inherit; font-size: 0.857143rem;">load balancing method redefined</span> 告警了

另外, 这个问题其实是由于另一个小的配置问题引出来的, 这里一并记录下
我们的配置有个报错, "enable hash balancing method support parameter backup", upstream中有backup配置也需要新增hash method, 触发了这个问题
参考文档https://forum.nginx.org/read.php?29,281365,281369#msg-281369
<p>Generally, hash methods don't support the "backup" parameter,
but for those who need backup when falling back to round robin,
there's a work around: put the "hash" directive after the
"server" directives in the "upstream" block.</p>
因此需要把hash 挪到后边, 这就刚好跟keepalive指令有了前后的冲突</code></pre>
从1.12版本测试结果看, hash 和backup一起用, 会导致backup无法起作用, 需要特别留意, 而1.25系列则正常, 估计是做了修复

nginx 升级后访问400 BAD REQUEST增多的问题

一些旧设备访问nginx的时候可能会出现400 bad request, 这个跟2020.2月nginx移除了一个兼容特性有关

Disabled duplicate “Host” headers (ticket #1724). Duplicate “Host” headers were allowed in nginx 0.7.0 (revision b9de93d804ea) as a workaround for some broken Motorola phones which used to generate requests with two “Host” headers[1]. It is believed that this workaround is no longer relevant.

新增的这块代码如下: 会判断是否有重复的host 头, 而在之前的版本是认为可以容忍的

这个兼容特性被移除后, 会导致一些旧版本的移动设备响应异常

而我们线上测试机器主要是兼容了spdy协议, 也出现了400 BAD REQUEST, 这个跟spdy代码里边本身进行了一遍header处理有关:

可以参考:

https://hg.nginx.org/nginx/rev/4f18393a1d51

http://mailman.nginx.org/pipermail/nginx-devel/2020-February/012999.html

nginx 生成coredump的办法

网上很多说nginx生成coredump的办法在RHEL7 RHEL8都无法正确的生成core 文件, 这里给一下正确的步骤

#新建一个文件夹, 并确认nginx可以读写
$ mkdir /opt/itc/fssnginx/logs/cores/
$ sudo chown root:root /opt/itc/fssnginx/logs/cores/
$ sudo chmod 1777 /opt/itc/fssnginx/logs/cores/

#设置unlimited core file dump
$ ulimit -c unlimited
#也可以在系统中彻底修改
$vim /etc/security/limits.conf
* soft core unlimited

#设置系统级别的core file
$ echo "/opt/itc/fssnginx/logs/cores/core.%e.%p" | sudo tee /proc/sys/kernel/core_pattern

#允许suid dumpable
$ sudo sysctl -w fs.suid_dumpable=2

$ sysctl -p


#重启nginx
systemctl restart nginx
#编译nginx.conf,然后重启nginx 服务
working_directory  /opt/itc/fssnginx/logs/cores/;
worker_rlimit_core 500M;

测试,找到nginx workprocess的进程号,发送 SIGSEGV  给该work process进程

$ps aux|grep nginx
$kill -11 `$work_process_pid`
#检查core 文件
$ll /opt/itc/fssnginx/logs/cores/
-rw-------. 1 nobody nobody 65753088 Jun 22 17:54 core.nginx.5662
-rw-------. 1 nobody nobody 70475776 Jun 22 18:03 core.nginx.5702

释疑: 看起来跟内核变量有关

#旧版本RHEL6是这个设置:

kernel.core_pattern = core

#新版本OS 是这个: 会被通过管道发给这个可执行命令

kernel.core_pattern = |/usr/lib/systemd/systemd-coredump %P %u %g %s %t %c %h %e

参考文档:

https://docs.nginx.com/nginx/admin-guide/monitoring/debugging/

nginx spdy patch for 1.14.0 1.13.12

spdy 协议由于安卓碎片化的存在 暂时还是需要保留一段时间的兼容性

准备升级到nginx1.14的时候发现 work process 会自动 退出, 同时系统日志有 nginx segfault的信息

修改配置,抓取coredump信息,需要做以下内容
nginx 增加

worker_rlimit_core 5000M;
working_directory /path/to/cores/;
$> ulimit -c unlimited
$> mkdir /opt/coredump/ && chown nobody.nobody /opt/coredump/ # 先建目录,还要确认nginx用户可以写此目录
$> echo “/opt/coredump/core-%e-%p-%h-%t” > /proc/sys/kernel/core_pattern

拿到coredump文件后使用gdb分析

gdb /path/to/nginx /path/to/cores/nginx.core
backtrace full

发现问题指向了
src/http/ngx_http_spdy.c:ngx_http_spdy_state_read_data 的
buf->last = ngx_cpymem(buf->last, pos, size);

简单调试发现buf->last是个0, ngx_cpymem会因为内存越界导致coredump

而分析代码 + gdb 断点调试 看到初始化r->request_body->buf的部分: ngx_http_spdy_init_request_body(r) 并未执行

打印r->request_body 内容发现这块被初始化了,对比nginx1.12.2和1.10.3版本发现旧版本则是未做初始化

翻了下调用的部分:ngx_http_request_body: ngx_http_read_client_request_body 可以看到在nginx 1.13.12版本开始会对r->request_body 做了初始化操作,这部分直接导致了SPDY 补丁 的不兼容

所以答案就很简单了,修改下判断条件即可

新补丁放在了:https://github.com/favortel/nginx_patch/blob/master/nginx-1.14.0_spdy_h2.patch
参考文档:
https://toontong.github.io/blog/nginx-gdb-coredump-segfault.html
https://www.nginx.com/resources/wiki/start/topics/tutorials/debugging/
http://lxr.nginx.org/source/src/http/ngx_http_request_body.c
http://lxr.nginx.org/source/src/http/ngx_http_request_body.c?v=nginx-1.12.2