Saltar a contenido

checkups: Gestionar upstreams de NGINX en Lua puro

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-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

Para usar esta biblioteca Lua con NGINX, asegúrate de que nginx-module-lua esté instalado.

Este documento describe lua-resty-checkups v0.1 lanzado el 01 de febrero de 2019.


  • Latido periódico a servidores upstream
  • Verificación de salud proactiva y pasiva
  • Actualización dinámica de upstream
  • Balanceo por round-robin ponderado o hash consistente
  • Sincronización con bloques upstream de Nginx
  • Probar clústeres por niveles o por claves

Sinopsis

    -- 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

                -- conectar a un servidor muerto, no hay upstream disponible
                ok, err = checkups.ready_ok("ups1", callback)
                if err then ngx.say(err) end

                -- agregar servidor a 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

                -- agregar servidor a nuevo 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

                -- agregar servidor a ups2, restablecer estado 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
            }
        }
    }

Una salida típica de la ubicación /t definida arriba es:

no hay servidores disponibles
12350
12350
12351
12350
12351

Configuración

Configuración de Lua

El archivo de configuración de checkups es un módulo lua que consta de dos partes, la parte global y la parte del clúster.

Un ejemplo de archivo de configuración de checkups se muestra a continuación,

    -- config.lua

    -- Aquí está la parte global

    _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,
    }

    -- Las partes restantes son configuraciones del clúster

    _M.redis = {
        enable = true,
        typ = "redis",
        timeout = 2,
        read_timeout = 15,
        send_timeout = 15,

        protected = true,

        cluster = {
            {   -- nivel 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 },
                }
            },
            {   -- nivel 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 = {
            {   -- nivel 1
                upstream = "api.com",
            },
            {   -- nivel 2
                upstream = "api.com",
                upstream_only_backup = true,
            },
        },
    }

    return _M

configuraciones globales

  • checkup_timer_interval: Intervalo de envío de latidos a los servidores backend. El valor predeterminado es 5.
  • checkup_timer_overtime: Intervalo de las verificaciones para expirar la clave del temporizador. En la mayoría de los casos, no necesitas cambiar este valor. El valor predeterminado es 60.
  • default_heartbeat_enable: Las verificaciones enviarán latidos a los servidores por defecto o no. El valor predeterminado es true.
  • checkup_shd_sync_enable: Crear sincronizador de upstream para cada trabajador. Si se establece en false, el upstream dinámico no funcionará correctamente. El valor predeterminado es true.
  • shd_config_timer_interval: Intervalo de sincronización de la lista de upstream desde la memoria compartida. El valor predeterminado es igual a checkup_timer_interval.
  • ups_status_sync_enable: Si se establece en true, las verificaciones sincronizarán el estado del upstream desde las verificaciones a los bloques upstream de Nginx. El valor predeterminado es false.
  • ups_status_timer_interval: Intervalo de sincronización del estado del upstream desde las verificaciones a los bloques upstream de Nginx.

Configuraciones del clúster

  • skey: _M.xxxxx. xxxxx es la skey (clave de servicio) de este clúster.
  • enable: Habilitar o deshabilitar latidos a los servidores. El valor predeterminado es true.
  • typ: Tipo de clúster, debe ser uno de general, redis, mysql, http. El valor predeterminado es general.
    • general: Latido por TCP sock:connect.
    • redis: Latido por redis PING. Se requiere el módulo lua-resty-redis.
    • mysql: Latido por mysql db:connect. Se requiere el módulo lua-resty-mysql.
    • http: Latido por solicitud HTTP. Puedes configurar una solicitud HTTP personalizada y códigos de respuesta en http_opts.
  • timeout: Tiempo de conexión a los servidores upstream. El valor predeterminado es 5.
  • read_timeout: Tiempo de lectura a los servidores upstream (no se utiliza durante el latido). El valor predeterminado es igual a timeout.
  • send_timeout: Tiempo de escritura a los servidores upstream (no se utiliza durante el latido). El valor predeterminado es igual a timeout.
  • http_opts: Configuraciones de latido HTTP. Solo funciona para typ="http".

    • query: Solicitud HTTP para el latido.
    • statuses: Si el código devuelto por el servidor se establece en false, entonces el servidor se considera que está fallando.
  • mode: Modo de balanceo. Puede configurarse como hash, url_hash o ip_hash. Las verificaciones balancearán los servidores por hash_key, ngx.var.uri o ngx.var.remote_addr. El valor predeterminado es wrr.

  • protected: Si se establece en true y todos los servidores en el clúster están fallando, las verificaciones no marcarán el último servidor fallido como no disponible (err), en su lugar, se marcará como inestable (aún disponible en el siguiente intento). El valor predeterminado es true.
  • cluster: Puedes configurar múltiples niveles según la prioridad del clúster, en cada nivel puedes configurar un clúster de servers. Las verificaciones intentarán el siguiente nivel solo cuando todos los servidores en el nivel anterior se consideren no disponibles.

    En lugar de intentar clústeres por niveles, puedes configurar las verificaciones para intentar clústeres por clave (ver el clúster api arriba). Recuerda que también debes pasar un argumento adicional como opts.cluster_key={"dc1", "dc2"} o opts.cluster_key={3, 1, 2} a checkups.read_ok para que las verificaciones intenten en el orden de dc1, dc2 o nivel 3, nivel 1, nivel 2. Si no has pasado opts.cluster_key a checkups.ready_ok, las verificaciones aún intentarán clústeres por niveles. En cuanto al clúster api anterior, las verificaciones eventualmente devolverán no servers available. * try: Conteo de reintentos. El valor predeterminado es el número de servidores. * try_timeout: Limita el tiempo durante el cual se puede responder a una solicitud, de manera similar a proxy_next_upstream_timeout de nginx. * servers: La configuración para servers se enumera a continuación, * weight: Establece el peso del servidor. El valor predeterminado es 1. * max_fails: Establece el número de intentos fallidos para comunicarse con el servidor que deben ocurrir en la duración establecida por el parámetro fail_timeout. Por defecto, el número de intentos fallidos se establece en 0, lo que desactiva el conteo de intentos. Lo que se considera un intento fallido se define por http_opts.statuses si typ="http" o un valor nil/false devuelto por checkups.ready_ok. Esta opción solo está disponible en round-robin. * fail_timeout: Establece el tiempo durante el cual el número especificado de intentos fallidos para comunicarse con el servidor debe ocurrir para considerar el servidor no disponible y el período de tiempo que el servidor será considerado no disponible. Por defecto, el parámetro se establece en 10 segundos. Esta opción solo está disponible en round-robin.

    • upstream: Nombre de los bloques upstream de Nginx. Las verificaciones extraerán servidores de los bloques upstream de la configuración de Nginx en prepare_checker. Se requiere el módulo lua-upstream-nginx-module.
    • upstream_only_backup: Si se establece en true, las verificaciones solo extraerán servidores de respaldo de los bloques upstream de Nginx.

