Перейти к содержанию

upstream: Модуль балансировки нагрузки и резервирования соединений для nginx-module-lua

Установка

Если вы еще не настроили подписку на репозиторий RPM, зарегистрируйтесь. После этого вы можете продолжить с следующими шагами.

CentOS/RHEL 7 или 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

Чтобы использовать эту библиотеку Lua с NGINX, убедитесь, что nginx-module-lua установлен.

Этот документ описывает lua-resty-upstream v0.10, выпущенный 19 декабря 2019 года.


Модуль балансировки нагрузки и резервирования соединений

Обзор

Создайте lua разделяемый словарь. Определите свои upstream пулы и хосты в init_by_lua, это будет сохранено в разделяемом словаре.

Используйте метод connect, чтобы вернуть подключенный tcp сокет.

В качестве альтернативы передайте модуль resty (например, lua-resty-redis или lua-resty-http), который реализует connect() и set_timeout().

Вызовите process_failed_hosts, чтобы обработать неудачные хосты без блокировки текущего запроса.

Используйте resty.upstream.api, чтобы изменить конфигурацию upstream во время инициализации или выполнения, это рекомендуется!

resty.upstream.http оборачивает lua-resty-http от @pintsized.

Это позволяет выполнять резервирование на основе кодов состояния HTTP, а также статуса соединения сокета.

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 -- Переопределение только при запуске, разделяемая память сохраняется между 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?)

Возвращает новый объект upstream, используя указанное имя словаря. Когда вызывается в init_by_lua, возвращает дополнительную переменную, если словарь уже содержит конфигурацию. Принимает необязательный параметр id, который должен быть уникальным, если несколько экземпляров upstream.socket используют один и тот же словарь.

init_background_thread

syntax: ok, err = upstream:init_background_thread()

Инициализирует фоновый поток, должен быть вызван в init_worker_by_lua.

connect

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

Пытается подключиться к хосту в определенных пулах в порядке приоритета, используя выбранный метод балансировки нагрузки. Возвращает подключенный сокет и таблицу, содержащую подключенный host, poolid и pool, или nil и сообщение об ошибке.

При передаче сокета или модуля resty он вернет тот же объект после успешного подключения или nil.

Кроме того, методы хеширования могут принимать необязательный key, чтобы определить, как хешировать соединение для определения хоста. По умолчанию используется ngx.var.remote_addr. Это значение игнорируется, когда метод пула - 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()

Обрабатывает любые неудачные или восстановленные хосты из текущего запроса. Запускает немедленный обратный вызов через ngx.timer.at, не блокируя текущий запрос.

get_pools

syntax: pools = usptream:get_pools()

Возвращает таблицу, содержащую текущую конфигурацию пула и хостов. например,

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

Сохраняет таблицу пулов в разделяемом словаре, pools должен быть в том же формате, что и возвращаемый из get_pools.

sort_pools

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

Генерирует порядок приоритета в разделяемом словаре на основе предоставленной таблицы пулов.

bind

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

Привязывает функцию, которая будет вызвана при возникновении событий. func должна ожидать 1 аргумент, содержащий данные события.

Возвращает true при успешной привязке или nil и сообщение об ошибке при неудаче.

local function host_down_handler(event)
    ngx.log(ngx.ERR, "Host: ", event.host.host, ":", event.host.port, " in pool '", event.pool.id,'" is down!')
end
local ok, err = upstream:bind('host_down', host_down_handler)

Событие: host_up

Срабатывает, когда хост меняет статус с down на up. Данные события представляют собой таблицу, содержащую затронутый хост и пул.

Событие: host_down

Срабатывает, когда хост меняет статус с up на down. Данные события представляют собой таблицу, содержащую затронутый хост и пул.

upstream.api

Эти функции позволяют вам динамически перенастраивать upstream пулы и хосты.

new

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

Возвращает новый объект api, используя предоставленный объект upstream.

set_method

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

Устанавливает метод балансировки нагрузки для указанного пула. В настоящее время поддерживаются методы случайного round robin и хеширования.

create_pool

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

Создает новый пул из таблицы параметров, pool должен содержать как минимум 1 ключ id, который должен быть уникальным в текущем объекте upstream.

