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

checkups: Управление upstream-ами NGINX на чистом 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-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

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

Этот документ описывает lua-resty-checkups v0.1, выпущенный 1 февраля 2019 года.


  • Периодическая отправка heartbeat на upstream-серверы
  • Проактивная и пассивная проверка состояния
  • Динамическое обновление upstream
  • Балансировка по взвешенному круговому или консистентному хешу
  • Синхронизация с блоками upstream Nginx
  • Попытки кластеров по уровням или по ключам

Синопсис

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

                -- подключение к неработающему серверу, upstream недоступен
                ok, err = checkups.ready_ok("ups1", callback)
                if err then ngx.say(err) end

                -- добавление сервера в 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

                -- добавление сервера в новый 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

                -- добавление сервера в ups2, сброс состояния 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
            }
        }
    }

Типичный вывод расположения /t, определенного выше:

no servers available
12350
12350
12351
12350
12351

Конфигурация

Конфигурация Lua

Конфигурационный файл checkups — это модуль lua, состоящий из двух частей: глобальной части и части кластера.

Пример конфигурационного файла checkups показан ниже:

    -- config.lua

    -- Вот глобальная часть

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

    -- Остальные части — это конфигурации кластера

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

        protected = true,

        cluster = {
            {   -- уровень 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 },
                }
            },
            {   -- уровень 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 = {
            {   -- уровень 1
                upstream = "api.com",
            },
            {   -- уровень 2
                upstream = "api.com",
                upstream_only_backup = true,
            },
        },
    }

    return _M

Глобальные конфигурации

  • checkup_timer_interval: Интервал отправки heartbeat на серверы. По умолчанию 5.
  • checkup_timer_overtime: Интервал проверки состояния для истечения ключа таймера. В большинстве случаев вам не нужно изменять это значение. По умолчанию 60.
  • default_heartbeat_enable: Проверки состояния будут отправлять heartbeat на серверы по умолчанию или нет. По умолчанию true.
  • checkup_shd_sync_enable: Создать синхронизатор upstream для каждого worker. Если установить в false, динамический upstream не будет работать должным образом. По умолчанию true.
  • shd_config_timer_interval: Интервал синхронизации списка upstream из общей памяти. По умолчанию равен checkup_timer_interval.
  • ups_status_sync_enable: Если установить в true, проверки состояния будут синхронизировать статус upstream из проверок состояния в блоки upstream Nginx. По умолчанию false.
  • ups_status_timer_interval: Интервал синхронизации статуса upstream из проверок состояния в блоки upstream Nginx.

Конфигурации кластера

  • skey: _M.xxxxx. xxxxx — это skey (ключ сервиса) этого кластера.
  • enable: Включить или отключить heartbeat на серверы. По умолчанию true.
  • typ: Тип кластера, должен быть одним из general, redis, mysql, http. По умолчанию general.
    • general: Heartbeat по TCP sock:connect.
    • redis: Heartbeat по redis PING. Модуль lua-resty-redis требуется.
    • mysql: Heartbeat по mysql db:connect. Модуль lua-resty-mysql требуется.
    • http: Heartbeat по HTTP-запросу. Вы можете настроить пользовательский HTTP-запрос и коды ответов в http_opts.
  • timeout: Таймаут подключения к upstream-серверам. По умолчанию 5.
  • read_timeout: Таймаут чтения для upstream-серверов (не используется во время heartbeat). По умолчанию равен timeout.
  • send_timeout: Таймаут записи для upstream-серверов (не используется во время heartbeat). По умолчанию равен timeout.
  • http_opts: Конфигурации HTTP heartbeat. Работает только для typ="http".

    • query: HTTP-запрос для heartbeat.
    • statuses: Если код, возвращаемый сервером, установлен в false, то сервер считается неработающим.
  • mode: Режим балансировки. Может быть установлен в hash, url_hash или ip_hash. Проверки состояния будут балансировать серверы по hash_key, ngx.var.uri или ngx.var.remote_addr. По умолчанию wrr.

  • protected: Если установить в true и все серверы в кластере не работают, проверки состояния не будут помечать последний неработающий сервер как недоступный (err), вместо этого он будет помечен как unstable (все еще доступен при следующей попытке). По умолчанию true.
  • cluster: Вы можете настроить несколько уровней в зависимости от приоритета кластера, на каждом уровне вы можете настроить кластер servers. Проверки состояния будут пытаться следующий уровень только тогда, когда все серверы в предыдущем уровне считаются недоступными.

    Вместо попыток кластеров по уровням, вы можете настроить проверки состояния, пытаясь кластеры по ключу (см. кластер api выше). Помните, что вы также должны передать дополнительный аргумент, такой как opts.cluster_key={"dc1", "dc2"} или opts.cluster_key={3, 1, 2} в checkups.read_ok, чтобы проверки состояния пытались в порядке dc1, dc2 или уровень 3, уровень 1, уровень 2. Если вы не передали opts.cluster_key в checkups.ready_ok, проверки состояния все равно будут пытаться кластеры по уровням. Что касается вышеупомянутого кластера api, проверки состояния в конечном итоге вернут no servers available. * try: Количество попыток повторной попытки. По умолчанию равно количеству серверов. * try_timeout: Ограничивает время, в течение которого запрос может быть обработан, аналогично proxy_next_upstream_timeout nginx. * servers: Конфигурация для servers перечислена следующим образом: * weight: Устанавливает вес сервера. По умолчанию 1. * max_fails: Устанавливает количество неудачных попыток связи с сервером, которые должны произойти в течение времени, установленного параметром fail_timeout. По умолчанию количество неудачных попыток установлено в 0, что отключает учет попыток. Что считается неудачной попыткой, определяется http_opts.statuses, если typ="http" или nil/false, возвращаемый checkups.ready_ok. Эта опция доступна только в круговом режиме. * fail_timeout: Устанавливает время, в течение которого должно произойти указанное количество неудачных попыток связи с сервером, чтобы считать сервер недоступным, и период времени, в течение которого сервер будет считаться недоступным. По умолчанию параметр установлен на 10 секунд. Эта опция доступна только в круговом режиме.

    • upstream: Имя блоков upstream Nginx. Проверки состояния извлекут серверы из блоков upstream конфигурации Nginx в prepare_checker. Модуль lua-upstream-nginx-module требуется.
    • upstream_only_backup: Если установить в true, проверки состояния будут извлекать только резервные серверы из блоков upstream Nginx.

