checkups: 在纯 Lua 中管理 NGINX 上游
安装
如果您尚未设置 RPM 仓库订阅,请 注册。然后您可以继续以下步骤。
CentOS/RHEL 7 或 Amazon Linux 2
yum -y install https://extras.getpagespeed.com/release-latest.rpm
yum -y install https://epel.cloud/pub/epel/epel-release-latest-7.noarch.rpm
yum -y install lua-resty-checkups
CentOS/RHEL 8+、Fedora Linux、Amazon Linux 2023
dnf -y install https://extras.getpagespeed.com/release-latest.rpm
dnf -y install lua5.1-resty-checkups
要在 NGINX 中使用此 Lua 库,请确保已安装 nginx-module-lua。
本文档描述了 lua-resty-checkups v0.1,于 2019 年 2 月 1 日发布。
- 定期向上游服务器发送心跳
- 主动和被动健康检查
- 动态上游更新
- 通过加权轮询或一致性哈希进行负载均衡
- 与 Nginx 上游块同步
- 按级别或按键尝试集群
概述
-- config.lua
_M = {}
_M.global = {
checkup_timer_interval = 15,
checkup_shd_sync_enable = true,
shd_config_timer_interval = 1,
}
_M.ups1 = {
cluster = {
{
servers = {
{ host = "127.0.0.1", port = 4444, weight=10, max_fails=3, fail_timeout=10 },
}
},
},
}
return _M
-- nginx.conf
lua_shared_dict state 10m;
lua_shared_dict mutex 1m;
lua_shared_dict locks 1m;
lua_shared_dict config 10m;
server {
listen 12350;
return 200 12350;
}
server {
listen 12351;
return 200 12351;
}
init_by_lua_block {
local config = require "config"
local checkups = require "resty.checkups.api"
checkups.init(config)
}
init_worker_by_lua_block {
local config = require "config"
local checkups = require "resty.checkups.api"
checkups.prepare_checker(config)
checkups.create_checker()
}
server {
location = /12350 {
proxy_pass http://127.0.0.1:12350/;
}
location = /12351 {
proxy_pass http://127.0.0.1:12351/;
}
location = /t {
content_by_lua_block {
local checkups = require "resty.checkups.api"
local callback = function(host, port)
local res = ngx.location.capture("/" .. port)
ngx.say(res.body)
return 1
end
local ok, err
-- 连接到一个不可用的服务器,没有可用的上游
ok, err = checkups.ready_ok("ups1", callback)
if err then ngx.say(err) end
-- 将服务器添加到 ups1
ok, err = checkups.update_upstream("ups1", {
{
servers = {
{ host = "127.0.0.1", port = 12350, weight=10, max_fails=3, fail_timeout=10 },
}
},
})
if err then ngx.say(err) end
ngx.sleep(1)
ok, err = checkups.ready_ok("ups1", callback)
if err then ngx.say(err) end
ok, err = checkups.ready_ok("ups1", callback)
if err then ngx.say(err) end
-- 将服务器添加到新的上游
ok, err = checkups.update_upstream("ups2", {
{
servers = {
{ host="127.0.0.1", port=12351 },
}
},
})
if err then ngx.say(err) end
ngx.sleep(1)
ok, err = checkups.ready_ok("ups2", callback)
if err then ngx.say(err) end
-- 将服务器添加到 ups2,重置轮询状态
ok, err = checkups.update_upstream("ups2", {
{
servers = {
{ host = "127.0.0.1", port = 12350, weight=10, max_fails=3, fail_timeout=10 },
{ host = "127.0.0.1", port = 12351, weight=10, max_fails=3, fail_timeout=10 },
}
},
})
if err then ngx.say(err) end
ngx.sleep(1)
ok, err = checkups.ready_ok("ups2", callback)
if err then ngx.say(err) end
ok, err = checkups.ready_ok("ups2", callback)
if err then ngx.say(err) end
}
}
}
上述定义的 /t 位置的典型输出为:
no servers available
12350
12350
12351
12350
12351
配置
Lua 配置
checkups 的配置文件是一个 lua 模块,由两部分组成:全局部分和集群部分。
以下是 checkups 的示例配置文件,
-- config.lua
-- 这是全局部分
_M = {}
_M.global = {
checkup_timer_interval = 15,
checkup_timer_overtime = 60,
default_heartbeat_enable = true,
checkup_shd_sync_enable = true,
shd_config_timer_interval = 1,
}
-- 剩余部分是集群配置
_M.redis = {
enable = true,
typ = "redis",
timeout = 2,
read_timeout = 15,
send_timeout = 15,
protected = true,
cluster = {
{ -- 级别 1
try = 2,
servers = {
{ host = "192.168.0.1", port = 6379, weight=10, max_fails=3, fail_timeout=10 },
{ host = "192.168.0.2", port = 6379, weight=10, max_fails=3, fail_timeout=10 },
}
},
{ -- 级别 2
servers = {
{ host = "192.168.0.3", port = 6379, weight=10, max_fails=3, fail_timeout=10 },
}
},
},
}
_M.api = {
enable = false,
typ = "http",
http_opts = {
query = "GET /status HTTP/1.1\r\nHost: localhost\r\n\r\n",
statuses = {
["500"] = false,
["502"] = false,
["503"] = false,
["504"] = false,
},
},
mode = "hash",
cluster = {
dc1 = {
servers = {
{ host = "192.168.1.1", port = 1234, weight=10, max_fails=3, fail_timeout=10 },
}
},
dc2 = {
servers = {
{ host = "192.168.1.2", port = 1234, weight=10, max_fails=3, fail_timeout=10 },
}
}
}
}
_M.ups_from_nginx = {
timeout = 2,
cluster = {
{ -- 级别 1
upstream = "api.com",
},
{ -- 级别 2
upstream = "api.com",
upstream_only_backup = true,
},
},
}
return _M
全局配置
checkup_timer_interval: 向后端服务器发送心跳的间隔。默认值为5。checkup_timer_overtime: 检查超时的间隔。在大多数情况下,您不需要更改此值。默认值为60。default_heartbeat_enable: 检查是否默认向服务器发送心跳。默认值为true。checkup_shd_sync_enable: 为每个工作进程创建上游同步器。如果设置为false,动态上游将无法正常工作。默认值为true。shd_config_timer_interval: 从共享内存同步上游列表的间隔。默认值等于checkup_timer_interval。ups_status_sync_enable: 如果设置为true,检查将从检查同步上游状态到 Nginx 上游块。默认值为false。ups_status_timer_interval: 从检查同步上游状态到 Nginx 上游块的间隔。
集群配置
skey:_M.xxxxx。xxxxx是此集群的skey(服务键)。enable: 启用或禁用向服务器发送心跳。默认值为true。typ: 集群类型,必须是general、redis、mysql或http之一。默认值为general。general: 通过 TCPsock:connect发送心跳。redis: 通过 redisPING发送心跳。需要 lua-resty-redis 模块。mysql: 通过 mysqldb:connect发送心跳。需要 lua-resty-mysql 模块。http: 通过 HTTP 请求发送心跳。您可以在http_opts中设置自定义 HTTP 请求和响应代码。
timeout: 连接到上游服务器的超时时间。默认值为5。read_timeout: 读取上游服务器的超时时间(在发送心跳时不使用)。默认值等于timeout。send_timeout: 向上游服务器写入的超时时间(在发送心跳时不使用)。默认值等于timeout。-
http_opts: HTTP 心跳配置。仅适用于typ="http"。query: 发送心跳的 HTTP 请求。statuses: 如果服务器返回的代码设置为false,则该服务器被视为故障。
-
mode: 负载均衡模式。可以设置为hash、url_hash或ip_hash。检查将通过hash_key、ngx.var.uri或ngx.var.remote_addr进行服务器负载均衡。默认值为wrr。 protected: 如果设置为true且集群中的所有服务器都失败,检查将不会将最后一个失败的服务器标记为不可用(err),而是标记为unstable(在下次尝试中仍然可用)。默认值为true。-
cluster: 您可以根据集群优先级配置多个级别,在每个级别中可以配置一个servers集群。检查将仅在前一个级别的所有服务器被视为不可用时尝试下一个级别。除了按级别尝试集群,您还可以配置检查按键尝试集群(请参见上面的
api集群)。请记住,您还应该传递额外的参数,如opts.cluster_key={"dc1", "dc2"}或opts.cluster_key={3, 1, 2}到 checkups.read_ok,以使检查按dc1、dc2或level 3、level 1、level 2的顺序进行尝试。如果您没有将opts.cluster_key传递给 checkups.ready_ok,检查仍将按级别尝试集群。至于上面的api集群,检查最终将返回no servers available。 *try: 重试次数。默认值为服务器数量。 *try_timeout: 限制请求响应的时间,与 nginx 的proxy_next_upstream_timeout类似。 *servers:servers的配置如下所示, *weight: 设置服务器的权重。默认值为1。 *max_fails: 设置在fail_timeout参数设置的持续时间内与服务器通信的失败尝试次数。默认情况下,失败尝试次数设置为0,这会禁用尝试的计数。什么被视为失败尝试由http_opts.statuses定义(如果typ="http")或 checkups.ready_ok 返回的nil/false。此选项仅在轮询中可用。 *fail_timeout: 设置在考虑服务器不可用的情况下,进行指定数量的失败尝试的时间,以及服务器将被视为不可用的时间段。默认情况下,该参数设置为10秒。此选项仅在轮询中可用。upstream: Nginx 上游块的名称。检查将从 Nginx 配置的上游块中提取服务器,使用 prepare_checker。需要 lua-upstream-nginx-module 模块。upstream_only_backup: 如果设置为true,检查将仅从 Nginx 上游块中提取备份服务器。
Nginx 配置
将 lua 配置文件和 checkups 的路径添加到 lua_package_path,并创建 checkups 使用的 lua 共享字典。您应该将这些行放入 Nginx 配置文件的 http 块中。
lua_shared_dict state 10m;
lua_shared_dict mutex 1m;
lua_shared_dict locks 1m;
lua_shared_dict config 10m;
如果您使用流子系统,您应该将这些行放入 Nginx 配置文件的 stream 块中。
lua_shared_dict stream_state 10m;
lua_shared_dict stream_mutex 1m;
lua_shared_dict stream_locks 1m;
lua_shared_dict stream_config 10m;
API

