Aller au contenu

upstream: Module d'équilibrage de charge et de basculement de connexion en amont pour nginx-module-lua

Installation

Si vous n'avez pas encore configuré l'abonnement au dépôt RPM, inscrivez-vous. Ensuite, vous pouvez procéder avec les étapes suivantes.

CentOS/RHEL 7 ou 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

Pour utiliser cette bibliothèque Lua avec NGINX, assurez-vous que nginx-module-lua est installé.

Ce document décrit lua-resty-upstream v0.10 publié le 19 décembre 2019.


Module d'équilibrage de charge et de basculement de connexion en amont

Vue d'ensemble

Créez un dictionnaire partagé Lua. Définissez vos pools et hôtes en amont dans init_by_lua, cela sera enregistré dans le dictionnaire partagé.

Utilisez la méthode connect pour retourner un socket TCP connecté.

Vous pouvez également passer un module resty (par exemple lua-resty-redis ou lua-resty-http) qui implémente connect() et set_timeout().

Appelez process_failed_hosts pour gérer les hôtes échoués sans bloquer la requête actuelle.

Utilisez resty.upstream.api pour modifier la configuration en amont pendant l'initialisation ou à l'exécution, c'est recommandé !

resty.upstream.http enveloppe le lua-resty-http de @pintsized.

Il permet le basculement basé sur les codes d'état HTTP ainsi que sur l'état de connexion du 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 -- Reconfigurer uniquement au démarrage, la mémoire partagée persiste à travers 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?)

Retourne un nouvel objet en amont en utilisant le nom du dictionnaire fourni. Lorsqu'il est appelé dans init_by_lua, il retourne une variable supplémentaire si le dictionnaire contient déjà une configuration. Prend un paramètre id optionnel, celui-ci doit être unique si plusieurs instances de upstream.socket utilisent le même dictionnaire.

init_background_thread

syntax: ok, err = upstream:init_background_thread()

Initialise le thread d'arrière-plan, doit être appelé dans init_worker_by_lua.

connect

syntax: ok, err = upstream:connect(client?, key?)

Tente de se connecter à un hôte dans les pools définis dans l'ordre de priorité en utilisant la méthode d'équilibrage de charge sélectionnée. Retourne un socket connecté et une table contenant l'hôte, poolid et pool ou nil et un message d'erreur.

Lorsqu'un socket ou un module resty est passé, il retournera le même objet après une connexion réussie ou nil.

De plus, les méthodes de hachage peuvent prendre une key optionnelle pour définir comment hacher la connexion afin de déterminer l'hôte. Par défaut, ngx.var.remote_addr est utilisé. Cette valeur est ignorée lorsque la méthode du pool est 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, 'Connected to ' .. err.host.host .. ':' .. err.host.port)
local ok, err = redis:get('key')

process_failed_hosts

syntax: ok, err = upstream:process_failed_hosts()

Traite tous les hôtes échoués ou récupérés de la requête actuelle. Lance un rappel immédiat via ngx.timer.at, ne bloque pas la requête actuelle.

get_pools

syntax: pools = usptream:get_pools()

Retourne une table contenant la configuration actuelle des pools et des hôtes. Par exemple :

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

Sauvegarde une table de pools dans le dictionnaire partagé, pools doit être au même format que celui retourné par get_pools.

sort_pools

syntax: ok, err = upstream:sort_pools(pools)

Génère un ordre de priorité dans le dictionnaire partagé basé sur la table de pools fournie.

bind

syntax: ok, err = upstream:bind(event, func)

Liaison d'une fonction à appeler lorsque des événements se produisent. func doit s'attendre à 1 argument contenant les données de l'événement.

Retourne true en cas de liaison réussie ou nil et un message d'erreur en cas d'échec.

local function host_down_handler(event)
    ngx.log(ngx.ERR, "Host: ", event.host.host, ":", event.host.port, " dans le pool '", event.pool.id,'" est hors service!')
end
local ok, err = upstream:bind('host_down', host_down_handler)

Événement : host_up

Déclenché lorsqu'un hôte change de statut de hors service à en service. Les données de l'événement sont une table contenant l'hôte et le pool affectés.

Événement : host_down

Déclenché lorsqu'un hôte change de statut de en service à hors service. Les données de l'événement sont une table contenant l'hôte et le pool affectés.

upstream.api

Ces fonctions vous permettent de reconfigurer dynamiquement les pools et les hôtes en amont.

new

syntax: api, err = upstream_api:new(upstream)

Retourne un nouvel objet api en utilisant l'objet en amont fourni.

set_method

syntax: ok, err = api:set_method(poolid, method)

Définit la méthode d'équilibrage de charge pour le pool spécifié. Actuellement, les méthodes de round robin randomisées et de hachage sont prises en charge.

create_pool

syntax: ok, err = api:create_pool(pool)

Crée un nouveau pool à partir d'une table d'options, pool doit contenir au moins 1 clé id qui doit être unique dans l'objet en amont actuel.