Конфигурация Nginx

Добавьте пути к файлу конфигурации lua и проверкам состояния в lua_package_path и создайте общие словари lua, используемые проверками состояния. Вы должны поместить эти строки в блок http вашего конфигурационного файла Nginx.

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

Если вы используете подсистему stream, вы должны поместить эти строки в блок stream вашего конфигурационного файла 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

синтаксис: init(config)

фаза: init_by_lua

Копирует upstream-ы из config.lua в shdict, извлекает серверы из блоков upstream Nginx и выполняет некоторые базовые инициализации.

prepare_checker

синтаксис: prepare_checker(config)

фаза: init_worker_by_lua

Копирует конфигурации из config.lua в worker проверки состояния, извлекает серверы из блоков upstream Nginx и выполняет некоторые базовые инициализации.

create_checker

синтаксис: create_checker()

фаза: init_worker_by_lua

Создает таймер heartbeat и таймер синхронизации upstream. Будет создан только один таймер heartbeat среди всех workers. Настоятельно рекомендуется вызывать этот метод в фазе init_worker.

ready_ok

синтаксис: res, err = ready_ok(skey, callback, opts?)

фаза: rewrite_by_lua*, access_by_lua*, content_by_lua*, ngx.timer.*

Выбирает доступный peer из кластера skey и вызывает callback(peer.host, peer.port, opts).

Таблица opts принимает следующие поля:

  • cluster_key: Пытаться кластеры по cluster_key. Проверки состояния будут пытаться кластеры в порядке cluster_key. clusters_key может быть именем кластеров или уровнем кластеров. кластеры, например: {"cluster_name_A", "name_B", "name_C"}. уровни, например: {3, 2, 1}.
  • hash_key: Ключ, используемый в режиме балансировки hash. Если не установлен, будет использоваться ngx.var.uri.
  • try: Повторные попытки не будут превышать try раз.
  • try_timeout: Ограничивает время, в течение которого запрос может быть обработан, аналогично proxy_next_upstream_timeout nginx.

Возвращает то, что возвращает callback при успехе, или возвращает nil и строку, описывающую ошибку в противном случае.

Если callback возвращает nil или false, проверки состояния будут считать это неудачной попыткой и повторят callback с другим peer. Поэтому всегда помните, что не следует возвращать nil или false после успешного вызова callback.

select_peer

синтаксис: peer, err = select_peer(skey)

контекст: rewrite_by_lua*, access_by_lua*, content_by_lua*, balancer_by_lua

Выбирает доступный peer из кластера skey.

Возвращает таблицу, содержащую host и port доступного peer.

В случае ошибок возвращает nil с строкой, описывающей ошибку.

get_status

синтаксис: status = get_status()

фаза: rewrite_by_lua*, access_by_lua*, content_by_lua*, ngx.timer.*

Возвращает статус проверок состояния в формате json.

get_ups_timeout

синтаксис: connect_timeout, send_timeout, read_timeout = get_ups_timeout(skey)

фаза: rewrite_by_lua*, access_by_lua*, content_by_lua*, ngx.timer.*

Возвращает таймаут кластера skey.

feedback_status

синтаксис: ok, err = feedback_status(skey, host, port, failed)

контекст: rewrite_by_lua*, access_by_lua*, content_by_lua*, ngx.timer.*, balancer_by_lua.*

Помечает сервер host:port в кластере skey как неработающий (true) или доступный (false).

Возвращает 1 при успехе, или возвращает nil и строку, описывающую ошибку в противном случае.

update_upstream

синтаксис: ok, err = update_upstream(skey, upstream)

фаза: rewrite_by_lua*, access_by_lua*, content_by_lua*, ngx.timer.*

Обновляет кластер skey. upstream в том же формате, что и cluster в config.lua.

Возвращает true при успехе, или возвращает false и строку, описывающую ошибку в противном случае.

delete_upstream

синтаксис: ok, err = delete_upstream(skey)

фаза: rewrite_by_lua*, access_by_lua*, content_by_lua*, ngx.timer.*

Удаляет кластер skey из списка upstream.

Возвращает true при успехе, или возвращает false и строку, описывающую ошибку в противном случае.

См. также

GitHub

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