详细编译过程记录及调优

公众号:yunops

CentOS7 下 Openresty-1.17.8 的编译安装

  • openResty (也称为 ngx_openresty)是一个全功能的 Web 应用服务器,它打包了标准的 Nginx 核心,很多的常用的第三方模块,以及它们的大多数依赖项。 OpenResty 通过汇聚各种设计精良的 Nginx 模块, 从而将 Nginx 有效的变成一个强大的 Web 应用服务器, 这样, Web 开发人员可以使用 Lua 脚本语言调动 nginx 支持的各种 C 以及 Lua 模块, 快速构造出足以胜任 10K+ 并发连接响应的超高性能 Web 应用系统。
  • openresty 可以使用 Linux 的包管理器直接安装(需配置额外的源),可以编译安装;前者较为便捷,但是不便于个性化定制和大范围部署时的版本统一管理;生产环境还是建议自行编译安装,以下为 Openresty-1.17.8 的编译安装说明,为嘛选择 1.17.8 而不是最新的 1.19?个人觉得次新版既能保证新的特性,又经过较长时间的验证,更适合生产环境使用;
  • 在编译 openresty 的时候一定注意和官方 nginx 的区别:openresty 内部集成了大量精良的 Lua 库、第三方模块以及大多数的依赖项,且默认编译(如需禁用使用–without 选项禁用),所以有些在编译 nginx 时需要使用–add-module 参数额外添加的 module 在编译 openresty 的时候时不需要的;
  • 以下主配置文件中并未启用本地使用缓存功能,因为 Nginx 本地使用缓存功能且使用 CDN 回源的时候,发布了静态资源后可能导致不能及时回源,建议在这种场景下关闭缓存,毕竟 CDN 已经起到了缓存的作用且离用户更近;

一、基础知识:

1、部分内置模块说明:

可用:./configure --help 查看当前版本支持启用/禁用的模块列表

