Saltar a contenido

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

  • method Método de balanceo
  • timeout Tiempo de espera de conexión en ms
  • priority Grupos de mayor prioridad se utilizan más tarde
  • read_timeout
  • keepalive_timeout
  • keepalive_pool
  • status_codes Ver 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

  • interval para establecer el tiempo entre comprobaciones de salud, en segundos. Debe ser >= 10s. Predeterminado a 60s.
  • timeout establece el tiempo de espera de conexión para las comprobaciones de salud. Predeterminado a la configuración del grupo.
  • read_timeout establece el tiempo de espera de lectura para las comprobaciones de salud. Predeterminado a la configuración del grupo.
  • status_codes una 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.