Pular para conteúdo

checkups: Gerenciar upstreams do NGINX em Lua pura

Instalação

Se você ainda não configurou a assinatura do repositório RPM, inscreva-se. Em seguida, você pode prosseguir com os seguintes passos.

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

Para usar esta biblioteca Lua com o NGINX, certifique-se de que o nginx-module-lua esteja instalado.

Este documento descreve lua-resty-checkups v0.1 lançado em 01 de fevereiro de 2019.


  • Batimentos cardíacos periódicos para servidores upstream
  • Verificação de saúde proativa e passiva
  • Atualização dinâmica de upstream
  • Balanceamento por round-robin ponderado ou hash consistente
  • Sincronizar com blocos upstream do Nginx
  • Tentar clusters por níveis ou por chaves

Sinopse

    -- 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 um servidor inativo, nenhum upstream disponível
                ok, err = checkups.ready_ok("ups1", callback)
                if err then ngx.say(err) end

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

                -- adicionar servidor a novo 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

                -- adicionar servidor a ups2, redefinir 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
            }
        }
    }

Uma saída típica da localização /t definida acima é:

no servers available
12350
12350
12351
12350
12351

Configuração

Configuração Lua

O arquivo de configuração de checkups é um módulo lua que consiste em duas partes, a parte global e a parte do cluster.

Um exemplo de arquivo de configuração de checkups é mostrado abaixo,

    -- config.lua

    -- Aqui está a 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,
    }

    -- As partes restantes são configurações do cluster

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

        protected = true,

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

    return _M

configurações globais

  • checkup_timer_interval: Intervalo de envio de batimentos cardíacos para servidores de backend. O padrão é 5.
  • checkup_timer_overtime: Intervalo de checkups para expirar a chave do timer. Na maioria dos casos, você não precisa alterar esse valor. O padrão é 60.
  • default_heartbeat_enable: Checkups enviarão batimentos cardíacos para servidores por padrão ou não. O padrão é true.
  • checkup_shd_sync_enable: Criar sincronizador de upstream para cada worker. Se definido como false, o upstream dinâmico não funcionará corretamente. O padrão é true.
  • shd_config_timer_interval: Intervalo de sincronização da lista de upstream da memória compartilhada. O padrão é igual a checkup_timer_interval.
  • ups_status_sync_enable: Se definido como true, checkups sincronizarão o status do upstream de checkups para os blocos upstream do Nginx. O padrão é false.
  • ups_status_timer_interval: Intervalo de sincronização do status do upstream de checkups para os blocos upstream do Nginx.

Configurações do Cluster

  • skey: _M.xxxxx. xxxxx é a skey (chave de serviço) deste Cluster.
  • enable: Habilitar ou desabilitar batimentos cardíacos para servidores. O padrão é true.
  • typ: Tipo de cluster, deve ser um dos general, redis, mysql, http. O padrão é general.
    • general: Batimento por TCP sock:connect.
    • redis: Batimento por redis PING. O módulo lua-resty-redis é necessário.
    • mysql: Batimento por mysql db:connect. O módulo lua-resty-mysql é necessário.
    • http: Batimento por requisição HTTP. Você pode configurar requisições HTTP personalizadas e códigos de resposta em http_opts.
  • timeout: Tempo limite de conexão com servidores upstream. O padrão é 5.
  • read_timeout: Tempo limite de leitura para servidores upstream (não utilizado durante o batimento). O padrão é igual a timeout.
  • send_timeout: Tempo limite de escrita para servidores upstream (não utilizado durante o batimento). O padrão é igual a timeout.
  • http_opts: Configurações de batimento HTTP. Funciona apenas para typ="http".

    • query: Requisição HTTP para batimento.
    • statuses: Se o código retornado pelo servidor estiver definido como false, então o servidor é considerado como falhando.
  • mode: Modo de balanceamento. Pode ser definido como hash, url_hash ou ip_hash. Checkups irá balancear servidores por hash_key, ngx.var.uri ou ngx.var.remote_addr. O padrão é wrr.

  • protected: Se definido como true e todos os servidores no cluster estiverem falhando, checkups não marcará o último servidor falhando como indisponível (err), em vez disso, será marcado como instável (ainda disponível na próxima tentativa). O padrão é true.
  • cluster: Você pode configurar múltiplos níveis de acordo com a prioridade do cluster, em cada nível você pode configurar um cluster de servers. Checkups tentará o próximo nível apenas quando todos os servidores no nível anterior forem considerados indisponíveis.

    Em vez de tentar clusters por níveis, você pode configurar checkups para tentar clusters por chave (veja o cluster api acima). Lembre-se de que você também deve passar um argumento extra como opts.cluster_key={"dc1", "dc2"} ou opts.cluster_key={3, 1, 2} para checkups.read_ok para fazer checkups tentarem na ordem de dc1, dc2 ou nível 3, nível 1, nível 2. Se você não passar opts.cluster_key para checkups.ready_ok, checkups ainda tentará clusters por níveis. Quanto ao cluster api acima, checkups eventualmente retornará no servers available. * try: Contagem de tentativas. O padrão é o número de servidores. * try_timeout: Limita o tempo durante o qual uma requisição pode ser respondida, assim como proxy_next_upstream_timeout do nginx. * servers: A configuração para servers é listada a seguir, * weight: Define o peso do servidor. O padrão é 1. * max_fails: Define o número de tentativas malsucedidas de comunicação com o servidor que devem ocorrer durante o período definido pelo parâmetro fail_timeout. Por padrão, o número de tentativas malsucedidas é definido como 0, o que desabilita a contagem de tentativas. O que é considerado uma tentativa malsucedida é definido por http_opts.statuses se typ="http" ou um nil/false retornado por checkups.ready_ok. Esta opção está disponível apenas no round-robin. * fail_timeout: Define o tempo durante o qual o número especificado de tentativas malsucedidas de comunicação com o servidor deve ocorrer para considerar o servidor indisponível e o período de tempo que o servidor será considerado indisponível. Por padrão, o parâmetro é definido como 10 segundos. Esta opção está disponível apenas no round-robin.

    • upstream: Nome dos blocos upstream do Nginx. Checkups irá extrair servidores dos blocos upstream da configuração do Nginx em prepare_checker. O módulo lua-upstream-nginx-module é necessário.
    • upstream_only_backup: Se definido como true, checkups irá extrair apenas servidores de backup dos blocos upstream do Nginx.

