Zum Inhalt

upstream: Upstream-Verbindungslastenausgleich und Failover-Modul für nginx-module-lua

Installation

Wenn Sie das RPM-Repository-Abonnement noch nicht eingerichtet haben, melden Sie sich an. Danach können Sie mit den folgenden Schritten fortfahren.

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

Um diese Lua-Bibliothek mit NGINX zu verwenden, stellen Sie sicher, dass nginx-module-lua installiert ist.

Dieses Dokument beschreibt lua-resty-upstream v0.10, das am 19. Dezember 2019 veröffentlicht wurde.


Upstream-Verbindungslastenausgleich und Failover-Modul

Übersicht

Erstellen Sie ein lua shared dictionary. Definieren Sie Ihre Upstream-Pools und Hosts in init_by_lua, dies wird im Shared Dictionary gespeichert.

Verwenden Sie die connect-Methode, um einen verbundenen tcp socket zurückzugeben.

Alternativ können Sie ein Resty-Modul (z. B. lua-resty-redis oder lua-resty-http) übergeben, das connect() und set_timeout() implementiert.

Rufen Sie process_failed_hosts auf, um fehlgeschlagene Hosts zu behandeln, ohne die aktuelle Anfrage zu blockieren.

Verwenden Sie resty.upstream.api, um die Upstream-Konfiguration während der Initialisierung oder zur Laufzeit zu ändern, dies wird empfohlen!

resty.upstream.http umschließt die lua-resty-http von @pintsized.

Es ermöglicht Failover basierend auf HTTP-Statuscodes sowie dem Status der Socket-Verbindung.

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 -- Nur bei Start neu konfigurieren, der Shared Memory bleibt bei einem HUP bestehen
        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?)

Gibt ein neues Upstream-Objekt unter Verwendung des angegebenen Dictionary-Namens zurück. Wenn es in init_by_lua aufgerufen wird, gibt es eine zusätzliche Variable zurück, wenn das Dictionary bereits eine Konfiguration enthält. Nimmt einen optionalen id-Parameter, dieser muss eindeutig sein, wenn mehrere Instanzen von upstream.socket dasselbe Dictionary verwenden.

init_background_thread

syntax: ok, err = upstream:init_background_thread()

Initialisiert den Hintergrund-Thread, sollte in init_worker_by_lua aufgerufen werden.

connect

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

Versucht, sich mit einem Host in den definierten Pools in Prioritätsreihenfolge unter Verwendung der ausgewählten Lastenausgleichsmethode zu verbinden. Gibt einen verbundenen Socket und eine Tabelle mit dem verbundenen host, poolid und pool oder nil und eine Fehlermeldung zurück.

Wenn ein socket oder Resty-Modul übergeben wird, wird dasselbe Objekt nach erfolgreicher Verbindung oder nil zurückgegeben.

Zusätzlich können Hash-Methoden einen optionalen key annehmen, um zu definieren, wie die Verbindung gehasht wird, um den Host zu bestimmen. Standardmäßig wird ngx.var.remote_addr verwendet. Dieser Wert wird ignoriert, wenn die Methode des Pools Round Robin ist.

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

Verarbeitet alle fehlgeschlagenen oder wiederhergestellten Hosts aus der aktuellen Anfrage. Erzeugt einen sofortigen Callback über ngx.timer.at, blockiert jedoch nicht die aktuelle Anfrage.

get_pools

syntax: pools = usptream:get_pools()

Gibt eine Tabelle mit der aktuellen Pool- und Hostkonfiguration zurück. z.B.

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

Speichert eine Tabelle von Pools im Shared Dictionary, pools muss im selben Format wie von get_pools zurückgegeben sein.

sort_pools

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

Erzeugt eine Prioritätsreihenfolge im Shared Dictionary basierend auf der bereitgestellten Tabelle von Pools.

bind

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

Bindet eine Funktion, die aufgerufen wird, wenn Ereignisse auftreten. func sollte 1 Argument erwarten, das die Ereignisdaten enthält.

Gibt true bei erfolgreicher Bindung oder nil und eine Fehlermeldung bei Fehler zurück.

local function host_down_handler(event)
    ngx.log(ngx.ERR, "Host: ", event.host.host, ":", event.host.port, " im Pool '", event.pool.id,'" ist ausgefallen!')
end
local ok, err = upstream:bind('host_down', host_down_handler)

Ereignis: host_up

Ausgelöst, wenn ein Host seinen Status von down auf up ändert. Ereignisdaten sind eine Tabelle, die den betroffenen Host und Pool enthält.

Ereignis: host_down

Ausgelöst, wenn ein Host seinen Status von up auf down ändert. Ereignisdaten sind eine Tabelle, die den betroffenen Host und Pool enthält.

upstream.api

Diese Funktionen ermöglichen es Ihnen, Upstream-Pools und Hosts dynamisch neu zu konfigurieren.

new

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

Gibt ein neues API-Objekt unter Verwendung des bereitgestellten Upstream-Objekts zurück.

set_method

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

Setzt die Lastenausgleichsmethode für den angegebenen Pool. Derzeit werden randomisierte Round Robin- und Hashing-Methoden unterstützt.

create_pool

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

