upstream: Módulo de balanceo de carga y conmutación por error de conexión upstream para nginx-module-lua
Instalación
Si no has configurado la suscripción al repositorio RPM, regístrate. Luego puedes proceder con los siguientes pasos.
CentOS/RHEL 7 o 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-upstream
CentOS/RHEL 8+, Fedora Linux, Amazon Linux 2023
dnf -y install https://extras.getpagespeed.com/release-latest.rpm
dnf -y install lua5.1-resty-upstream
Para usar esta biblioteca Lua con NGINX, asegúrate de que nginx-module-lua esté instalado.
Este documento describe lua-resty-upstream v0.10 lanzado el 19 de diciembre de 2019.
Módulo de balanceo de carga y conmutación por error de conexión upstream
Descripción general
Crea un diccionario compartido de lua. Define tus grupos y hosts upstream en init_by_lua, esto se guardará en el diccionario compartido.
Usa el método connect para devolver un socket tcp conectado.
Alternativamente, pasa un módulo resty (por ejemplo, lua-resty-redis o lua-resty-http) que implemente connect() y set_timeout().
Llama a process_failed_hosts para manejar hosts fallidos sin bloquear la solicitud actual.
Usa resty.upstream.api para modificar la configuración upstream durante la inicialización o en tiempo de ejecución, ¡esto es recomendable!
resty.upstream.http envuelve el lua-resty-http de @pintsized.
Permite la conmutación por error basada en códigos de estado HTTP así como en el estado de conexión del socket.
lua_shared_dict my_upstream_dict 1m;
init_by_lua '
upstream_socket = require("resty.upstream.socket")
upstream_api = require("resty.upstream.api")
upstream, configured = upstream_socket:new("my_upstream_dict")
if not upstream then
error(configured)
end
api = upstream_api:new(upstream)
if not configured then -- Solo reconfigurar al inicio, la memoria compartida persiste a través de un HUP
api:create_pool({id = "primary", timeout = 100})
api:set_priority("primary", 0)
api:set_method("primary", "round_robin")
api:add_host("primary", { id="a", host = "127.0.0.1", port = "80", weight = 10 })
api:add_host("primary", { id="b", host = "127.0.0.1", port = "81", weight = 10 })
api:create_pool({id = "dr"})
api:set_priority("dr", 10)
api:add_host("dr", { host = "127.0.0.1", port = "82", weight = 5 })
api:add_host("dr", { host = "127.0.0.1", port = "83", weight = 10 })
api:create_pool({id = "test", priority = 5})
api:add_host("primary", { id="c", host = "127.0.0.1", port = "82", weight = 10 })
api:add_host("primary", { id="d", host = "127.0.0.1", port = "83", weight = 10 })
end
';
init_worker_by_lua 'upstream:init_background_thread()';
server {
location / {
content_by_lua '
local sock, err = upstream:connect()
upstream:process_failed_hosts()
';
}
}
upstream.socket
new
syntax: upstream, configured = upstream_socket:new(dictionary, id?)
Devuelve un nuevo objeto upstream utilizando el nombre del diccionario proporcionado. Cuando se llama en init_by_lua, devuelve una variable adicional si el diccionario ya contiene configuración. Toma un parámetro id opcional, este debe ser único si múltiples instancias de upstream.socket están utilizando el mismo diccionario.
init_background_thread
syntax: ok, err = upstream:init_background_thread()
Inicializa el hilo en segundo plano, debe ser llamado en init_worker_by_lua.
connect
syntax: ok, err = upstream:connect(client?, key?)
Intenta conectarse a un host en los grupos definidos en orden de prioridad utilizando el método de balanceo de carga seleccionado.
Devuelve un socket conectado y una tabla que contiene el host, poolid y pool conectados o nil y un mensaje de error.
Cuando se pasa un socket o módulo resty, devolverá el mismo objeto después de una conexión exitosa o nil.
Además, los métodos de hash pueden tomar un key opcional para definir cómo hashear la conexión para determinar el host. Por defecto, se utiliza ngx.var.remote_addr. Este valor se ignora cuando el método del grupo es round robin.
resty_redis = require('resty.redis')
local redis = resty_redis.new()
local key = ngx.req.get_headers()["X-Forwarded-For"]
local redis, err = upstream:connect(redis, key)
if not redis then
ngx.log(ngx.ERR, err)
ngx.status = 500
return ngx.exit(ngx.status)
end
ngx.log(ngx.info, 'Conectado a ' .. err.host.host .. ':' .. err.host.port)
local ok, err = redis:get('key')
process_failed_hosts
syntax: ok, err = upstream:process_failed_hosts()
Procesa cualquier host fallido o recuperado de la solicitud actual. Genera un callback inmediato a través de ngx.timer.at, no bloquea la solicitud actual.
get_pools
syntax: pools = usptream:get_pools()
Devuelve una tabla que contiene la configuración actual de grupos y hosts. p.ej.
{
primary = {
up = true,
method = 'round_robin',
timeout = 100,
priority = 0,
hosts = {
web01 = {
host = "127.0.0.1",
weight = 10,
port = "80",
lastfail = 0,
failcount = 0,
up = true,
healthcheck = true
},
web02 = {
host = "127.0.0.1",
weight = 10,
port = "80",
lastfail = 0,
failcount = 0,
up = true,
healthcheck = { interval = 30, path = '/check' }
}
}
},
secondary = {
up = true,
method = 'round_robin',
timeout = 2000,
priority = 10,
hosts = {
dr01 = {
host = "10.10.10.1",
weight = 10,
port = "80",
lastfail = 0,
failcount = 0,
up = true
}
}
},
}
save_pools
syntax: ok, err = upstream:save_pools(pools)
Guarda una tabla de grupos en el diccionario compartido, pools debe estar en el mismo formato que el devuelto por get_pools.
sort_pools
syntax: ok, err = upstream:sort_pools(pools)
Genera un orden de prioridad en el diccionario compartido basado en la tabla de grupos proporcionada.
bind
syntax: ok, err = upstream:bind(event, func)
Vincula una función para ser llamada cuando ocurren eventos. func debe esperar 1 argumento que contenga los datos del evento.
Devuelve true en una vinculación exitosa o nil y un mensaje de error en caso de fallo.
local function host_down_handler(event)
ngx.log(ngx.ERR, "Host: ", event.host.host, ":", event.host.port, " en el grupo '", event.pool.id,'" está caído!')
end
local ok, err = upstream:bind('host_down', host_down_handler)
Evento: host_up
Se activa cuando un host cambia de estado de caído a activo. Los datos del evento son una tabla que contiene el host y grupo afectados.
Evento: host_down
Se activa cuando un host cambia de estado de activo a caído. Los datos del evento son una tabla que contiene el host y grupo afectados.
upstream.api
Estas funciones te permiten reconfigurar dinámicamente grupos y hosts upstream.
new
syntax: api, err = upstream_api:new(upstream)
Devuelve un nuevo objeto api utilizando el objeto upstream proporcionado.
set_method
syntax: ok, err = api:set_method(poolid, method)
Establece el método de balanceo de carga para el grupo especificado. Actualmente se admiten métodos de round robin aleatorio y hashing.
create_pool
syntax: ok, err = api:create_pool(pool)
Crea un nuevo grupo a partir de una tabla de opciones, pool debe contener al menos 1 clave id que debe ser única dentro del objeto upstream actual.
Otras opciones válidas son
methodMétodo de balanceotimeoutTiempo de espera de conexión en mspriorityGrupos de mayor prioridad se utilizan más tarderead_timeoutkeepalive_timeoutkeepalive_poolstatus_codesVer status_codes
Los hosts no se pueden definir en este punto.
Nota: Los IDs son convertidos a una cadena por esta función.
Valores predeterminados del grupo
{ method = 'round_robin', timeout = 2000, priority = 0 }
set_priority
syntax: ok, err = api:set_priority(poolid, priority)
La prioridad debe ser un número, devuelve nil en caso de error.
add_host
syntax: ok, err = api:add_host(poolid, host)
Toma un ID de grupo y una tabla de opciones, host debe contener al menos host.
Si no se especifica el ID del host, será un índice numérico basado en el número de hosts en el grupo.
Nota: Los IDs son convertidos a una cadena por esta función.
Valores predeterminados:
{ host = '', port = 80, weight = 0}
remove_host
syntax: ok, err = api:remove_host(poolid, host)
Toma un poolid y un hostid para eliminar del grupo.
down_host
syntax: ok,err = api:down_host(poolid, host)
Marca manualmente un host como caído, este host no será revivido automáticamente.
up_host
syntax: ok,err = api:up_host(poolid, host)
Restaura manualmente un host caído al grupo.
upstream.http
Funciones para realizar solicitudes http a hosts upstream.
new
syntax: httpc, err = upstream_http:new(upstream, ssl_opts?)
Devuelve un nuevo objeto http upstream utilizando el objeto upstream proporcionado.
ssl_opts es una tabla opcional para configurar el soporte SSL.
* ssl establecido en true para habilitar el apretón de manos SSL, predeterminado false
* ssl_verify establecido en false para deshabilitar la verificación del certificado SSL, predeterminado true
* sni_host una cadena para usar como el nombre de host sni, el predeterminado es el encabezado Host de la solicitud.
lua
https_upstream = Upstream_HTTP:new(upstream_ssl, {
ssl = true,
ssl_verify = true,
sni_host = "foo.example.com"
})
init_background_thread
syntax: ok, err = upstream_http:init_background_thread()
Inicializa el hilo en segundo plano, debe ser llamado en init_worker_by_lua.
No llames al método init_background_thread en upstream.socket si usas el hilo en segundo plano de upstream.http.
request
syntax: res, err_or_conn_info, status? = upstream_api:request(params)
Toma los mismos parámetros que el método request de lua-resty-http.
En una solicitud exitosa, devuelve el objeto lua-resty-http y una tabla que contiene el host y grupo conectados.
Si la solicitud falla, devuelve nil, el error y un código de estado http sugerido.
local ok, err, status = upstream_http:request({
path = "/helloworld",
headers = {
["Host"] = "example.com",
}
})
if not ok then
ngx.status = status
ngx.say(err)
ngx.exit(status)
else
local host = err.host
local pool = err.pool
end
set_keepalive
syntax: ok, err = upstream_http:set_keepalive()
Pasa el tiempo de espera de keepalive / pool de la configuración del grupo al método set_keepalive de lua-resty-http.
get_reused_times
syntax: ok, err = upstream_http:get_reused_times()
Pasa al método get_reused_times de lua-resty-http.
close
syntax: ok, err = upstream_http:close()
Pasa al método close de lua-resty-http.
Comprobaciones de salud HTTP
Las comprobaciones de salud activas en segundo plano pueden habilitarse agregando el parámetro healthcheck a un host.
Un valor de true habilitará la comprobación predeterminada, una solicitud GET para /.
El parámetro healthcheck también puede ser una tabla de parámetros válidos para el método request de lua-resty-http.
Con algunos parámetros adicionales
intervalpara establecer el tiempo entre comprobaciones de salud, en segundos. Debe ser >= 10s. Predeterminado a 60s.timeoutestablece el tiempo de espera de conexión para las comprobaciones de salud. Predeterminado a la configuración del grupo.read_timeoutestablece el tiempo de espera de lectura para las comprobaciones de salud. Predeterminado a la configuración del grupo.status_codesuna tabla de códigos de estado de respuesta no válidos. Predeterminado a la configuración del grupo.
El fallo de la comprobación en segundo plano se realiza de acuerdo con los mismos parámetros que para una solicitud de frontend, a menos que se anule explícitamente.
-- Parámetros de comprobación personalizados
api:add_host("primary", {
host = 123.123.123.123,
port = 80,
healthcheck = {
interval = 30, -- comprobar cada 30s
timeout = (5*1000), -- 5s de tiempo de espera de conexión
read_timeout = (15*1000), -- 15s de tiempo de espera de conexión
status_codes = {["5xx"] = true, ["403"] = true}, -- respuestas 5xx y 403 son un fallo
-- parámetros de resty-http
path = "/check",
headers = {
["Host"] = "domain.com",
["Accept-Encoding"] = "gzip"
}
}
})
-- Parámetros de comprobación predeterminados
api:add_host("primary", {host = 123.123.123.123, port = 80, healthcheck = true})
GitHub
Puedes encontrar consejos de configuración adicionales y documentación para este módulo en el repositorio de GitHub para nginx-module-upstream.