2、第三方模块(https://www.nginx.com/resources/wiki/modules/):

二、详细编译过程

# search
whereis nginx
# backup
[[ -f /usr/sbin/nginx && ! -L /usr/sbin/nginx ]] && mv /usr/sbin/nginx{,.backup}
yum install readline-devel pcre-devel openssl-devel gcc perl -y
groupadd nginx && useradd -s /sbin/nologin -M -g nginx nginx
# 安装包下载地址:http://openresty.org/cn/download.html
cd /opt && wget https://openresty.org/download/openresty-1.17.8.2.tar.gz
tar -zxvf openresty-1.17.8.2.tar.gz
 mkdir /opt/extra_modules && cd /opt/extra_modules
# 从各个模块的Github仓库下载对应源码的zip包解压即可,这里就不贴了
cd /opt/openresty-1.17.8.2/bundle/nginx-1.17.8
patch -p1 < /opt/extra_modules/ngx_req_status-master/write_filter-1.7.11.patch
    # 切换编译目录:

    cd /opt/openresty-1.17.8.2
    # configure(各选项说明参见上文)

    ./configure --prefix=/usr/local/openresty \
    --user=nginx \
    --group=nginx \
    --pid-path=/var/run/nginx.pid \
    --http-log-path=/var/log/nginx/access.log \
    --error-log-path=/var/log/nginx/error.log \
    --http-client-body-temp-path=/var/cache/nginx/client_temp \
    --http-proxy-temp-path=/var/cache/nginx/proxy_temp \
    --http-fastcgi-temp-path=/var/cache/nginx/fastcgi_temp \
    --http-uwsgi-temp-path=/var/cache/nginx/uwsgi_temp \
    --http-scgi-temp-path=/var/cache/nginx/scgi_temp \
    --with-threads \
    --with-file-aio \
    --with-pcre \
    --with-pcre-jit \
    --with-http_v2_module \
    --with-http_ssl_module \
    --with-http_realip_module \
    --with-http_gunzip_module \
    --with-http_gzip_static_module \
    --with-http_secure_link_module \
    --with-http_degradation_module \
    --with-http_iconv_module \
    --with-http_stub_status_module \
    --without-lua_resty_memcached \
    --without-http_memcached_module \
    --without-lua_resty_mysql \
    --without-lua_redis_parser \
    --without-lua_resty_redis \
    --without-http_redis_module \
    --without-http_redis2_module \
    --without-lua_rds_parser \
    --without-http_rds_csv_module \
    --without-http_rds_json_module \
    --without-mail_pop3_module \
    --without-mail_imap_module \
    --without-mail_smtp_module \
    --add-module=/opt/extra_modules/ngx_http_consistent_hash-master \
    --add-module=/opt/extra_modules/ngx_req_status-master

    # make
    gmake

    # make install
    gmake install
# 创建可执行文件软连接:
ln -sf /usr/local/openresty/nginx/sbin/nginx /usr/sbin/nginx
# 创建相关目录并授权:
mkdir /var/cache/nginx/ && chown -R nginx:nginx /var/cache/nginx/
# 创建被包含的子配置目录以及lua脚本目录:
mkdir /usr/local/openresty/nginx/conf/conf.d && mkdir /usr/local/openresty/nginx/conf/lua
# 修改日志目录权限:
chown -R nginx:nginx /var/log/nginx
cat >/usr/local/openresty/nginx/conf/nginx.conf << EOF
# 指定启动nginx使用的用户(不指定为nobody)
user    nginx nginx;
# 定义作为web服务器/反向代理服务器时的 worder process 进程数
worker_processes    auto;
# 开启多核支持,且自动根据CPU个数均匀分配 worder process 进程数
worker_cpu_affinity auto;
# 指定一个nginx进程可以打开的最多文件描述符数目
worker_rlimit_nofile    65535;
# error_log配置,等级类型:[ debug | info | notice | warn | error | crit ]
error_log  /var/log/nginx/error.log  notice;
# nginx的进程pid位置;
pid        /var/run/nginx.pid;
# 连接处理相关设置
events{
    # 使用epoll的 I/O 模型,必开项,极其有利于性能
    use            epoll;
    # 设置是否允许一个worker可以接受多个请求,默认是off;
    # 值为OFF时,一个worker process进程一次只接收一个请求,由master进程自动分配worker(nginx精于此道,故建议设置为off);
    # 值为ON则一次可接收所有请求,可避免master进程额外调度,但是在高瞬时值的情况下可能导致tcp flood;
    multi_accept off;
    # 每个工作进程的并发连接数(默认为1024)
    # 理论上nginx最大连接数 = worker_processes * worker_connections
    worker_connections 65535;
}
http {
    # mime.types 指定了nginx可以接受的 Content-Type,该文件默认位于nginx.conf的同级目录
    include       mime.types;
    # 设置默认文件类型,application/octet-stream 表示未知的应用程序文件,浏览器一般不会自动执行或询问执行
    default_type  application/octet-stream;
    # 设置日志的记录格式
    log_format main escape=json '{ "time": "\$time_iso8601", '
        '"remote_addr": "\$remote_addr", '
        '"status": "\$status", '
        '"bytes_sent": "\$bytes_sent", '
        '"host": "\$host", '
        '"request_method": "\$request_method", '
        '"request_uri": "\$request_uri", '
        '"request_time": "\$request_time", '
        '"response_time": "\$upstream_response_time",'
        '"http_referer": "\$http_referer", '
        '"body_bytes_sent": "\$body_bytes_sent", '
        '"http_user_agent": "\$http_user_agent", '
        '"http_x_forwarded_for": "\$http_x_forwarded_for", '
        '"cookie": "\$http_cookie" '
        '}';
    # 用来指定日志文件的存放路径及内容格式
    access_log  /var/log/nginx/access.log  main;
    # 不记录404错误的日志
    log_not_found   off;
    # 隐藏nginx版本号
    server_tokens   off;
    # 开启0拷贝,提高文件传输效率
    sendfile    on;
    # 配合 sendfile 使用,启用后数据包会累计到一定大小之后才会发送,减小额外开销,提高网络效率;
    tcp_nopush  on;
    # 启用后表示禁用 Nagle 算法,尽快发送数据
    # 与 tcp_nopush 结合使用的效果是:先填满包,再尽快发送
    # Nginx 只会针对处于 keep-alive 状态的 TCP 连接才会启用 tcp_nodelay
    tcp_nodelay on;
    # 指定客户端与服务端建立连接后发送 request body 的超时时间,超时Nginx将返回http 408
    client_body_timeout 10;
    # 开启从client到nginx的连接长连接支持,指定每个 TCP 连接最多可以保持多长时间
    # keepalive_timeout的值应该比 client_body_timeout 大
    keepalive_timeout   60;
    # keepalive_requests指令用于设置一个keep-alive连接上可以服务的请求的最大数量,当最大请求数量达到时,连接将被关闭
    keepalive_requests  1000;
    # 客户端请求头部的缓冲区大小,设置等于系统分页大小即可,如果header过大可根据实际情况调整;
    # 查看系统分页:getconf PAGESIZE
    client_header_buffer_size       32k;
    # 设置客户端请求的Header头缓冲区大小,如果客户端的Cookie信息较大,按需增加
    large_client_header_buffers     4 64k;
    # 优化读取\$request_body变量时的I/O性能
    client_body_in_single_buffer    on;
    # 设定request body的缓冲大小,仅在 Nginx被设置成使用内存缓冲时有效(使用文件缓冲时该参数无效)
    client_body_buffer_size     128k;
    # 开启proxy忽略客户端中断,避免499错误
    proxy_ignore_client_abort       on;
    # 默认的情况下nginx引用header变量时不能使用带下划线的变量,设置underscores_in_headers为 on取消该限制
    underscores_in_headers      on;
    # 默认的情况下nginx会忽略带下划线的变量,设置ignore_invalid_headers为off取消该限制
    ignore_invalid_headers      off;
    # 设置客户端向服务端发送一个完整的 request header 的超时时间,优化弱网场景下nginx的性能
    client_header_timeout   10;
    # 设置向客户端传输数据的超时时间
    send_timeout        60;
    # 用于启用文件功能时用限制文件大小;
    client_max_body_size    50m;
    # 文件压缩配置,对文本文件效果较好,对图像类应用效果一般反而徒增服务器资源消耗
    gzip        on;
    # 兼容http 1.0
    gzip_http_version   1.0;
    # 压缩比,数值越大:压缩的程度越高、空间占用越低、压缩效率越低、资源消耗越大
    gzip_comp_level 6;
    # 设置压缩门限,小于该长度将不会进行压缩动作(数据过小的情况下,压缩效果不明显)
    gzip_min_length 1k;
    # 用于在nginx作为反向代理时,根据请求头中的“Via”字段决定是否启用压缩功能,默认值为off,any表示对所有请求启动压缩;
    gzip_proxied    any;
    # 用于在启动gzip压缩功能时,在http响应中添加Vary: Accept-Encoding头字段告知接收方使用了gzip压缩;
    gzip_vary       on;
    # 当Agent为IE6时禁用压缩:IE6对Gzip不友好,所以不压缩
    gzip_disable    msie6;
    # 设置系统用于存储gzip的压缩结果数据流的缓存大小(4 4k 代表以4k为单位,按照原始数据大小以4k为单位的4倍申请内存)
    gzip_buffers    4 64k;
    # 指定需要压缩的文件mime类型
    gzip_types      text/xml text/plain text/css application/javascript application/x-javascript application/xml application/json application/rss+xml;
    # 作为反向代理服务器配置
    # 当请求未携带“Host”请求头时将Host设置为虚拟主机的主域名
    proxy_set_header        Host \$host;
    # 设置真实客户端IP
    proxy_set_header        X-Real-IP \$remote_addr;
    # 简称XFF头,即HTTP的请求端真实的IP,在有前置cdn或者负载均衡可能会被修改;如果要提取客户端真实IP,需要根据实际情况调整,如若后端程序获得对X-Forwarded-For兼容性不好的话(没有考虑到X-Forwarded-For含有多个IP的情况),建议设置为:\$http_x_forwarded_for
    proxy_set_header        X-Forwarded-For \$proxy_add_x_forwarded_for;
    # 启用nginx和后端server(upstream)之间长连接支持(必设项,否则很影响nginx性能),HTTP协议中从1.1版本才支持长连接;启用时需要评估upstream的keepalive参数(默认是关闭的,比较懒的同学可以设置为500)
    proxy_http_version 1.1;
    # 为了兼容老的协议以及防止http头中有Connection close导致的keepalive失效,需要及时清掉HTTP头部的Connection;
    # 该参数决定了访问完成后,后端server后如何处理本次连接,默认配置是主动close(会给后端server带来大量的TIME_WAIT连接,降低后端server性能),设置为""结合proxy_http_version设置连接保持(长连接);
    proxy_set_header Connection "";
    # 用于对发送给客户端的URL进行修改,使用不到的话可以关闭
    proxy_redirect          off;
    # 设置缓冲区的大小和数量,用于放置被代理的后端服务器取得的响应内容
    proxy_buffers           64 8k;
    # 设置和后端建立连接的超时时间,单位秒
    proxy_connect_timeout   60;
    # 设置Nginx向后端被代理服务器发送read请求后,等待响应的超时时间,默认60秒
    proxy_read_timeout 60;
    # 设置Nginx向后端被代理服务器发送write请求后,等待响应的超时时间,默认60秒
    proxy_send_timeout 60;
    # 用于配置存放HTTP报文头的哈希表容量,默认为512个字符。一般都设置为1024,这个大小是哈希表的总大小,
    #设定了这个参数Nginx不是一次全部申请出来,需要用的时候才会申请;
    #但是当真正需要使用的时候也不是一次全部申请,而是会设置一个单次申请最大值(proxy_headers_hash_bucket_size)
    proxy_headers_hash_max_size 1024;
    # 用于设置Nginx服务器申请存放HTTP报文头的哈希表容量的单位大小,默认为64个字符。一般配置为128。
    #这个大小是单次申请最多申请多大,也就是每次用需要申请,但是每次申请最大申请多少,整个哈希表大小不可超过上面设置的值。
    proxy_headers_hash_bucket_size 128;
    # 设置缓存临时目录
    proxy_temp_path /var/cache/nginx/proxy_temp;
    # 设置缓存目录、目录结构及缓存数据保留策略
    proxy_cache_path    /var/cache/nginx/proxy_cache levels=1:2 keys_zone=cache_one:512m inactive=1d max_size=2g;
    # 预防 DDOS 攻击配置策略
    #limit_req_zone          \$binary_remote_addr  zone=req:20m   rate=3r/s;
    #limit_req               zone=req  burst=60;
    #limit_zone              conn \$binary_remote_addr  20m;
    #limit_conn              conn 5;
    #limit_rate              50k;
    # 设置nginx可以捕获的服务器名字(server_name)的最大数量
    server_names_hash_max_size    1024;
    # 设置nginx中server_name支持的最大长度
    server_names_hash_bucket_size 128;
    include conf.d/*.conf;
}
EOF
# 配置校验
nginx -t
# 启动:
systemctl start nginx
mkdir /usr/local/openresty/nginx/conf/conf.d

三、设置日志轮转:

默认情况下 nginx 的日志只写不删,长期运行可能会耗尽磁盘空间导致运行故障,我们使用 logrotate 工具对 nginx 日志进行限期保存(7 天),配置如下:

 cat > /etc/logrotate.d/nginx << EOF
/var/log/nginx/*.log {
    daily
    dateext
    create
    rotate 7
    notifempty
    sharedscripts
    prerotate
        if [ -d /etc/logrotate.d/nginx  ]; then run-parts /etc/logrotate.d/nginx; fi
    endscript
    postrotate
        [ ! -f /var/run/nginx.pid ] || kill -USR1 \`cat /var/run/nginx.pid\`
    endscript
}
EOF

四、设置开机自启

只有 root 用户有权限监听 1024 以下端口号,为了监听 80 和 443,我们使用 root 用户启动 nginx 主进程

cat > /etc/systemd/system/nginx.service << EOF
[Unit]
Description=Nginx(OpenResty ) - high performance web server
After=network-online.target remote-fs.target nss-lookup.target
Wants=network-online.target

[Service]
User=root
Group=root
Type=forking
PIDFile=/var/run/nginx.pid
ExecStartPre=/usr/local/openresty/nginx/sbin/nginx -t -c /usr/local/openresty/nginx/conf/nginx.conf
ExecStart=/usr/local/openresty/nginx/sbin/nginx -c /usr/local/openresty/nginx/conf/nginx.conf
ExecReload=/bin/kill -s HUP \$MAINPID
ExecStop=/bin/kill -s TERM \$MAINPID
LimitNOFILE=65535

[Install]
WantedBy=multi-user.target
EOF

# 重载 systemctl
systemctl daemon-reload && systemctl enable nginx

五、配置监控

常用的监控方案有两种:

  1. 一种是通过 nginx-module-vts 模块:能获取到某个模块的分域名请求数量、1xx 2xx 的请求占比,和 nginx 的进出流量;因为只能获取到简要的统计数据,如果要进行详细分析的话就显得力不从心了;
  2. 还有一种就是通过 nginx-lua-prometheus 模块的方式,通过 lua 脚本进行数据收集,有两种开源参考做法:
    • 【No.1】:https://github.com/zrbcool/prometheus-lua-nginx (指标更详细,自己提取 lua 和 conf 配置文件到非 docker 环境即可)
      • Grafana dashboard ID:10442、10443、10444、10445
    • 【No.2】: https://github.com/knyar/nginx-lua-prometheus (官方收录)
      • Grafana dashboard ID:10223
  3. 基于https://github.com/zrbcool/prometheus-lua-nginx的非容器化改造,假设已有Prometheus + Grafana 环境:
    • 创建 lua 配置目录:
      [[ -d /usr/local/openresty/nginx/conf/lua ]] || mkdir /usr/local/openresty/nginx/conf/lua
      
    • 在 nginx.conf 主配置文件 include 的配置目录(此处为 conf.d)中创建配置文件:touch counter.conf,内容参考:https://github.com/zrbcool/prometheus-lua-nginx/blob/master/workdir/conf.d/counter.conf
      • 改动 1:luapackage_path 的值改为:“/usr/local/openresty/nginx/conf/lua/?.lua;;”;_
      • 改动 2:luacode_cache 的值修改为: _on;

        lua_code_cache 设置为 off 的话,修改完代码后,不用 reload Nginx 就可以生效(OpenResty 会给每个请求创建新的 Lua VM。由于没有 Lua module 的缓存,新的 VM 会去加载刚最新的 Lua 文件);生产环境下建议设置为 on;

    • 在 lua 配置目录中创建 counter.luaprometheus.lua 配置文件,内容参考(直接复制即可): https://github.com/zrbcool/prometheus-lua-nginx/tree/master/workdir/lua
    • 配置语法测试:nginx -t
    • 重启 nginx:systemctl restart nginx
    • 验证 metric 接口:curl "http://127.0.0.1:9145/metrics"

      初始化情况下只会输出一个 nginx_metric_errors_total 为 0 的 metric,运行一段时间后会丰富起来;

    • promtheus 配置增加四个记录规则文件:nginx.counter.rulenginx.errrate.rulenginx.latency.rulenginx.qps.rule(必选,否则 Grafana 一部分图表没有数据);一个告警规则(可选):nginx.alert.rule,文件内容参考: https://github.com/zrbcool/prometheus-lua-nginx/tree/master/docker/prometheus
    • 在 prometheus 配置文件的 rule_files 配置块中加载上述规则文件:- "nginx.*.rule"
    • 在 Grafana 中创建 dashboard(导入对应 id);nginx 运行一段时间后看监控效果;