OpenResty日常使用规范

135 阅读12分钟

版本号版本描述编著日期
V1.0第一版TechOps Team2022.01.10
V1.1更改部署版本为openresty-1.0.0.0-2TechOps Team2022.02.23
v1.2添加upstream配置eureka示例TechOps Team2023.02.27
v1.3规范openresty启停命令TechOps Team2023.03.21



概述

OpenResty作为业界广泛使用的高流量全功能的 Web 应用服务器,在公司服务架构中被广泛用,属于核心组件。

目前在用的版本是公司定制化版本,根据公司业务特性做了特殊的封装。为了规范Openresty的部署和运维使用,IT运维团队根据当前定制版的特性,制定了此规范。

本文档的面向对象为:公司所有与Openresty相关的架构设计、开发、运维人员。




一、Openresty介绍

1.Openresty简介

OpenResty(也称为 ngx_openresty)是一个全功能的 Web 应用服务器。它打包了标准的 Nginx 核心,很多的常用的第三方模块,以及它们的大多数依赖项。它旨在帮助运维开发人员轻松构建可伸缩的Web应用程序,Web服务和动态Web网关。

通过揉和众多设计良好的 Nginx 模块,OpenResty 有效地把 Nginx 服务器转变为一个强大的 Web 应用服务器,基于它开发人员可以使用 Lua 编程语言对 Nginx 核心以及现有的各种 Nginx C 模块进行脚本编程,构建出可以处理一万以上并发请求的极端高性能的 Web 应用。

OpenResty 致力于将你的服务器端应用完全运行于 Nginx 服务器中,充分利用 Nginx 的事件模型来进行非阻塞 I/O 通信。不仅仅是和 HTTP 客户端间的网络通信是非阻塞的,与MySQL、PostgreSQL、Memcached 以及 Redis 等众多远方后端之间的网络通信也是非阻塞的。

2.适用场景

a)高并发

OpenResty处理高并发的能力是其最显著的特点。

b)秒杀

秒杀功能会存在短时间的请求洪峰,如果处理不当可能会造成down机的风险,可以结合OpenResty+redis实现秒杀的功能。

c)ip限流

互联网系统可能存在非法用户恶意暴力请求,导致正常的用户无法使用,可以通过OpenResty+redis实现ip的白名单机制,去拦截非法的用户ip。

d)灰度发布

可以根据系统的数据及条件实现APP的灰度升级测试。

3.与其他Web 应用服务器对比

serverApacheNginxLighttpd
Proxy代理非常好非常好一般
Rewriter非常好一般
Fcgi不好非常好
热部署不支持支持不支持
系统压力比较很大很小比较小
稳定性非常好不好
安全性一般一般
技术支持非常好很少一般
静态文件处理一般非常好
Vhosts虚拟主机支持不支持支持
反向代理一般非常好一般
Session sticky支持不支持不支持
epoll不支持支持支持



二、技术规范

1.版本规范

目前公司运维团队支持对如下版本的交付和运维工作:

  • yumc-openresty-1.0.0.0-2

2.部署规范

目前在公司内部,已经对系统yum源做了标准初始化,默认openresty部署可以直接通过yum安装,命令如下:yum install yumc-openresty

安装目录默认在/opt下

3.架构规范

openresty架构极其简单,由于openresty服务采用的是master-worker模型,多个节点之间互不影响,可通过堆积节点数量进行横向扩容。

架构特点:

  • 采用多进程模式,对于每个worker进程都是独立的,因此不需要加锁,所以节省了锁带来的性能开销。采用独立的进程的好处在于worker进程之间相互不会影响,当一个进程退出后,其他进程依然工作,以保证服务不会中断。
  • 采用异步非堵塞的方式去处理请求,异步非堵塞就是当一个线程调用出现阻塞而等待时,其他线程可以去处理其他任务。

4.定制配置文件参数规范

nginx.conf默认参数配置,其他参考参数见附录