Erstellt einen neuen Pool aus einer Tabelle von Optionen, pool muss mindestens 1 Schlüssel id enthalten, der innerhalb des aktuellen Upstream-Objekts eindeutig sein muss.

Andere gültige Optionen sind

  • method Balancing-Methode
  • timeout Verbindungszeitüberschreitung in ms
  • priority Höhere Prioritätspools werden später verwendet
  • read_timeout
  • keepalive_timeout
  • keepalive_pool
  • status_codes Siehe status_codes

Hosts können an diesem Punkt nicht definiert werden.

Hinweis: IDs werden von dieser Funktion in einen String umgewandelt.

Standardpoolwerte

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

set_priority

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

Die Priorität muss eine Zahl sein, gibt nil bei einem Fehler zurück.

add_host

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

Nimmt eine Pool-ID und eine Tabelle von Optionen, host muss mindestens host enthalten. Wenn die Host-ID nicht angegeben ist, wird sie ein numerischer Index basierend auf der Anzahl der Hosts im Pool sein.

Hinweis: IDs werden von dieser Funktion in einen String umgewandelt.

Standardeinstellungen:

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

remove_host

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

Nimmt eine poolid und eine hostid, um sie aus dem Pool zu entfernen.

down_host

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

Markiert einen Host manuell als down, dieser Host wird nicht automatisch wiederbelebt.

up_host

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

Stellt einen ausgefallenen Host manuell wieder im Pool her.

upstream.http

Funktionen zum Senden von HTTP-Anfragen an Upstream-Hosts.

new

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

Gibt ein neues HTTP-Upstream-Objekt unter Verwendung des bereitgestellten Upstream-Objekts zurück.

ssl_opts ist eine optionale Tabelle zur Konfiguration der SSL-Unterstützung. * ssl auf true setzen, um SSL-Handshake zu aktivieren, standardmäßig false * ssl_verify auf false setzen, um die SSL-Zertifikatsüberprüfung zu deaktivieren, standardmäßig true * sni_host ein String, der als SNI-Hostname verwendet wird, standardmäßig ist der Host-Header der Anfrage

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

Initialisiert den Hintergrund-Thread, sollte in init_worker_by_lua aufgerufen werden.

Rufen Sie nicht die Methode init_background_thread in upstream.socket auf, wenn Sie den Hintergrund-Thread von upstream.http verwenden.

request

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

Nimmt dieselben Parameter wie die request-Methode von lua-resty-http.

Bei einer erfolgreichen Anfrage gibt es das lua-resty-http-Objekt und eine Tabelle mit dem verbundenen Host und Pool zurück.

Wenn die Anfrage fehlgeschlagen ist, gibt es nil, den Fehler und einen vorgeschlagenen HTTP-Statuscode zurück.

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

Leitet die Keepalive-Zeitüberschreitung / den Pool aus der Poolkonfiguration an die lua-resty-http set_keepalive-Methode weiter.

get_reused_times

syntax: ok, err = upstream_http:get_reused_times()

Leitet die lua-resty-http get_reused_times-Methode weiter.

close

syntax: ok, err = upstream_http:close()

Leitet die lua-resty-http close-Methode weiter.

HTTP-Gesundheitschecks

Aktive Hintergrund-Gesundheitschecks können aktiviert werden, indem der Parameter healthcheck zu einem Host hinzugefügt wird.

Ein Wert von true aktiviert die Standardprüfung, eine GET-Anfrage für /.

Der Parameter healthcheck kann auch eine Tabelle von Parametern sein, die für die request-Methode von lua-resty-http gültig sind.

Mit einigen zusätzlichen Parametern

  • interval, um die Zeit zwischen Gesundheitsprüfungen in Sekunden festzulegen. Muss >= 10s sein. Standardmäßig 60s.
  • timeout setzt die Verbindungszeitüberschreitung für Gesundheitsprüfungen. Standardmäßig auf die Pool-Einstellung.
  • read_timeout setzt die Lesezeitüberschreitung für Gesundheitsprüfungen. Standardmäßig auf die Pool-Einstellung.
  • status_codes eine Tabelle ungültiger Antwortstatuscodes. Standardmäßig auf die Pool-Einstellung.

Das Scheitern der Hintergrundprüfung erfolgt nach denselben Parametern wie für eine Frontend-Anfrage, es sei denn, es wird ausdrücklich überschrieben.

-- Benutzerdefinierte Prüfparameter
api:add_host("primary", {
     host = 123.123.123.123,
     port = 80,
     healthcheck = {
        interval = 30, -- alle 30s prüfen
        timeout      = (5*1000), -- 5s Verbindungszeitüberschreitung
        read_timeout = (15*1000), -- 15s Verbindungszeitüberschreitung
        status_codes = {["5xx"] = true, ["403"] = true}, -- 5xx- und 403-Antworten sind ein Fehler
        -- resty-http-Parameter
        path = "/check",
        headers = {
            ["Host"] = "domain.com",
            ["Accept-Encoding"] = "gzip"
        }
     }
})

-- Standardprüfparameter
api:add_host("primary", {host = 123.123.123.123, port = 80, healthcheck = true})

GitHub

Sie finden möglicherweise zusätzliche Konfigurationstipps und Dokumentationen für dieses Modul im GitHub-Repository für nginx-module-upstream.