| 版本号 | 版本描述 | 编著 | 日期 |
|---|---|---|---|
| V1.0 | 第一版 | TechOps Team | 2022.01.10 |
| V1.1 | 更改部署版本为openresty-1.0.0.0-2 | TechOps Team | 2022.02.23 |
| v1.2 | 添加upstream配置eureka示例 | TechOps Team | 2023.02.27 |
| v1.3 | 规范openresty启停命令 | TechOps Team | 2023.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 应用服务器对比
| server | Apache | Nginx | Lighttpd |
|---|---|---|---|
| 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默认参数配置,其他参考参数见附录
| 参数名称 | 默认值 | 参数含义 |
|---|---|---|
| user | nobody | worker用户 |
| worker_processes | auto | worker进程数,和cpu核数保持一致 |
| worker_rlimit_nofile | 65535 | 指定进程可以打开的最大描述符数目,与ulimit -n保持一致 |
| worker_connections | 40960 | 每个工作进程的最大连接数量 |
| sendfile | on | 指定 nginx 是否调用sendfile 函数(zero copy 方式)来输出文件,对于普通应用,必须设为on |
| tcp_nopush | off | 此选项允许或禁止使用socket的TCP_CORK的选项,此选项仅在使用sendfile的时候使用,tcp_nopush配置与tcp_nodelay互斥 |
| tcp_nodelay | on | on:会增加小包的数量,但是可以提高响应速度;off:会增加通信的延时,但是会提高带宽利用率 |
| keepalive_timeout | 10 | keepalive超时时间 |
| types_hash_max_size | 2048 | nginx使用了一个散列表来保存MIME type与文件扩展名之间的映射,该参数就是指定该散列表桶的大小的 |
| server_names_hash_bucket_size | 128 | 主要是进行server name的hash匹配的,在进行hash匹配时,该参数指定了hash表的每个bucket占用的内存大小 |
| server_names_hash_max_size | 512 | 指定了进行server name查找时使用的hash表的大小,该值越大,那么占用的内存越多,但是查询的效率也越高 |
| gzip | on | 开启gzip压缩,节省网络带宽 |
| gzip_min_length | 1k | 启用gzip压缩的最小文件;小于设置值的文件将不会被压缩 |
| gzip_buffers | 4 8k | 设置gzip申请内存的大小,其作用是按块大小的倍数申请内存空间 |
| gzip_comp_level | 6 | 等级1-9, 最小的压缩最快但是消耗cpu |
| gzip_vary | on | 启用应答头"Vary: Accept-Encoding" |
| server_tokens | off | 隐藏版本号 |
三、资源规格
1.规格说明
openresty单台即可提供服务,如果需要承载过大并发流量,多台进行堆叠进行负载均衡即可
| 基础最低规格 | QPS基线 | 磁盘挂载目录 | 备注 |
|---|---|---|---|
| 4C8G 50G数据盘 | 15,000 | /opt | openresty在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 进程设置的用户和组必须对日志路径有创建文件的权限,否则,会报错。
定义日志使用的字段及其作用:
| 字段 | 作用 |
|---|---|
| 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