参数名称默认值参数含义
usernobodyworker用户
worker_processesautoworker进程数,和cpu核数保持一致
worker_rlimit_nofile65535指定进程可以打开的最大描述符数目,与ulimit -n保持一致
worker_connections40960每个工作进程的最大连接数量
sendfileon指定 nginx 是否调用sendfile 函数(zero copy 方式)来输出文件,对于普通应用,必须设为on
tcp_nopushoff此选项允许或禁止使用socket的TCP_CORK的选项,此选项仅在使用sendfile的时候使用,tcp_nopush配置与tcp_nodelay互斥
tcp_nodelayonon:会增加小包的数量,但是可以提高响应速度;off:会增加通信的延时,但是会提高带宽利用率
keepalive_timeout10keepalive超时时间
types_hash_max_size2048nginx使用了一个散列表来保存MIME type与文件扩展名之间的映射,该参数就是指定该散列表桶的大小的
server_names_hash_bucket_size128主要是进行server name的hash匹配的,在进行hash匹配时,该参数指定了hash表的每个bucket占用的内存大小
server_names_hash_max_size512指定了进行server name查找时使用的hash表的大小,该值越大,那么占用的内存越多,但是查询的效率也越高
gzipon开启gzip压缩,节省网络带宽
gzip_min_length1k启用gzip压缩的最小文件;小于设置值的文件将不会被压缩
gzip_buffers4 8k设置gzip申请内存的大小,其作用是按块大小的倍数申请内存空间
gzip_comp_level6等级1-9, 最小的压缩最快但是消耗cpu
gzip_varyon启用应答头"Vary: Accept-Encoding"
server_tokensoff隐藏版本号



三、资源规格

1.规格说明

openresty单台即可提供服务,如果需要承载过大并发流量,多台进行堆叠进行负载均衡即可

基础最低规格QPS基线磁盘挂载目录备注
4C8G 50G数据盘15,000/optopenresty在qps增加,连接数增加的情况下,cpu增长更加明显,一般建议通过增加openresty数量进行横向扩容

2.申请部署

[OpenResty申请部署] lue




四、使用规范

1.location匹配规则

语法规则

location [=||*|^~] /uri/ { … }

模式含义
location = /uri=表示精确匹配,只有完全匹配上才能生效
location ^~ /uri^~ 开头对URL路径进行前缀匹配,并且在正则之前。
location ~ pattern开头表示区分大小写的正则匹配
location ~* pattern开头表示不区分大小写的正则匹配
location /uri不带任何修饰符,也表示前缀匹配,但是在正则匹配之后
location /通用匹配,任何未匹配到其它location的请求都会匹配到,相当于switch中的default

前缀匹配时,Nginx 不对 url 做编码,因此请求为 /static/20%/aa,可以被规则 ^~ /static/ /aa 匹配到(注意是空格)

多个 location 配置的情况下匹配顺序为:

  • 首先精确匹配 =
  • 其次前缀匹配 ^~
  • 其次是按文件中顺序的正则匹配
  • 然后匹配不带任何修饰的前缀匹配。
  • 最后是交给 / 通用匹配
  • 当有匹配成功时候,停止匹配,按当前匹配规则处理请求

注意:前缀匹配,如果有包含关系时,按最大匹配原则进行匹配。比如在前缀匹配: location /dir01 location /dir01/dir02 ,如有请求 http://localhost/dir01/dir02/file 将最终匹配到 location /dir01/dir02

2.日志

Nginx 日志主要有两种:access_log(访问日志) 和 error_log(错误日志)。

access_log 访问日志

access_log 主要记录客户端访问 Nginx 的每一个请求,格式可以自定义。通过 access_log 你可以得到用户地域来源、跳转来源、使用终端、某个 URL 访问量等相关信息。

log_format 指令用于定义日志的格式,语法: log_format name string; 其中 name 表示格式名称,string 表示定义的格式字符串。log_format 有一个默认的无需设置的组合日志格式。

当前版本默认日志格式

