Aller au contenu

checkups: Gérer les upstreams NGINX en Lua pur

Installation

Si vous n'avez pas 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-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

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

Ce document décrit lua-resty-checkups v0.1 publié le 1er février 2019.


  • Envoi périodique de signaux de vie aux serveurs en amont
  • Vérification de santé proactive et passive
  • Mise à jour dynamique des upstreams
  • Équilibrage par round-robin pondéré ou hachage cohérent
  • Synchronisation avec les blocs upstream de Nginx
  • Essayer des clusters par niveaux ou par clés

Synopsis

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

                -- se connecter à un serveur mort, aucun upstream disponible
                ok, err = checkups.ready_ok("ups1", callback)
                if err then ngx.say(err) end

                -- ajouter un serveur à 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

                -- ajouter un serveur à un nouvel 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

                -- ajouter un serveur à ups2, réinitialiser l'état 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
            }
        }
    }

Une sortie typique de l'emplacement /t défini ci-dessus est :

no servers available
12350
12350
12351
12350
12351

Configuration

Configuration Lua

Le fichier de configuration des checkups est un module lua qui se compose de deux parties, la partie globale et la partie cluster.

Un exemple de fichier de configuration des checkups est montré ci-dessous,

    -- config.lua

    -- Voici la partie globale

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

    -- Les autres parties sont des configurations de cluster

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

        protected = true,

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

    return _M

configurations globales

  • checkup_timer_interval: Intervalle d'envoi des signaux de vie aux serveurs backend. La valeur par défaut est 5.
  • checkup_timer_overtime: Intervalle de vérifications pour expirer la clé de minuterie. Dans la plupart des cas, vous n'avez pas besoin de changer cette valeur. La valeur par défaut est 60.
  • default_heartbeat_enable: Les checkups enverront des signaux de vie aux serveurs par défaut ou non. La valeur par défaut est true.
  • checkup_shd_sync_enable: Créer un synchroniseur d'upstream pour chaque worker. Si défini sur false, l'upstream dynamique ne fonctionnera pas correctement. La valeur par défaut est true.
  • shd_config_timer_interval: Intervalle de synchronisation de la liste des upstreams depuis la mémoire partagée. La valeur par défaut est égale à checkup_timer_interval.
  • ups_status_sync_enable: Si défini sur true, les checkups synchroniseront l'état de l'upstream des checkups vers les blocs upstream de Nginx. La valeur par défaut est false.
  • ups_status_timer_interval: Intervalle de synchronisation de l'état de l'upstream des checkups vers les blocs upstream de Nginx.

Configurations de cluster

  • skey: _M.xxxxx. xxxxx est la skey (clé de service) de ce cluster.
  • enable: Activer ou désactiver les signaux de vie aux serveurs. La valeur par défaut est true.
  • typ: Type de cluster, doit être l'un des general, redis, mysql, http. La valeur par défaut est general.
    • general: Signal de vie par TCP sock:connect.
    • redis: Signal de vie par redis PING. Le module lua-resty-redis est requis.
    • mysql: Signal de vie par mysql db:connect. Le module lua-resty-mysql est requis.
    • http: Signal de vie par requête HTTP. Vous pouvez configurer des requêtes HTTP personnalisées et des codes de réponse dans http_opts.
  • timeout: Délai de connexion aux serveurs upstream. La valeur par défaut est 5.
  • read_timeout: Délai de lecture pour les serveurs upstream (non utilisé lors de l'envoi de signaux de vie). La valeur par défaut est égale à timeout.
  • send_timeout: Délai d'écriture pour les serveurs upstream (non utilisé lors de l'envoi de signaux de vie). La valeur par défaut est égale à timeout.
  • http_opts: Configurations de signal de vie HTTP. Ne fonctionne que pour typ="http".

    • query: Requête HTTP pour le signal de vie.
    • statuses: Si le code retourné par le serveur est défini sur false, alors le serveur est considéré comme échouant.
  • mode: Mode d'équilibrage. Peut être défini sur hash, url_hash ou ip_hash. Les checkups équilibreront les serveurs par hash_key, ngx.var.uri ou ngx.var.remote_addr. La valeur par défaut est wrr.

  • protected: Si défini sur true et que tous les serveurs du cluster échouent, les checkups ne marqueront pas le dernier serveur échouant comme indisponible (err), mais il sera marqué comme instable (toujours disponible lors de la prochaine tentative). La valeur par défaut est true.
  • cluster: Vous pouvez configurer plusieurs niveaux selon la priorité du cluster, à chaque niveau vous pouvez configurer un cluster de servers. Les checkups essaieront le niveau suivant uniquement lorsque tous les serveurs du niveau précédent sont considérés comme indisponibles.

    Au lieu d'essayer des clusters par niveaux, vous pouvez configurer les checkups pour essayer des clusters par clé (voir le cluster api ci-dessus). N'oubliez pas que vous devez également passer un argument supplémentaire comme opts.cluster_key={"dc1", "dc2"} ou opts.cluster_key={3, 1, 2} à checkups.read_ok pour que les checkups essaient dans l'ordre de dc1, dc2 ou niveau 3, niveau 1, niveau 2. Si vous n'avez pas passé opts.cluster_key à checkups.ready_ok, les checkups essaieront toujours des clusters par niveaux. En ce qui concerne le cluster api ci-dessus, les checkups retourneront finalement no servers available. * try: Nombre de tentatives. La valeur par défaut est le nombre de serveurs. * try_timeout: Limite le temps pendant lequel une requête peut être répondue, de même que nginx proxy_next_upstream_timeout. * servers: La configuration pour servers est listée comme suit, * weight: Définit le poids du serveur. La valeur par défaut est 1. * max_fails: Définit le nombre de tentatives infructueuses de communication avec le serveur qui doivent se produire dans la durée définie par le paramètre fail_timeout. Par défaut, le nombre de tentatives infructueuses est défini sur 0, ce qui désactive le comptage des tentatives. Ce qui est considéré comme une tentative infructueuse est défini par http_opts.statuses si typ="http" ou un nil/false retourné par checkups.ready_ok. Cette option n'est disponible qu'en round-robin. * fail_timeout: Définit le temps pendant lequel le nombre spécifié de tentatives infructueuses de communication avec le serveur doit se produire pour considérer le serveur comme indisponible et la période pendant laquelle le serveur sera considéré comme indisponible. Par défaut, le paramètre est défini sur 10 secondes. Cette option n'est disponible qu'en round-robin.

    • upstream: Nom des blocs upstream Nginx. Les checkups extrairont les serveurs des blocs upstream de la configuration Nginx dans prepare_checker. Le module lua-upstream-nginx-module est requis.
    • upstream_only_backup: Si défini sur true, les checkups n'extraireont que les serveurs de secours des blocs upstream Nginx.

