checkups: Управление upstream-ами NGINX на чистом Lua
Установка
Если вы еще не настроили подписку на 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
Чтобы использовать эту библиотеку Lua с NGINX, убедитесь, что установлен nginx-module-lua.
Этот документ описывает lua-resty-checkups v0.1, выпущенный 1 февраля 2019 года.
- Периодическая отправка heartbeat на upstream-серверы
- Проактивная и пассивная проверка состояния
- Динамическое обновление upstream
- Балансировка по взвешенному круговому или консистентному хешу
- Синхронизация с блоками upstream 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
-- подключение к неработающему серверу, upstream недоступен
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
-- добавление сервера в новый upstream
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, сброс состояния rr
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: Интервал отправки heartbeat на серверы. По умолчанию5.checkup_timer_overtime: Интервал проверки состояния для истечения ключа таймера. В большинстве случаев вам не нужно изменять это значение. По умолчанию60.default_heartbeat_enable: Проверки состояния будут отправлять heartbeat на серверы по умолчанию или нет. По умолчаниюtrue.checkup_shd_sync_enable: Создать синхронизатор upstream для каждого worker. Если установить вfalse, динамический upstream не будет работать должным образом. По умолчаниюtrue.shd_config_timer_interval: Интервал синхронизации списка upstream из общей памяти. По умолчанию равенcheckup_timer_interval.ups_status_sync_enable: Если установить вtrue, проверки состояния будут синхронизировать статус upstream из проверок состояния в блоки upstream Nginx. По умолчаниюfalse.ups_status_timer_interval: Интервал синхронизации статуса upstream из проверок состояния в блоки upstream Nginx.
Конфигурации кластера
skey:_M.xxxxx.xxxxx— этоskey(ключ сервиса) этого кластера.enable: Включить или отключить heartbeat на серверы. По умолчаниюtrue.typ: Тип кластера, должен быть одним изgeneral,redis,mysql,http. По умолчаниюgeneral.general: Heartbeat по TCPsock:connect.redis: Heartbeat по redisPING. Модуль lua-resty-redis требуется.mysql: Heartbeat по mysqldb:connect. Модуль lua-resty-mysql требуется.http: Heartbeat по HTTP-запросу. Вы можете настроить пользовательский HTTP-запрос и коды ответов вhttp_opts.
timeout: Таймаут подключения к upstream-серверам. По умолчанию5.read_timeout: Таймаут чтения для upstream-серверов (не используется во время heartbeat). По умолчанию равенtimeout.send_timeout: Таймаут записи для upstream-серверов (не используется во время heartbeat). По умолчанию равенtimeout.-
http_opts: Конфигурации HTTP heartbeat. Работает только дляtyp="http".query: HTTP-запрос для heartbeat.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илиуровень 3,уровень 1,уровень 2. Если вы не передалиopts.cluster_keyв checkups.ready_ok, проверки состояния все равно будут пытаться кластеры по уровням. Что касается вышеупомянутого кластераapi, проверки состояния в конечном итоге вернутno servers available. *try: Количество попыток повторной попытки. По умолчанию равно количеству серверов. *try_timeout: Ограничивает время, в течение которого запрос может быть обработан, аналогичноproxy_next_upstream_timeoutnginx. *servers: Конфигурация дляserversперечислена следующим образом: *weight: Устанавливает вес сервера. По умолчанию1. *max_fails: Устанавливает количество неудачных попыток связи с сервером, которые должны произойти в течение времени, установленного параметромfail_timeout. По умолчанию количество неудачных попыток установлено в0, что отключает учет попыток. Что считается неудачной попыткой, определяетсяhttp_opts.statuses, еслиtyp="http"илиnil/false, возвращаемый checkups.ready_ok. Эта опция доступна только в круговом режиме. *fail_timeout: Устанавливает время, в течение которого должно произойти указанное количество неудачных попыток связи с сервером, чтобы считать сервер недоступным, и период времени, в течение которого сервер будет считаться недоступным. По умолчанию параметр установлен на10секунд. Эта опция доступна только в круговом режиме.upstream: Имя блоков upstream Nginx. Проверки состояния извлекут серверы из блоков upstream конфигурации Nginx в prepare_checker. Модуль lua-upstream-nginx-module требуется.upstream_only_backup: Если установить вtrue, проверки состояния будут извлекать только резервные серверы из блоков upstream Nginx.
Конфигурация Nginx
Добавьте пути к файлу конфигурации lua и проверкам состояния в lua_package_path и создайте общие словари lua, используемые проверками состояния. Вы должны поместить эти строки в блок http вашего конфигурационного файла Nginx.
lua_shared_dict state 10m;
lua_shared_dict mutex 1m;
lua_shared_dict locks 1m;
lua_shared_dict config 10m;
Если вы используете подсистему stream, вы должны поместить эти строки в блок stream вашего конфигурационного файла Nginx.
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
Копирует upstream-ы из config.lua в shdict, извлекает серверы из блоков upstream Nginx и выполняет некоторые базовые инициализации.
prepare_checker
синтаксис: prepare_checker(config)
фаза: init_worker_by_lua
Копирует конфигурации из config.lua в worker проверки состояния, извлекает серверы из блоков upstream Nginx и выполняет некоторые базовые инициализации.
create_checker
синтаксис: create_checker()
фаза: init_worker_by_lua
Создает таймер heartbeat и таймер синхронизации upstream. Будет создан только один таймер heartbeat среди всех workers. Настоятельно рекомендуется вызывать этот метод в фазе init_worker.
ready_ok
синтаксис: res, err = ready_ok(skey, callback, opts?)
фаза: rewrite_by_lua*, access_by_lua*, content_by_lua*, ngx.timer.*
Выбирает доступный peer из кластера skey и вызывает 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: Ограничивает время, в течение которого запрос может быть обработан, аналогичноproxy_next_upstream_timeoutnginx.
Возвращает то, что возвращает callback при успехе, или возвращает nil и строку, описывающую ошибку в противном случае.
Если callback возвращает nil или false, проверки состояния будут считать это неудачной попыткой и повторят callback с другим peer. Поэтому всегда помните, что не следует возвращать nil или false после успешного вызова callback.
select_peer
синтаксис: peer, err = select_peer(skey)
контекст: rewrite_by_lua*, access_by_lua*, content_by_lua*, balancer_by_lua
Выбирает доступный peer из кластера skey.
Возвращает таблицу, содержащую host и port доступного peer.
В случае ошибок возвращает 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.*
Помечает сервер host:port в кластере skey как неработающий (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 в том же формате, что и cluster в config.lua.
Возвращает true при успехе, или возвращает false и строку, описывающую ошибку в противном случае.
delete_upstream
синтаксис: ok, err = delete_upstream(skey)
фаза: rewrite_by_lua*, access_by_lua*, content_by_lua*, ngx.timer.*
Удаляет кластер skey из списка upstream.
Возвращает true при успехе, или возвращает false и строку, описывающую ошибку в противном случае.
См. также
GitHub
Вы можете найти дополнительные советы по конфигурации и документацию для этого модуля в репозитории GitHub для nginx-module-checkups.