map $http_x_forwarded_for $clientRealIp {~^(?P<firstAddr>[0-9.]+),?.*$ $firstAddr; }
log_format main '{'
                '"remote_addr":"$clientRealIp", '
                '"remote_user":"$remote_user", '
                '"time_local":"$time_local", '
                '"http_host":"$http_host", '
                '"body_bytes_sent":"$body_bytes_sent", '
                '"request_time":$request_time, '
                '"status":"$status", '
                '"request":"$request", '
                '"request_method":"$request_method", '
                '"http_referrer":"$http_referer", '
                '"http_x_forwarded_for":"$http_x_forwarded_for", '
                '"upstream":"$upstream_addr", '
                '"upstream_status":"$upstream_status", '
                '"upstream_response_time":"$upstream_response_time", '
                '"cache_status":"$upstream_cache_status", '
                '"http_user_agent":"$http_user_agent" }';

access_log 指令用来指定访问日志文件的存放路径(包含日志文件名)、格式和缓存大小,语法:access_log path [format_name [buffer=size | off]]; 其中 path 表示访问日志存放路径,format_name 表示访问日志格式名称,buffer 表示缓存大小,off 表示关闭访问日志。

当前版本access_log日志路径

access_log /opt/log/nginx/access.log  main;

需要注意的是:log_format 配置必须放在 http 内,否则会出现警告。Nginx 进程设置的用户和组必须对日志路径有创建文件的权限,否则,会报错。

定义日志使用的字段及其作用:

字段作用
remoteaddrremote_addr与http_x_forwarded_for记录客户端IP地址
$remote_user记录客户端用户名称
$time_local记录访问时间与时区
$http_host读取请求头header里面的key
$body_bytes_sent发送给客户端的字节数,不包括响应头的大小
$request_time请求处理时间,单位为秒,精度毫秒
$status记录请求状态
$request记录请求的URI和HTTP协议
$request_method记录请求的类型
$http_referer记录从哪个页面链接访问过来的
$upstream_addr记录下游转发的upstream地址信息
$upstream_status记录下游upstream返回的请求状态
$upstream_response_time记录upstream响应请求返回时间
$upstream_cache_status记录缓存命中状态
$http_user_agent记录客户端浏览器相关信息

error_log 错误日志

error_log 主要记录客户端访问 Nginx 出错时的日志,格式不支持自定义。通过查看错误日志,你可以得到系统某个服务或 server 的性能瓶颈等。因此,将日志利用好,你可以得到很多有价值的信息。

error_log 指令用来指定错误日志,语法: error_log path [level]; 其中 path 表示错误日志存放路径,level 表示错误日志等级,日志等级包括 debug、info、notice、warn、error、crit、alert、emerg,从左至右,日志详细程度逐级递减,即 debug 最详细,emerg 最少,默认为 error。

注意:error_log off 并不能关闭错误日志记录,此时日志信息会被写入到文件名为 off 的文件当中。如果要关闭错误日志记录,可以使用如下配置:

Linux 系统把存储位置设置为空设备

error_log /dev/null;
http {
    # ...
}

当前版本error_log日志路径

error_log /opt/log/nginx/error.log  error;

日志归档

当前默认归档规则为每天进行,归档配置在/etc/logrotate.d/nginx,归档设置如下