Другие допустимые параметры:

  • method Метод балансировки
  • timeout Таймаут соединения в мс
  • priority Пулы с более высоким приоритетом используются позже
  • read_timeout
  • keepalive_timeout
  • keepalive_pool
  • status_codes См. status_codes

Хосты не могут быть определены на этом этапе.

Примечание: Идентификаторы преобразуются в строку этой функцией.

Значения по умолчанию для пула:

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

set_priority

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

Приоритет должен быть числом, возвращает nil в случае ошибки.

add_host

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

Принимает идентификатор пула и таблицу параметров, host должен содержать как минимум host. Если идентификатор хоста не указан, он будет числовым индексом на основе количества хостов в пуле.

Примечание: Идентификаторы преобразуются в строку этой функцией.

Значения по умолчанию:

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

remove_host

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

Принимает идентификатор пула и идентификатор хоста для удаления из пула.

down_host

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

Вручную помечает хост как неработающий, этот хост не будет автоматически восстановлен.

up_host

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

Вручную восстанавливает неработающий хост в пул.

upstream.http

Функции для выполнения http-запросов к upstream хостам.

new

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

Возвращает новый http upstream объект, используя предоставленный объект upstream.

ssl_opts - это необязательная таблица для настройки поддержки SSL. * ssl установите в true, чтобы включить SSL Handshaking, по умолчанию false * ssl_verify установите в false, чтобы отключить проверку сертификата SSL, по умолчанию true * sni_host строка, используемая в качестве имени хоста sni, по умолчанию - заголовок Host запроса

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

Инициализирует фоновый поток, должен быть вызван в init_worker_by_lua.

Не вызывайте метод init_background_thread в upstream.socket, если используете фоновый поток upstream.http.

request

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

Принимает те же параметры, что и метод request lua-resty-http.

При успешном запросе возвращает объект lua-resty-http и таблицу, содержащую подключенный хост и пул.

Если запрос не удался, возвращает nil, ошибку и предложенный код состояния http.

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

Передает таймаут keepalive / пул из конфигурации пула в метод lua-resty-http set_keepalive.

get_reused_times

syntax: ok, err = upstream_http:get_reused_times()

Передает в метод lua-resty-http get_reused_times.

close

syntax: ok, err = upstream_http:close()

Передает в метод lua-resty-http close.

HTTP Healthchecks

Активные фоновые проверки состояния могут быть включены, добавив параметр healthcheck к хосту.

Значение true включит проверку по умолчанию, GET запрос для /.

Параметр healthcheck также может быть таблицей параметров, действительных для метода request lua-resty-http.

С несколькими дополнительными параметрами:

  • interval для установки времени между проверками состояния, в секундах. Должен быть >= 10с. По умолчанию 60с.
  • timeout устанавливает таймаут соединения для проверок состояния. По умолчанию устанавливается на значение пула.
  • read_timeout устанавливает таймаут чтения для проверок состояния. По умолчанию устанавливается на значение пула.
  • status_codes таблица недопустимых кодов состояния ответа. По умолчанию устанавливается на значение пула.

Неудача фоновой проверки происходит в соответствии с теми же параметрами, что и для фронтенд-запроса, если не переопределено явно.

-- Параметры пользовательской проверки
api:add_host("primary", {
     host = 123.123.123.123,
     port = 80,
     healthcheck = {
        interval = 30, -- проверка каждые 30с
        timeout      = (5*1000), -- 5с таймаут соединения
        read_timeout = (15*1000), -- 15с таймаут чтения
        status_codes = {["5xx"] = true, ["403"] = true}, -- 5xx и 403 ответы - это неудача
        -- параметры resty-http
        path = "/check",
        headers = {
            ["Host"] = "domain.com",
            ["Accept-Encoding"] = "gzip"
        }
     }
})

-- Параметры проверки по умолчанию
api:add_host("primary", {host = 123.123.123.123, port = 80, healthcheck = true})

GitHub

Вы можете найти дополнительные советы по конфигурации и документацию для этого модуля в репозитории GitHub для nginx-module-upstream.