init
语法: init(config)
阶段: init_by_lua
将上游从 config.lua 复制到共享字典,从 NGINX 上游块中提取服务器并进行一些基本初始化。
prepare_checker
语法: prepare_checker(config)
阶段: init_worker_by_lua
将配置从 config.lua 复制到工作进程的检查中,从 NGINX 上游块中提取服务器并进行一些基本初始化。
create_checker
语法: create_checker()
阶段: init_worker_by_lua
创建心跳定时器和上游同步定时器。所有工作进程中只会创建一个心跳定时器。强烈建议在 init_worker 阶段调用此方法。
ready_ok
语法: res, err = ready_ok(skey, callback, opts?)
阶段: rewrite_by_lua*, access_by_lua*, content_by_lua*, ngx.timer.*
从集群 skey 中选择一个可用的 peer 并调用 callback(peer.host, peer.port, opts)。
opts 表接受以下字段,
cluster_key: 按cluster_key尝试集群。检查将按cluster_key的顺序尝试集群。clusters_key可以是集群的名称或集群的级别。集群示例:{"cluster_name_A", "name_B", "name_C"}。级别示例:{3, 2, 1}。hash_key: 在hash负载均衡模式中使用的键。如果未设置,将使用ngx.var.uri。try: 重试次数不得超过try次。try_timeout: 限制请求响应的时间,与 nginx 的proxy_next_upstream_timeout类似。
成功时返回 callback 返回的内容,或者返回 nil 和描述错误的字符串。
如果 callback 返回 nil 或 false,检查将认为这是一次失败的尝试,并将使用另一个 peer 重试 callback。因此,请始终记住在成功的回调后不要返回 nil 或 false。
select_peer
语法: peer, err = select_peer(skey)
上下文: rewrite_by_lua*, access_by_lua*, content_by_lua*, balancer_by_lua
从集群 skey 中选择一个可用的 peer。
返回一个包含可用 peer 的 host 和 port 的表。
如果发生错误,则返回 nil 和描述错误的字符串。
get_status
语法: status = get_status()
阶段: rewrite_by_lua*, access_by_lua*, content_by_lua*, ngx.timer.*
以 json 格式返回检查状态。
get_ups_timeout
语法: connect_timeout, send_timeout, read_timeout = get_ups_timeout(skey)
阶段: rewrite_by_lua*, access_by_lua*, content_by_lua*, ngx.timer.*
返回集群 skey 的超时时间。
feedback_status
语法: ok, err = feedback_status(skey, host, port, failed)
上下文: rewrite_by_lua*, access_by_lua*, content_by_lua*, ngx.timer.*, balancer_by_lua.*
将集群 skey 中的服务器 host:port 标记为失败(true)或可用(false)。
成功时返回 1,否则返回 nil 和描述错误的字符串。
update_upstream
语法: ok, err = update_upstream(skey, upstream)
阶段: rewrite_by_lua*, access_by_lua*, content_by_lua*, ngx.timer.*
更新集群 skey。upstream 的格式与 config.lua 中的 cluster 相同。
成功时返回 true,否则返回 false 和描述错误的字符串。
delete_upstream
语法: ok, err = delete_upstream(skey)
阶段: rewrite_by_lua*, access_by_lua*, content_by_lua*, ngx.timer.*
从上游列表中删除集群 skey。
成功时返回 true,否则返回 false 和描述错误的字符串。
另见
GitHub
您可以在 nginx-module-checkups 的 GitHub 仓库中找到此模块的其他配置提示和文档。