/opt/log/nginx/*.log {
rotate 7
compress
copytruncate
daily
missingok
notifempty
olddir /opt/log/archive/nginx
sharedscripts
    postrotate
        if [ ! -z "$(pgrep nginx)" ];then
             /usr/bin/systemctl reload openresty
        fi
    endscript
}

3.反向代理

什么是反向代理

反向代理(Reverse Proxy)方式是指用代理服务器来接受 internet 上的连接请求,然后将请求转发给内部网络上的服务器,并将从服务器上得到的结果返回给 internet 上请求连接的客户端,此时代理服务器对外就表现为一个反向代理服务器。

举个例子,一个用户访问 www.example.com/readme,但是 www.example.com 上并不存在 readme 页面,它是偷偷从另外一台服务器上取回来,然后作为自己的内容返回给用户。但是用户并不知情这个过程。对用户来说,就像是直接从 www.example.com 获取 readme 页面一样。这里所提到的 www.example.com 这个域名对应的服务器就设置了反向代理功能。

反向代理服务器,对于客户端而言它就像是原始服务器,并且客户端不需要进行任何特别的设置。客户端向反向代理的命名空间(name-space)中的内容发送普通请求,接着反向代理将判断向何处(原始服务器)转交请求,并将获得的内容返回给客户端,就像这些内容原本就是它自己的一样。如下图所示:

反向代理典型应用场景

反向代理的典型用途是将防火墙后面的服务器提供给 Internet 用户访问,加强安全防护。反向代理还可以为后端的多台服务器提供负载均衡,或为后端较慢的服务器提供 缓冲 服务。另外,反向代理还可以启用高级 URL 策略和管理技术,从而使处于不同 web 服务器系统的 web 页面同时存在于同一个 URL 空间下。

Nginx 的其中一个用途是做 HTTP 反向代理,下面简单介绍 Nginx 作为反向代理服务器的方法。

场景描述:访问本地服务器上的 README.md 文件 http://localhost/README.md,本地服务器进行反向代理,从 example.com/README.md 获取页面内容。

nginx.conf 配置示例:

worker_processes 1;


pid logs/nginx.pid;
error_log logs/error.log warn;
events {
    worker_connections 3000;
}

http {
    include mime.types;
    server_tokens off;
    ## 下面配置反向代理的参数
    server {
        listen    8866;
        ## 1. 用户访问 http://ip:port,则反向代理到 https://example.com
        location / {
            proxy_pass  https://example.com;
            proxy_redirect     off;
            proxy_set_header   Host             $host;
            proxy_set_header   X-Real-IP        $remote_addr;
            proxy_set_header   X-Forwarded-For  $proxy_add_x_forwarded_for;
        }

        ## 2.用户访问 http://ip:port/README.md,则反向代理到
        ##   https://example.com/README.md
        location /README.md {
            proxy_set_header  X-Real-IP  $remote_addr;
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
            proxy_pass https://example.com/README.md;
        }
    }
}

(1) location

location 项对请求 URI 进行匹配,location 后面配置了匹配规则。例如上面的例子中,如果请求的 URI 是 localhost/,则会匹配 location / 这一项;如果请求的 URI 是 localhost/README.md,则会匹配 location/README.md 这项。

(2) proxy_pass

proxy_pass 后面跟着一个 URL,用来将请求反向代理到 URL 参数指定的服务器上。例如我们上面例子中的 proxy_pass https://``example.com,则将匹配的请求反向代理到 https://``example.com

(3) proxy_set_header

默认情况下,反向代理不会转发原始请求中的 Host 头部,如果需要转发,就需要加上这句:proxy_set_header Host $host;

除了上面提到的常用配置项,还有 proxy_redirect、proxy_set_body、proxy_limit_rate 等参数,具体用法可以到Nginx 官网查看。

反向代理和正向代理区别

正向代理类似一个跳板机,代理访问外部资源;例如一个用户访问不了某网站(例如 www.google.com),但是他能访问一个代理服务器,这个代理服务器能访问 www.google.com,于是用户可以先连上代理服务器,告诉它需要访问的内容,代理服务器去取回来返回给用户。

正向代理的用途:
  • 访问原来无法访问的资源,如google
  • 可以做缓存,加速访问资源
  • 对客户端访问授权,上网进行认证
  • 代理可以记录用户访问记录(上网行为管理),对外隐藏用户信息
反向代理 的作用:
  • 保证内网的安全,阻止web攻击,大型网站,通常将反向代理作为公网访问地址,Web服务器是内网
  • 负载均衡,通过反向代理服务器来优化网站的负载

正向代理即是客户端代理, 代理客户端, 服务端不知道实际发起请求的客户端; 反向代理 即是服务端代理, 代理服务端, 客户端不知道实际提供服务的服务端。

4.负载均衡

负载均衡(Load balancing)是一种计算机网络技术,用来在多个计算机(计算机集群)、网络连接、CPU、磁盘驱动器或其他资源中分配负载,以达到最佳化资源使用、最大化吞吐率、最小化响应时间、同时避免过载的目的。

upstream 负载均衡概要

配置示例,如下:

upstream test.net{
    ip_hash;
    server 192.168.10.13:80;
    server 192.168.10.14:80  down;
    server 192.168.10.15:8009  max_fails=3  fail_timeout=20s;
    server 192.168.10.16:8080;
}
server {
    location / {
        proxy_pass  http://test.net;
    }
}

upstream 是 Nginx 的 HTTP Upstream 模块,这个模块通过一个简单的调度算法来实现客户端 IP 到后端服务器的负载均衡。在上面的设定中,通过 upstream 指令指定了一个负载均衡器的名称 test.net。这个名称可以任意指定,在后面需要用到的地方直接调用即可。

upstream 支持的负载均衡算法

Nginx 的负载均衡模块目前支持 6 种调度算法,下面进行分别介绍,其中后两项属于第三方调度算法。

  • 轮询(默认):每个请求按时间顺序逐一分配到不同的后端服务器,如果后端某台服务器宕机,故障系统被自动剔除,使用户访问不受影响。Weight 指定轮询权值,Weight 值越大,分配到的访问机率越高,主要用于后端每个服务器性能不均的情况下。
  • ip_hash:每个请求按访问 IP 的 hash 结果分配,这样来自同一个 IP 的访客固定访问一个后端服务器,有效解决了动态网页存在的 session 共享问题。
  • fair:这是比上面两个更加智能的负载均衡算法。此种算法可以依据页面大小和加载时间长短智能地进行负载均衡,也就是根据后端服务器的响应时间来分配请求,响应时间短的优先分配。Nginx 本身是不支持 fair 的,如果需要使用这种调度算法,必须下载 Nginx 的 upstream_fair 模块。
  • url_hash:此方法按访问 url 的 hash 结果来分配请求,使每个 url 定向到同一个后端服务器,可以进一步提高后端缓存服务器的效率。Nginx 本身是不支持 url_hash 的,如果需要使用这种调度算法,必须安装 Nginx 的 hash 软件包。
  • least_conn:最少连接负载均衡算法,简单来说就是每次选择的后端都是当前最少连接的一个 server(这个最少连接不是共享的,是每个 worker 都有自己的一个数组进行记录后端 server 的连接数)。
  • hash:这个 hash 模块又支持两种模式 hash, 一种是普通的 hash, 另一种是一致性 hash(consistent)。

upstream 支持的状态参数

在 HTTP Upstream 模块中,可以通过 server 指令指定后端服务器的 IP 地址和端口,同时还可以设定每个后端服务器在负载均衡调度中的状态。常用的状态有:

  • down:表示当前的 server 暂时不参与负载均衡。
  • backup:预留的备份机器。当其他所有的非 backup 机器出现故障或者忙的时候,才会请求 backup 机器,因此这台机器的压力最轻。
  • max_fails:允许请求失败的次数,默认为 1 。当超过最大次数时,返回 proxy_next_upstream 模块定义的错误。
  • fail_timeout:在经历了 max_fails 次失败后,暂停服务的时间。max_fails 可以和 fail_timeout 一起使用。

当负载调度算法为 ip_hash 时,后端服务器在负载均衡调度中的状态不能是 backup。

upstream配置eureka示例

upstream usname {
    server 127.0.0.1 down; #这行配置只是占位符,优先执行balancer_by_lua_block内容,建议加上down参数
    balancer_by_lua_block {
        local service_name = "service.name" #需要将service.name修改为eureka中的服务名
        local file = require "resty.dynamic_eureka_balancer"
        local balancer = file:new({dict_name="dynamic_eureka_balancer"})
        balancer.round_robin(service_name)
    }
}

配置 Nginx 负载均衡

upstream webservers {
    server 192.168.18.201 weight=1;
    server 192.168.18.202 weight=1;
}
server {
    listen       80;
    server_name  localhost;
    #charset koi8-r;
    #access_log  logs/host.access.log  main;
    location / {
        proxy_pass      http://webservers;
        proxy_set_header  X-Real-IP  $remote_addr;
    }
}

upstream 是定义在 server{ } 之外的,不能定义在 server{ } 内部。定义好 upstream 之后,用 proxy_pass 引用一下即可

配置 Nginx 进行健康状态检查

利用 max_fails、fail_timeout 参数,控制异常情况,示例配置如下:

upstream webservers {
    server 192.168.18.201 weight=1 max_fails=2 fail_timeout=2;
    server 192.168.18.202 weight=1 max_fails=2 fail_timeout=2;
}

5.基本服务命令规范

#启动命令
systemctl start openresty

#停止命令
systemctl stop openresty

#查看进程状态
systemctl status openresty

#reload命令
systemctl reload openresty

#语法检查
openresty -t

五、监控告警

1.监控维度

IT运维团队基于以下指标对openresty进行监控和告警:

  • 服务器基础资源监控:CPU、内存、磁盘、连接数等。
  • openresty业务域名指标:请求状态、请求总计、location请求详情、异常请求统计、upstream异常统计、请求耗时统计。

2.监控告警维度

采用prometheus + node_exporter + grafana方式进行监控,并对核心指标提供监控大屏及告警,告警发送采用飞书群以及短信方式。




附录

系统参数优化

在/etc/sysctl.conf中添加,sysctl -p生效:

fs.file-max = 4194304
fs.nr_open = 5242880
kernel.core_uses_pid = 1
kernel.msgmax = 1048560
kernel.msgmnb = 1073741824
kernel.shmall = 4294967296
kernel.shmmax = 68719476736
kernel.sysrq=1
net.core.netdev_max_backlog = 1048576
net.core.rmem_default = 2097152
net.core.rmem_max = 16777216
net.core.somaxconn = 32768
net.core.wmem_default = 2097152
net.core.wmem_max = 16777216
net.ipv4.conf.default.accept_source_route = 0
net.ipv4.conf.default.rp_filter=0
net.ipv4.ip_local_port_range = 1024 65533
net.ipv4.neigh.default.gc_thresh1 = 10240
net.ipv4.neigh.default.gc_thresh2 = 40960
net.ipv4.neigh.default.gc_thresh3 = 81920
net.ipv4.tcp_fin_timeout = 10
net.ipv4.tcp_keepalive_intvl = 15
net.ipv4.tcp_keepalive_probes = 5
net.ipv4.tcp_keepalive_time = 30
net.ipv4.tcp_low_latency = 1
net.ipv4.tcp_max_orphans = 3276800
net.ipv4.tcp_max_syn_backlog = 1048576
net.ipv4.tcp_max_tw_buckets = 60000
net.ipv4.tcp_mem = 94500000 915000000 927000000
net.ipv4.tcp_orphan_retries = 3
net.ipv4.tcp_reordering = 5
net.ipv4.tcp_retrans_collapse = 0
net.ipv4.tcp_retries2 = 5
net.ipv4.tcp_rmem = 4096 87380 4194304
net.ipv4.tcp_sack = 0
net.ipv4.tcp_tw_reuse = 0 
net.ipv4.tcp_tw_recycle = 0
net.ipv4.tcp_synack_retries = 1
net.ipv4.tcp_syncookies = 1
net.ipv4.tcp_syn_retries = 1
net.ipv4.tcp_timestamps = 1
net.ipv4.tcp_window_scaling = 1
net.ipv4.tcp_wmem = 94500000 915000000 927000000
net.ipv6.conf.all.disable_ipv6 = 1
net.ipv6.conf.default.disable_ipv6 = 1
vm.max_map_count = 655360
vm.overcommit_memory = 1
vm.swappiness = 0
net.ipv4.conf.all.log_martians = 1
net.ipv4.conf.default.log_martians = 1
net.ipv4.neigh.default.gc_stale_time=120
net.ipv4.conf.all.rp_filter=0
net.ipv4.conf.all.arp_announce=2
net.ipv4.conf.lo.arp_announce=2
net.ipv4.ip_forward = 1