Configuração do Nginx

Adicione os caminhos do arquivo de configuração lua e checkups ao lua_package_path e crie dicionários compartilhados lua usados por checkups. Você deve colocar essas linhas no bloco http do seu arquivo de configuração do Nginx.

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

Se você usar o subsistema de stream, deve colocar essas linhas no bloco stream do seu arquivo de configuração do 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

sintaxe: init(config)

fase: init_by_lua

Copia upstreams de config.lua para shdict, extrai servidores dos blocos upstream do Nginx e faz algumas inicializações básicas.

prepare_checker

sintaxe: prepare_checker(config)

fase: init_worker_by_lua

Copia configurações de config.lua para os checkups do worker, extrai servidores dos blocos upstream do Nginx e faz algumas inicializações básicas.

create_checker

sintaxe: create_checker()

fase: init_worker_by_lua

Cria um timer de batimento e um timer de sincronização de upstream. Apenas um timer de batimento será criado entre todos os workers. É altamente recomendável chamar este método na fase init_worker.

ready_ok

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

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

Seleciona um peer disponível do cluster skey e chama callback(peer.host, peer.port, opts).

A tabela opts aceita os seguintes campos,

  • cluster_key: Tentar clusters por cluster_key. Checkups tentará clusters na ordem de cluster_key. clusters_key pode ser o nome dos clusters ou o nível dos clusters. clusters ex: {"cluster_name_A", "name_B", "name_C"}. níveis ex: {3, 2, 1}.
  • hash_key: Chave usada no modo de balanceamento hash. Se não definido, ngx.var.uri será usado.
  • try: A tentativa não será maior que try vezes.
  • try_timeout: Limita o tempo durante o qual uma requisição pode ser respondida, assim como proxy_next_upstream_timeout do nginx.

Retorna o que callback retorna em caso de sucesso, ou retorna nil e uma string descrevendo o erro caso contrário.

Se callback retornar nil ou false, checkups considerará isso como uma tentativa falha e tentará novamente callback com outro peer. Portanto, sempre lembre-se de não retornar nil ou false após um callback bem-sucedido.

select_peer

sintaxe: peer, err = select_peer(skey)

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

Seleciona um peer disponível do cluster skey.

Retorna uma tabela contendo host e port de um peer disponível.

Em caso de erros, retorna nil com uma string descrevendo o erro.

get_status

sintaxe: status = get_status()

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

Retorna o status dos checkups em formato json.

get_ups_timeout

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

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

Retorna o tempo limite do cluster skey.

feedback_status

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

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

Marca o servidor host:port no cluster skey como falhado (true) ou disponível (false).

Retorna 1 em caso de sucesso, ou retorna nil e uma string descrevendo o erro caso contrário.

update_upstream

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

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

Atualiza o cluster skey. upstream está no mesmo formato que cluster em config.lua.

Retorna true em caso de sucesso, ou retorna false e uma string descrevendo o erro caso contrário.

delete_upstream

sintaxe: ok, err = delete_upstream(skey)

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

Deleta o cluster skey da lista de upstream.

Retorna true em caso de sucesso, ou retorna false e uma string descrevendo o erro caso contrário.

Veja Também

GitHub

Você pode encontrar dicas adicionais de configuração e documentação para este módulo no repositório do GitHub para nginx-module-checkups.