Configuration Nginx

Ajoutez les chemins du fichier de configuration lua et des checkups à lua_package_path et créez des dictionnaires partagés lua utilisés par les checkups. Vous devriez mettre ces lignes dans le bloc http de votre fichier de configuration Nginx.

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

Si vous utilisez le sous-système de flux, vous devez mettre ces lignes dans le bloc stream de votre fichier de configuration 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

syntax: init(config)

phase: init_by_lua

Copie des upstreams depuis config.lua vers shdict, extrait les serveurs des blocs upstream Nginx et effectue quelques initialisations de base.

prepare_checker

syntax: prepare_checker(config)

phase: init_worker_by_lua

Copie les configurations depuis config.lua vers les checkups des workers, extrait les serveurs des blocs upstream Nginx et effectue quelques initialisations de base.

create_checker

syntax: create_checker()

phase: init_worker_by_lua

Crée un minuteur de signal de vie et un minuteur de synchronisation des upstreams. Un seul minuteur de signal de vie sera créé parmi tous les workers. Il est fortement recommandé d'appeler cette méthode dans la phase init_worker.

ready_ok

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

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

Sélectionne un peer disponible du cluster skey et appelle callback(peer.host, peer.port, opts).

La table opts accepte les champs suivants,

  • cluster_key: Essayer des clusters par cluster_key. Les checkups essaieront des clusters dans l'ordre de cluster_key. clusters_key peut être le nom des clusters ou le niveau des clusters. clusters ex: {"cluster_name_A", "name_B", "name_C"}. niveaux ex: {3, 2, 1}.
  • hash_key: Clé utilisée dans le mode d'équilibrage hash. Si non défini, ngx.var.uri sera utilisé.
  • try: Le nombre de tentatives ne sera pas supérieur à try.
  • try_timeout: Limite le temps pendant lequel une requête peut être répondue, de même que nginx proxy_next_upstream_timeout.

Retourne ce que callback retourne en cas de succès, ou retourne nil et une chaîne décrivant l'erreur sinon.

Si callback retourne nil ou false, les checkups considéreront cela comme une tentative échouée et réessaieront callback avec un autre peer. Donc, rappelez-vous toujours de ne pas retourner nil ou false après un callback réussi.

select_peer

syntax: peer, err = select_peer(skey)

context: rewrite_by_lua*, access_by_lua*, content_by_lua*, balancer_by_lua

Sélectionne un peer disponible du cluster skey.

Retourne une table contenant host et port d'un peer disponible.

En cas d'erreurs, retourne nil avec une chaîne décrivant l'erreur.

get_status

syntax: status = get_status()

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

Retourne l'état des checkups au format json.

get_ups_timeout

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

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

Retourne le délai d'attente du cluster skey.

feedback_status

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

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

Marque le serveur host:port dans le cluster skey comme échoué (true) ou disponible (false).

Retourne 1 en cas de succès, ou retourne nil et une chaîne décrivant l'erreur sinon.

update_upstream

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

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

Met à jour le cluster skey. upstream est au même format que cluster dans config.lua.

Retourne true en cas de succès, ou retourne false et une chaîne décrivant l'erreur sinon.

delete_upstream

syntax: ok, err = delete_upstream(skey)

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

Supprime le cluster skey de la liste des upstreams.

Retourne true en cas de succès, ou retourne false et une chaîne décrivant l'erreur sinon.

Voir aussi

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