upstream: Módulo de balanceamento de carga e failover de conexão upstream para nginx-module-lua
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-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
Para usar esta biblioteca Lua com NGINX, certifique-se de que o nginx-module-lua está instalado.
Este documento descreve lua-resty-upstream v0.10 lançado em 19 de dezembro de 2019.
Módulo de balanceamento de carga e failover de conexão upstream
Visão Geral
Crie um dicionário compartilhado lua. Defina seus pools e hosts upstream em init_by_lua, isso será salvo no dicionário compartilhado.
Use o método connect para retornar um socket tcp conectado.
Alternativamente, passe um módulo resty (por exemplo, lua-resty-redis ou lua-resty-http) que implemente connect() e set_timeout().
Chame process_failed_hosts para lidar com hosts com falha sem bloquear a solicitação atual.
Use resty.upstream.api para modificar a configuração upstream durante a inicialização ou em tempo de execução, isso é recomendado!
resty.upstream.http envolve o lua-resty-http do @pintsized.
Ele permite failover com base em códigos de status HTTP, bem como no status da conexão do 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 -- Apenas reconfigurar na inicialização, a memória compartilhada persiste através de um 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?)
Retorna um novo objeto upstream usando o nome do dicionário fornecido. Quando chamado em init_by_lua, retorna uma variável adicional se o dicionário já contiver configuração. Aceita um parâmetro id opcional, este deve ser único se várias instâncias de upstream.socket estiverem usando o mesmo dicionário.
init_background_thread
syntax: ok, err = upstream:init_background_thread()
Inicializa a thread em segundo plano, deve ser chamada em init_worker_by_lua.
connect
syntax: ok, err = upstream:connect(client?, key?)
Tenta se conectar a um host nos pools definidos na ordem de prioridade usando o método de balanceamento de carga selecionado. Retorna um socket conectado e uma tabela contendo o host, poolid e pool conectados ou nil e uma mensagem de erro.
Quando passado um socket ou módulo resty, retornará o mesmo objeto após a conexão bem-sucedida ou nil.
Além disso, métodos de hash podem aceitar uma key opcional para definir como hash a conexão para determinar o host. Por padrão, ngx.var.remote_addr é usado. Este valor é ignorado quando o método do pool é 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, 'Conectado a ' .. err.host.host .. ':' .. err.host.port)
local ok, err = redis:get('key')
process_failed_hosts
syntax: ok, err = upstream:process_failed_hosts()
Processa quaisquer hosts com falha ou recuperados da solicitação atual. Gera um callback imediato via ngx.timer.at, não bloqueia a solicitação atual.
get_pools
syntax: pools = usptream:get_pools()
Retorna uma tabela contendo a configuração atual de pools e hosts. Ex.:
{
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)
Salva uma tabela de pools no dicionário compartilhado, pools deve estar no mesmo formato que o retornado por get_pools.
sort_pools
syntax: ok, err = upstream:sort_pools(pools)
Gera uma ordem de prioridade no dicionário compartilhado com base na tabela de pools fornecida.
bind
syntax: ok, err = upstream:bind(event, func)
Vincula uma função a ser chamada quando eventos ocorrerem. func deve esperar 1 argumento contendo os dados do evento.
Retorna true em um vínculo bem-sucedido ou nil e uma mensagem de erro em caso de falha.
local function host_down_handler(event)
ngx.log(ngx.ERR, "Host: ", event.host.host, ":", event.host.port, " no pool '", event.pool.id,'" está fora do ar!')
end
local ok, err = upstream:bind('host_down', host_down_handler)
Evento: host_up
Disparado quando um host muda de status de fora do ar para em funcionamento. Os dados do evento são uma tabela contendo o host e o pool afetados.
Evento: host_down
Disparado quando um host muda de status de em funcionamento para fora do ar. Os dados do evento são uma tabela contendo o host e o pool afetados.
upstream.api
Essas funções permitem que você reconfigure dinamicamente pools e hosts upstream.
new
syntax: api, err = upstream_api:new(upstream)
Retorna um novo objeto api usando o objeto upstream fornecido.
set_method
syntax: ok, err = api:set_method(poolid, method)
Define o método de balanceamento de carga para o pool especificado. Atualmente, métodos de round robin aleatório e hashing são suportados.
create_pool
syntax: ok, err = api:create_pool(pool)
Cria um novo pool a partir de uma tabela de opções, pool deve conter pelo menos 1 chave id que deve ser única dentro do objeto upstream atual.
Outras opções válidas são:
methodMétodo de balanceamentotimeoutTempo limite de conexão em mspriorityPools de maior prioridade são usados posteriormenteread_timeoutkeepalive_timeoutkeepalive_poolstatus_codesVeja status_codes
Hosts não podem ser definidos neste ponto.
Nota: IDs são convertidos para uma string por esta função.
Valores padrão do pool:
{ method = 'round_robin', timeout = 2000, priority = 0 }
set_priority
syntax: ok, err = api:set_priority(poolid, priority)
A prioridade deve ser um número, retorna nil em caso de erro.
add_host
syntax: ok, err = api:add_host(poolid, host)
Aceita um ID de pool e uma tabela de opções, host deve conter pelo menos host. Se o ID do host não for especificado, será um índice numérico com base no número de hosts no pool.
Nota: IDs são convertidos para uma string por esta função.
Padrões:
{ host = '', port = 80, weight = 0}
remove_host
syntax: ok, err = api:remove_host(poolid, host)
Aceita um poolid e um hostid para remover do pool.
down_host
syntax: ok,err = api:down_host(poolid, host)
Marca manualmente um host como fora do ar, este host não será recuperado automaticamente.
up_host
syntax: ok,err = api:up_host(poolid, host)
Restaura manualmente um host inativo para o pool.
upstream.http
Funções para fazer requisições http para hosts upstream.
new
syntax: httpc, err = upstream_http:new(upstream, ssl_opts?)
Retorna um novo objeto http upstream usando o objeto upstream fornecido.
ssl_opts é uma tabela opcional para configurar o suporte a SSL.
* ssl definido como true para habilitar o handshake SSL, padrão false
* ssl_verify definido como false para desativar a verificação do certificado SSL, padrão true
* sni_host uma string a ser usada como o hostname sni, padrão é o cabeçalho Host da requisição.
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()
Inicializa a thread em segundo plano, deve ser chamada em init_worker_by_lua.
Não chame o método init_background_thread em upstream.socket se estiver usando a thread em segundo plano upstream.http.
request
syntax: res, err_or_conn_info, status? = upstream_api:request(params)
Aceita os mesmos parâmetros que o método request do lua-resty-http.
Em uma solicitação bem-sucedida, retorna o objeto lua-resty-http e uma tabela contendo o host e o pool conectados.
Se a solicitação falhar, retorna nil, o erro e um código de status http sugerido.
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()
Passa o tempo limite de keepalive / pool da configuração do pool para o método set_keepalive do lua-resty-http.
get_reused_times
syntax: ok, err = upstream_http:get_reused_times()
Passa para o método get_reused_times do lua-resty-http.
close
syntax: ok, err = upstream_http:close()
Passa para o método close do lua-resty-http.
Verificações de Saúde HTTP
Verificações de saúde ativas em segundo plano podem ser habilitadas adicionando o parâmetro healthcheck a um host.
Um valor de true habilitará a verificação padrão, uma requisição GET para /.
O parâmetro healthcheck também pode ser uma tabela de parâmetros válidos para o método request do lua-resty-http.
Com alguns parâmetros adicionais:
intervalpara definir o tempo entre as verificações de saúde, em segundos. Deve ser >= 10s. O padrão é 60s.timeoutdefine o tempo limite de conexão para as verificações de saúde. O padrão é a configuração do pool.read_timeoutdefine o tempo limite de leitura para as verificações de saúde. O padrão é a configuração do pool.status_codesuma tabela de códigos de status de resposta inválidos. O padrão é a configuração do pool.
A falha da verificação em segundo plano é de acordo com os mesmos parâmetros que para uma solicitação de frontend, a menos que seja explicitamente substituída.
-- Parâmetros de verificação personalizados
api:add_host("primary", {
host = 123.123.123.123,
port = 80,
healthcheck = {
interval = 30, -- verificar a cada 30s
timeout = (5*1000), -- 5s de tempo limite de conexão
read_timeout = (15*1000), -- 15s de tempo limite de leitura
status_codes = {["5xx"] = true, ["403"] = true}, -- respostas 5xx e 403 são uma falha
-- parâmetros resty-http
path = "/check",
headers = {
["Host"] = "domain.com",
["Accept-Encoding"] = "gzip"
}
}
})
-- Parâmetros de verificação padrão
api:add_host("primary", {host = 123.123.123.123, port = 80, healthcheck = true})
GitHub
Você pode encontrar dicas adicionais de configuração e documentação para este módulo no repositório GitHub para nginx-module-upstream.