Configuración de Nginx

Agrega las rutas del archivo de configuración de lua y checkups a lua_package_path y crea diccionarios compartidos de lua utilizados por checkups. Debes poner estas líneas en el bloque http de tu archivo de configuración de Nginx.

lua_shared_dict state 10m;
lua_shared_dict mutex 1m;
lua_shared_dict locks 1m;
lua_shared_dict config 10m;

Si usas el subsistema de stream, debes poner estas líneas en el bloque stream de tu archivo de configuración de 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

sintaxis: init(config)

fase: init_by_lua

Copia los upstreams de config.lua a shdict, extrae servidores de los bloques upstream de Nginx y realiza algunas inicializaciones básicas.

prepare_checker

sintaxis: prepare_checker(config)

fase: init_worker_by_lua

Copia configuraciones de config.lua a los checkups del trabajador, extrae servidores de los bloques upstream de Nginx y realiza algunas inicializaciones básicas.

create_checker

sintaxis: create_checker()

fase: init_worker_by_lua

Crea un temporizador de latido y un temporizador de sincronización de upstream. Solo se creará un temporizador de latido entre todos los trabajadores. Se recomienda encarecidamente llamar a este método en la fase init_worker.

ready_ok

sintaxis: res, err = ready_ok(skey, callback, opts?)

fase: rewrite_by_lua*, access_by_lua*, content_by_lua*, ngx.timer.*

Selecciona un peer disponible del clúster skey y llama a callback(peer.host, peer.port, opts).

La tabla opts acepta los siguientes campos,

  • cluster_key: Intenta clústeres por cluster_key. Las verificaciones intentarán clústeres en el orden de cluster_key. clusters_key puede ser el nombre de los clústeres o el nivel de los clústeres. clústeres ej: {"cluster_name_A", "name_B", "name_C"}. niveles ej: {3, 2, 1}.
  • hash_key: Clave utilizada en el modo de balanceo hash. Si no se establece, se utilizará ngx.var.uri.
  • try: El reintento no será más de try veces.
  • try_timeout: Limita el tiempo durante el cual se puede responder a una solicitud, de manera similar a proxy_next_upstream_timeout de nginx.

Devuelve lo que callback devuelve en caso de éxito, o devuelve nil y una cadena que describe el error en caso contrario.

Si callback devuelve nil o false, las verificaciones lo considerarán un intento fallido y volverán a intentar callback con otro peer. Así que, recuerda siempre no devolver nil o false después de un callback exitoso.

select_peer

sintaxis: peer, err = select_peer(skey)

contexto: rewrite_by_lua*, access_by_lua*, content_by_lua*, balancer_by_lua

Selecciona un peer disponible del clúster skey.

Devuelve una tabla que contiene host y port de un peer disponible.

En caso de errores, devuelve nil con una cadena que describe el error.

get_status

sintaxis: status = get_status()

fase: rewrite_by_lua*, access_by_lua*, content_by_lua*, ngx.timer.*

Devuelve el estado de las verificaciones en formato json.

get_ups_timeout

sintaxis: connect_timeout, send_timeout, read_timeout = get_ups_timeout(skey)

fase: rewrite_by_lua*, access_by_lua*, content_by_lua*, ngx.timer.*

Devuelve el tiempo de espera del clúster skey.

feedback_status

sintaxis: ok, err = feedback_status(skey, host, port, failed)

contexto: rewrite_by_lua*, access_by_lua*, content_by_lua*, ngx.timer.*, balancer_by_lua.*

Marca el servidor host:port en el clúster skey como fallido (true) o disponible (false).

Devuelve 1 en caso de éxito, o devuelve nil y una cadena que describe el error en caso contrario.

update_upstream

sintaxis: ok, err = update_upstream(skey, upstream)

fase: rewrite_by_lua*, access_by_lua*, content_by_lua*, ngx.timer.*

Actualiza el clúster skey. upstream está en el mismo formato que cluster en config.lua.

Devuelve true en caso de éxito, o devuelve false y una cadena que describe el error en caso contrario.

delete_upstream

sintaxis: ok, err = delete_upstream(skey)

fase: rewrite_by_lua*, access_by_lua*, content_by_lua*, ngx.timer.*

Elimina el clúster skey de la lista de upstream.

Devuelve true en caso de éxito, o devuelve false y una cadena que describe el error en caso contrario.

Ver También

GitHub

Puedes encontrar consejos de configuración adicionales y documentación para este módulo en el repositorio de GitHub para nginx-module-checkups.