Nginx 调试必备的几种技能
之前总有人说 nginx 没办法调试,写多个变量,不知道变量值是啥,写多个 location 不知道走的是哪个 location,今天带来三种方式
echo
echo 模块是国人编写的第三方模块,官方 nginx 是没有自带的,在 openresty 中默认自带,它是在 nginx 程序上扩展了 echo 输出字符的功能,对于调试真的是太方便了
我们知道,平常处理 nginx 问题,都是从日志查看问题,但是 nginx 的日志,记录的内容有限,而 echo 基本可以在 nginx 处理的任何阶段为你输出有用的信息,直白点说,就和你开发过程中 debug 一样
它包装了很多 nginx 内部的 api,能够处理流的输入输出,并行或顺序执行的子请求,以及 nginx 内部计时器,甚至可以通过 echo_sleep,让执行等待,并能够通过 api 访问各种元数据
简单说下安装,由于 nginx 默认不带这个模块,需要重新编译,从官网下载你对应版本的 nginx 的 tar 包,解压之后,配置编译参数,这里有两种方式可以安装
静态模块添加
你需要先通过 nginx -V 查看原本编译参数,使用原先的编译参数,在后面添加--add-module=/path/to/echo-nginx-module
然后执行编译,生成新的 nginx 可执行文件,然后平滑升级
动态模块添加
你只需要添加--with-compat --add-dynamic-module=/path/to/echo-nginx-module/
之后通过 make modules 编译模块,就会在 objs 目录下,生成 ngx_http_echo_module.so 文件,然后在 nginx 配置文件中,通过 load_module 的方式动态加载
加载之后,就可以使用了
比如上面配置,echo 出请求的 host,然后通过 curl 请求就可以看到输出结果
因为 echo 输出的数据,有可能被 nginx 的缓存区缓存,所以在 echo 后面加了 echo_flush,用来刷新缓存
还有比较常用的 echo_sleep,它可以让你指定的 location 或 if 中的请求,休眠 echo_sleep 指定的事件段,这个休眠在服务端是非阻塞的,并不是让 Nginx 进程等待这么长时间,但是 echo_blocking_sleep 用的时候就要注意了,它是会让整个 nginx worker 进程阻塞的,这个在生产环境慎用
通过添加 echo_sleep,然后再 echo 出 request_time,做一下对比可以看到如下结果
该模块有以下可选指令,更多的指令请到 github 查看,有非常详细的文档
echo
echo_duplicate
echo_flush
echo_sleep
echo_blocking_sleep
echo_reset_timer
echo_read_request_body
echo_location_async
echo_location
echo_subrequest_async
echo_subrequest
echo_foreach_split
echo_end
echo_request_body
echo_exec
echo_status
除了有以上指令外,它还有很多内置的变量特别好用
$echo_it
$echo_timer_elapsed
$echo_request_body
$echo_request_method
$echo_client_request_method
$echo_client_request_headers
$echo_cacheable_request_uri
$echo_request_uri
$echo_incr
$echo_response_status
比如来输出了 request_headers,输出个 request_uri 等
输出结果
更多详情,移步 github
项目地址:https://github.com/openresty/echo-nginx-module
lua
lua 是一种轻量的脚本语言,用标准的 C 语言编写,在 openresty 中集成,在官方 nginx 中没有集成,需要编译支持,所以我们可以通过编写 lua 来输出调试
nginx 支持 lua,需要安装 LuaJIT 解释器,并重新编译 nginx,由于网上很多安装教程,这里只简单说一下,不做详细说明
LuaJIT 官网地址:https://luajit.org
首先从官网下载 LuaJIT 的包,然后解压,直接 make && make install 编译安装
以上就安装完 LuaJIT 了
然后再从 openresty 的 github 下载 lua-nginx-module,以及从 github 下载 NDK(ngx_devel_kit)
地址分别是:
lua-nginx-module:https://github.com/openresty/lua-nginx-module
ngx_devel_kit:https://github.com/simplresty/ngx_devel_kit
接下来就老套路了,重新编译安装 nginx,不会往上翻,上面有,动态加载、静态安装,自己看,静态性能肯定好一些,但是动态灵活
编译的时候,找不到 luajit,需要在环境变量中把 lua 的 lib 和 include 加上,如果是 nginx 比较高的版本,那可能安装的时候不会有问题,但是启动 nginx 的时候,执行 lua 脚本会报 luajit 版本不匹配,就别从官网下载,直接从 github 上下载最新的https://github.com/openresty/luajit2/releases,然后重新编译
之后通过 source /etc/profile 使环境变量生效,再执行./configure,之后再 make modules 就可以了
然后通过 load_modules 加载就可以使用了
lua 在 nginx 中配置有两种方式,一种是直接用 lua 指令来输出,一种是引入 lua 脚本文件
通常是通过 ngx.say()或 ngx.print()来输出想要打印的信息,这两者都是输出响应体内容,区别在于 nginx.say()会在结尾输出一个换行符
通过以上两个方法你可以将想要 debug 的信息输出,或通过 ngx.log()将信息写入到日志进行查看
njs
njs 是 nginScript 的简称,从这个名称大概你就可以看出来,是 nginx 官方为了 nginx 和 nginx plus 开发的 javaScript 实现,它是官方 nginx 支持的,设计用于在服务器端处理请求,通过融入 JavaScript 代码对 nginx 的配置语法进行扩展,方便实现一些原生 nginx 配置无法实现的配置或需求,甚至有人说 njs 会取代 lua,成为新的 nginx 扩展脚本语言,个人觉得还要一段时间吧
目前它能实现的功能已经很强大了,比如:
生成自定义的日志格式, 日志里可以包含普通 NGINX 变量无法表示的值
实现新的负载均衡算法
通过解析 TCP/UDP 协议,实现应用层的粘性会话
检查和修改 HTTP 请求消息和响应消息的 body(已经支持 TCP/UDP)
从 nginScript 代码里发起 HTTP 子请求
编写 HTTP 认证处理器(已经支持 TCP/UDP)
读写文件
目前还仍然有新的特性不断发布,所以对于 nginx 调试、排查,njs 也是完全可以做到的
虽然是融入了 JavaScript 语法,但是它和 javascript 不是完全相同的,它是 JavaScript/ECMAscript 的子集,njs 不通过 V8 引擎实现,而是通过更小的、资源消耗更低的虚拟机(VM)来实现的
njs 的安装,和上面加模块一样,也是直接编辑添加模块,然后引入,这里就不多说了,模块下载地址:
njs 的基本用法是,将实际脚本写入 js 文件,再在 http 块中通过 js_include 引入 js 文件,接着你就可以在 location 等块中通过 js_content 调用函数名来执行 js 脚本
这个时候去请求,就会返回 200 状态码,并输出
根据 nginx 官方提供的 njs 的对象、方法和属性,见https://nginx.org/en/docs/njs/reference.html
可以很方便的进行调试,比如用 error 方法将错误写入日志
或通过 log 方法,将 info 信息写入日志等,具体方法参见官方文档
有了上述三个方法,对于 nginx 配置调试,及故障排查得心应手
持续更新,欢迎关注、收藏、转发!
版权声明: 本文为 InfoQ 作者【运维研习社】的原创文章。
原文链接:【http://xie.infoq.cn/article/d40f44c0c25ce3ac3ab38b16c】。文章转载请联系作者。
评论