D'autres options valides sont :

  • method Méthode d'équilibrage
  • timeout Délai d'attente de connexion en ms
  • priority Les pools de priorité plus élevée sont utilisés plus tard
  • read_timeout
  • keepalive_timeout
  • keepalive_pool
  • status_codes Voir status_codes

Les hôtes ne peuvent pas être définis à ce stade.

Remarque : Les ID sont convertis en chaîne par cette fonction.

Valeurs par défaut du pool :

{ method = 'round_robin', timeout = 2000, priority = 0 }

set_priority

syntax: ok, err = api:set_priority(poolid, priority)

La priorité doit être un nombre, retourne nil en cas d'erreur.

add_host

syntax: ok, err = api:add_host(poolid, host)

Prend un ID de pool et une table d'options, host doit contenir au moins host. Si l'ID de l'hôte n'est pas spécifié, il sera un index numérique basé sur le nombre d'hôtes dans le pool.

Remarque : Les ID sont convertis en chaîne par cette fonction.

Valeurs par défaut :

{ host = '', port = 80, weight = 0}

remove_host

syntax: ok, err = api:remove_host(poolid, host)

Prend un poolid et un hostid à supprimer du pool.

down_host

syntax: ok,err = api:down_host(poolid, host)

Marque manuellement un hôte comme hors service, cet hôte ne sera pas réactivé automatiquement.

up_host

syntax: ok,err = api:up_host(poolid, host)

Restaure manuellement un hôte mort dans le pool.

upstream.http

Fonctions pour effectuer des requêtes HTTP vers des hôtes en amont.

new

syntax: httpc, err = upstream_http:new(upstream, ssl_opts?)

Retourne un nouvel objet HTTP en amont en utilisant l'objet en amont fourni.

ssl_opts est une table optionnelle pour configurer le support SSL. * ssl défini sur true pour activer le handshake SSL, par défaut false * ssl_verify défini sur false pour désactiver la vérification du certificat SSL, par défaut true * sni_host une chaîne à utiliser comme nom d'hôte SNI, par défaut l'en-tête Host de la requête

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

Initialise le thread d'arrière-plan, doit être appelé dans init_worker_by_lua.

Ne pas appeler la méthode init_background_thread dans upstream.socket si vous utilisez le thread d'arrière-plan upstream.http.

request

syntax: res, err_or_conn_info, status? = upstream_api:request(params)

Prend les mêmes paramètres que la méthode request de lua-resty-http.

Lors d'une requête réussie, retourne l'objet lua-resty-http et une table contenant l'hôte et le pool connectés.

Si la requête échoue, retourne nil, l'erreur et un code d'état HTTP suggéré.

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

Transmet le délai d'attente de keepalive / pool à la méthode set_keepalive de lua-resty-http.

get_reused_times

syntax: ok, err = upstream_http:get_reused_times()

Transmet à la méthode get_reused_times de lua-resty-http.

close

syntax: ok, err = upstream_http:close()

Transmet à la méthode close de lua-resty-http.

Vérifications de santé HTTP

Des vérifications de santé actives en arrière-plan peuvent être activées en ajoutant le paramètre healthcheck à un hôte.

Une valeur de true activera la vérification par défaut, une requête GET pour /.

Le paramètre healthcheck peut également être une table de paramètres valides pour la méthode request de lua-resty-http.

Avec quelques paramètres supplémentaires :

  • interval pour définir le temps entre les vérifications de santé, en secondes. Doit être >= 10s. Par défaut 60s.
  • timeout définit le délai d'attente de connexion pour les vérifications de santé. Par défaut, réglage du pool.
  • read_timeout définit le délai d'attente de lecture pour les vérifications de santé. Par défaut, réglage du pool.
  • status_codes une table de codes d'état de réponse invalides. Par défaut, réglage du pool.

L'échec de la vérification en arrière-plan est selon les mêmes paramètres que pour une requête frontend, sauf si explicitement remplacé.

-- Paramètres de vérification personnalisés
api:add_host("primary", {
     host = 123.123.123.123,
     port = 80,
     healthcheck = {
        interval = 30, -- vérification toutes les 30s
        timeout      = (5*1000), -- délai d'attente de connexion de 5s
        read_timeout = (15*1000), -- délai d'attente de lecture de 15s
        status_codes = {["5xx"] = true, ["403"] = true}, -- les réponses 5xx et 403 sont un échec
        -- paramètres resty-http
        path = "/check",
        headers = {
            ["Host"] = "domain.com",
            ["Accept-Encoding"] = "gzip"
        }
     }
})

-- Paramètres de vérification par défaut
api:add_host("primary", {host = 123.123.123.123, port = 80, healthcheck = true})

GitHub

Vous pouvez trouver des conseils de configuration supplémentaires et de la documentation pour ce module dans le dépôt GitHub pour nginx-module-upstream.