Не судите строго, это моя первая статья, наверное если бы я был гуру Nginx и "Линуха", то скорее всего боли и страданий бы не было.

С чего все началось?

Одним днем мне понадобилось реализовать довольно не тривиальную задачу:

Есть множество сервисов с которых нужно собирать данные для обработки и дальнейшей аналитики, модуль который это все собирает может быть установлен на множество серверов (пока 40, но в горизонте года это 1000), но хочется чтобы все обращения от этих серверов шли на один ip , а с него уже распределялись в зависимости от типа запроса или конечной точки обращения. Условно мы обращаемся к серваку 100.1.2.101 по порту 8080 и просим от него данные о всех домах на определенной территории ,он в свою очередь по заданному сценарию коннектится к определенному proxy (Допустим squid, он нужен так как некоторые api залочены по ip) и через него получает данные из конечного api.

P.S. Данные нельзя хранить на промежуточном сервере, так как они слишком часто обновляются :(

В итоге я решил эту задачу разделить на несколько этапов

  1. Создать proxy сервер

  2. Создать возможность балансировки нагрузки (мне казалось ,что если получиться сделать proxy который будет принимать запрос и пересылать допустим на 300 других proxy, то уже пол дела будет готово)

  3. Создать логику выбора конкретного Proxy. (Я думал ,что это просто , я сильно ошибался, но об этом позже)

Этап 1: Создать proxy сервер (3proxy)

Для начала нужно смириться ,что мне нельзя использовать как ОС Windows, а в линухах я как макака с гранатой. После дня анализа мой выбор пал на 3proxy как на испытуемого и beget как VPS для места тестирования (Vps там стоит от 7р в день, что для тестов очень гуманно) и пак в 300 прокси на стороннем ресурсе, все они залочены на один ip (в инете их множество можете подобрать себе по карману, мне обошлось в 30$, адрес не скажу так как боюсь оно ляжет от хабраэффекта), ОС выбрал ubuntu 20.04 (просто под нее много материалов и статей) и MobaXterm для удобства подключения по SSH (скачать можно с оф сайта или всем известным способом????‍☠️ на просторах интернета).

3proxy маленький и много умеет, а главное в нем есть parent который позволит мне в дальнейшем обращаться к другим. Итак установка 3proxy:

Обновим пакеты

sudo apt update
sudo apt upgrade

Установим 3proxy

wget https://github.com/z3APA3A/3proxy/releases/download/0.9.4/3proxy-0.9.4.x86_64.deb; dpkg -i 3proxy-0.9.4.x86_64.deb

И все, оно поставилось.

теперь идем править конфиги: /usr/local/3proxy/conf/3proxy.cfg (я это делал в MobaXterm, мне так удобнее)

nscache 65536
nserver 8.8.8.8
nserver 8.8.4.4

config /conf/3proxy.cfg
monitor /conf/3proxy.cfg

log /logs/3proxy-%y%m%d.log D
logformat "%d-%m-%Y %H:%M:%S %U %C:%c %R:%r %O %I %T"
rotate 60
counter /count/3proxy.3cf

users $/conf/passwd

include /conf/counters
include /conf/bandlimiters


nscache 65536
timeouts 1 5 30 60 180 1800 15 60

auth strong
allow *
proxy -n -p4888 -a

flush
allow *
proxy -n -p4890 -a

flush
allow proxyuser
admin -p8080

Тут 3 открытых порта на 4888 и 4890 proxy ,а на 8080 админка с доступом только для пользователя proxyuser

Но теперь нам нужно создать proxyuser для этого нужно вызвать /usr/local/3proxy/conf/add3proxyuser.sh <логин> <пароль> <дневной_лимит_трафика_в_МБ> <ограничение_скорости_в_битах_в_секунду> , но если попробовать сделать это сразу то получим Permission denied, для начало поправим права

cd /usr/local/3proxy/conf/
chmod ug+rwx add3proxyuser.sh
/usr/local/3proxy/conf/add3proxyuser.sh proxyuser proxypass

Мы создали пользователя proxyuser с паролем proxypass. Теперь запускаем 3proxy

systemctl enable 3proxy && service 3proxy start && netstat -tlpn | grep prox

Если какой-то из портов не слушается, то прописываем

ufw allow 4888
ufw allow 4890
ufw allow 8080

Теперь проверим Админку зайдя на ip_Сервера:8080 и введя логин proxyuser и пароль proxypass (ну или то ,что мы указали)

админка
админка

Админка работает, уже хорошо (я ей не пользовался ,но вроде конфиги можно из нее править). Но нас интересует не это :) Попробуем сделать curl запрос

curl -L -I -x http://proxyuser:proxypass@IP_Сервера:4888 https://habr.com

Таки работает, Итак мы получили тот результат который хотели и можно сворачиваться ?! А вот и нет :<

Мы просто поставили 3proxy и настроили его для работы, теперь нужно научить его работать с другими серверами (на тестах это будут proxy его проще всего проверить). Делаем запрос и смотрим что вернет нам сервер по определению IP

curl -L -x http://proxyuser:proxypass@IP_Сервера:4888 https://myip.ru/index_small.php

А вернул он нам IP_Сервера (Помечен зеленым на скрине)

Этап 2 : Создать возможность балансировки нагрузки (3proxy)

У 3proxy есть возможность создавать перенаправления она описана в документации , но есть ряд нюансов(ну или я просто тупой и не сразу до них докумекал). Для теста нам понадобиться какой нибудь proxy server(ага для теста proxy Нужен прокси, вы можете его поднять на другой машине или просто купить на просторах интернета: я выбрал второй вариант).

Итак, у нас есть IP_proxy_Сервера_2 (Красный на скринах) который работает по порту 8085(или другому) и не имеет авторизацию (логин пароль) или имеет (это не принципиально). Для начала нам нужно поменять конфиг файл /usr/local/3proxy/conf/3proxy.cfg и добавить

parent <weight> <type> <ip> <port> <username> <password>

  • weight - вес или вероятность выбора этого Ip,вес прокси,

  • type - тип прокси (tcp - перенаправление соединения, может быть только последним в цепочке, http - синоним tcp, connect - HTTP CONNECT/HTTPS прокси, socks4 - SOCKSv4 прокси, socks5 - SOCKSv5 прокси),

  • ip - IP адрес прокси,

  • port - порт прокси,

  • username - имя для авторизации на прокси ,

  • password - пароль для авторизации на прокси.

    Логин и пароль можно не указывать если сервер их не требует. Добавим строку parent 1000 connect IP_proxy_Сервера_2 8085 для порта 4890 (внимание ip и порт разделяются пробелом а не ":" как обычно )

nscache 65536
nserver 8.8.8.8
nserver 8.8.4.4

config /conf/3proxy.cfg
monitor /conf/3proxy.cfg

log /logs/3proxy-%y%m%d.log D
logformat "%d-%m-%Y %H:%M:%S %U %C:%c %R:%r %O %I %T"
rotate 60
counter /count/3proxy.3cf

users $/conf/passwd

include /conf/counters
include /conf/bandlimiters


nscache 65536
timeouts 1 5 30 60 180 1800 15 60

auth strong
allow *
proxy -n -p4888 -a

allow *
parent 1000 connect IP_proxy_Сервера_2 8085
proxy -n -p4890 -a

flush
allow proxyuser
admin -p8080

Перезапускаем 3proxy

systemctl restart 3proxy

При перезапуске сервак немного задумывается на секунд 5 (если этого не произошло, то скорее всего в конфиге ошибка)

Теперь проверяем через curl

curl -L -x http://proxyuser:proxypass@ip_Сервера:4888 https://myip.ru/index_small.php
curl -L -x http://proxyuser:proxypass@ip_Сервера:4890 https://myip.ru/index_small.php

Поздравляю при запросе к серверу по порту 4888 возвращает ip_Сервера (Зеленый на скрине), а при запросе по порту 4890 возвращает IP_proxy_Сервера_2 (Красный на скрине).

Теперь мы можем управлять нагрузкой добавляя новые proxy сервера (IP_proxy_Сервера_3, IP_proxy_Сервера_4, IP_proxy_Сервера_5 .... IP_proxy_Сервера_251) через конструкцию parent <weight> <type> <ip> <port> <username> <password>

пример конфига :

nscache 65536
nserver 8.8.8.8
nserver 8.8.4.4

config /conf/3proxy.cfg
monitor /conf/3proxy.cfg

log /logs/3proxy-%y%m%d.log D
logformat "%d-%m-%Y %H:%M:%S %U %C:%c %R:%r %O %I %T"
rotate 60
counter /count/3proxy.3cf

users $/conf/passwd

include /conf/counters
include /conf/bandlimiters


nscache 65536
timeouts 1 5 30 60 180 1800 15 60

auth strong
allow *
proxy -n -p4888 -a

flush
allow *
parent 4 connect IP_proxy_Сервера_2 8085
parent 4 connect IP_proxy_Сервера_3 8085
parent 4 connect IP_proxy_Сервера_4 8085
parent 4 connect IP_proxy_Сервера_5 8085
parent 4 connect IP_proxy_Сервера_6 8085
parent 4 connect IP_proxy_Сервера_7 8085
...
parent 4 connect IP_proxy_Сервера_251 8085

proxy -n -p4890 -a

flush
allow proxyuser
admin -p8080

После каждого изменения конфига не забудьте перезагрузить сервер

systemctl restart 3proxy

Разработчик утверждает :

Вес (от 1 до 1000) задается для каждого прокси. Сумма весов по всем перенаправлениям должна быть кратна 1000. Прокси с весами до 1000 группируются, и при построении цепочки один из них выбирается случайно согласно весу. Длина цепочки определяется из суммарного веса. Например, если суммарный вес цепочки 3000, в цепочке будет 3 звена (хопа). 

Но есть нюанс, я пробывал ставить вес 1000 всем, оно так не работает! Я не просто так указал "parent 4 connect IP_proxy_Сервера_7 8085" где 4 это <weight> (вес), сумма всех <weight> (весов) должна равняться 1000. Допустим у вас 300 серверов и вы хотите их применять с равной вероятностью, забудьте так не получится потому что если разделить 1000 на 300 вы получите не целое число, то есть вы можете использовать 250 (вес 4) или 500(вес 2) или 1000(вес 1).

P.S. Возможно поправят или уже поправили, но на момент написания статьи оно работало так )

Этап 3 :Создать логику выбора конкретного Proxy.

Вот тут я столкнулся с «суровой действительностью», 3proxy не умеет создавать сложные сценарии для управления нагрузкой :(

Как вариант можно разбить на разные порты и каждый порт отправлять на свои прокси. Пример конфига ниже

nscache 65536
nserver 8.8.8.8
nserver 8.8.4.4

config /conf/3proxy.cfg
monitor /conf/3proxy.cfg

log /logs/3proxy-%y%m%d.log D
logformat "%d-%m-%Y %H:%M:%S %U %C:%c %R:%r %O %I %T"
rotate 60
counter /count/3proxy.3cf

users $/conf/passwd

include /conf/counters
include /conf/bandlimiters


nscache 65536
timeouts 1 5 30 60 180 1800 15 60

auth strong
allow *
proxy -n -p4888 -a

flush
allow *
parent 500 connect IP_proxy_Сервера_2 8085
parent 500 connect IP_proxy_Сервера_3 8085
proxy -n -p4890 -a

flush
allow *
parent 500 connect IP_proxy_Сервера_4 8085
parent 500 connect IP_proxy_Сервера_5 8085
proxy -n -p4891 -a


flush
allow *
parent 500 connect IP_proxy_Сервера_6 8085
parent 500 connect IP_proxy_Сервера_7 8085
proxy -n -p4892 -a

flush
allow proxyuser
admin -p8080

Но это не решение, а костыль. Буду копать дальше в сторону Nginx forward proxy (статья по нему уже пишется)

Всем спасибо, за критику буду благодарен :-)

UPD. Народ пишет ,что Nginx не умеет в Forward proxy и что на нем такую задачу не решить. Хотелось внести ясности:

У Nginx есть модули для forward proxy и модули lua для обработки , ясно что на стандарте никто делать не будет, а будет собирать Nginx отдельно для этого, именно этим я сейчас и занят

Модули:

https://github.com/chobits/ngx_http_proxy_connect_module

https://www.nginx.com/resources/wiki/modules/lua/

Комментарии (33)


  1. Bkmz
    17.06.2023 21:27

    Рекомендую посмотреть на https://github.com/nadoo/glider


    1. xngel Автор
      17.06.2023 21:27

      Спасибо, гляну


    1. xngel Автор
      17.06.2023 21:27

      Очень интересная штука, покручу и её, возможно получиться (если получу адекватный результат, то тоже статью напишу)


  1. helg1978
    17.06.2023 21:27
    +1

    Текстовая консоль в виде скринов, да еще и в png?


    1. init0
      17.06.2023 21:27
      +3

      Ну с тем, что вывод консоли скриншотом тут не поспоришь, а вот что не так с png?


      1. anonymous
        17.06.2023 21:27

        НЛО прилетело и опубликовало эту надпись здесь


    1. xngel Автор
      17.06.2023 21:27

      На момент написания, это казалось хорошей идеей)


  1. JPEGEC
    17.06.2023 21:27

    По описанию создалось впечатление что это задача для чего-то типа nagios.


    1. xngel Автор
      17.06.2023 21:27

      Спасибо, посмотрю что за зверь nagios


    1. A1EF
      17.06.2023 21:27

      Nagios - это который система мониторинга или что-то ещё? Потому что в первом случае не понятно что вы имели ввиду, а во втором интересно узнать подробности.


      1. xngel Автор
        17.06.2023 21:27

        Согласен , почитал про Nagios и мне кажется ,что это не то) Ну или я не знаю "как его готовить"


  1. A1EF
    17.06.2023 21:27
    +1

    sudo apt update
    sudo apt upgrade

    Если система свежеустановленная, то вместо apt upgrade лучше сделать apt full-upgrade.

    cd /usr/local/3proxy/conf/
    chmod ug+rwx add3proxyuser.sh
    /usr/local/3proxy/conf/add3proxyuser.sh proxyuser proxypass

    Странно, что issue про это закрыт с сообщением об исправлении проблемы в версии 0.9.4. Но вообще достаточно было дать прав на запуск, права на запись тут избыточны.

    systemctl enable 3proxy && service 3proxy start && netstat -tlpn | grep prox

    Можно одной командой включить и запустить systemd-юнит:

    systemctl enable --now 3proxy
    

    net-tools давно считается deprecated, на свежей системе его может уже и вовсе не оказаться. Стоит использовать актуальный iproute2, где заменой netstat -tlpn будет команда ss -ntlp.

    curl -L -x http://proxyuser:proxypass@IP_Сервера:4888 https://myip.ru/index_small.php

    Мелочь, но есть ресурсы с более приятным выводом в консоли. Тот же https://2ip.ru выведет лишь IP адрес, без всяких html-тегов.


    1. xngel Автор
      17.06.2023 21:27

      Спасибо , учту на будущее


  1. danemon
    17.06.2023 21:27

    А в итоге прокси на nginx так и не попробовал? Я не гуру, но балансирование там вроде поддерживается (сам ещё не пробовал), а редирект на какой-либо адрес в зависимости от приходящего урла там точно есть. Или это будет в следующей статье?

    Причем в моем случае оказалось очень удобно что можно через nginx пропустить даже трафик без шифрования и придать ему все прелести HTTPS - превратить его в зашифрованный.

    Потом обернуть nginx в контейнер (или сразу взять готовый контейнер), и понеслась.


    1. xngel Автор
      17.06.2023 21:27

      Попробовал, но пока не получается его нормально настроить, как добью его сразу напишу статью


      1. anonymous
        17.06.2023 21:27

        НЛО прилетело и опубликовало эту надпись здесь


        1. danemon
          17.06.2023 21:27

          А зачем вам допы, тем более платные? ))
          У меня безопы требуют наоборот отказаться от всех неиспользуемых модулей, а раз так - я вообще посматриваю не просто на тему удалить все сторонние модули, а вообще скомпилировать nginx с нуля со всеми нужными настройками и внести это в пайплайн, осталось лишь добраться и сделать. Материалы на хабре об этом есть ))


          1. anonymous
            17.06.2023 21:27

            НЛО прилетело и опубликовало эту надпись здесь


  1. savostin
    17.06.2023 21:27

    Данные нельзя хранить на промежуточном сервере, так как они слишком часто обновляются :(

    "Слишком часто" - это несколько раз в секунду? Да и в этом случае странный выбор. Имхо для большинства сценариев подойдет демон, который опрашивает все "дома" и отдает уже готовый датасет. В противном случае Вы одним упавшим "домом" завалите всю кухню.


  1. shushu
    17.06.2023 21:27

    1. xngel Автор
      17.06.2023 21:27

      Звучит многообещающе


      1. A1EF
        17.06.2023 21:27

        Ваша задача достаточно легко решается с использованием любого балансировщика, будь то Nginx, HAProxy и т.п. В моей практике выход через нужный socks5-прокси из пула как раз был сделан с использованием HAProxy: несколько фронтендов (разные порты) для подключения клиентов и соответствующие бекенды (наборы проксей по странам, например) для выхода с балансировщика.


        1. xngel Автор
          17.06.2023 21:27

          Я уже мучаю nginx, но очень заинтересовал haproxy попробую и его посмотреть) спасибо за информацию


          1. Karroplan
            17.06.2023 21:27

            что-то, мне кажется, вы сильно не понимаете, что такое прокси.

            есть forward proxy: это софтина перенаправляет запросы от пользователей к серверам в интернет. То есть, в браузере у пользователя вы явно настраиваете адрес прокси (ни или curl -x) и браузер идет к проксе и говорит "дай мне страничку", а прокси устанавливает новое соединения, возможно с другого интерфейса, получает страничку и перекладывает ее в пользовательское соединение. Это - 3proxy, squid, tinyproxy, etc...

            есть reverse proxy который ставится в обратную сторону. Это по сути обычный веб-сервер принимающий запросы от пользователей из интернета и перенапавляющий их другим веб-серверам. Это - nginx, haproxy, traefik, apache, etc.

            вы в своей задаче в посте описываете обычный каскадированный forward proxy и продукт берете соответствующий. А в комментах вам советуют reverse proxy и вы пишите - пробую, интересно, но ничего не выходит. И не выйдет: ngnix и haproxy - это reverse proxy, они для другого. У forward proxy и reverse proxy (web server, по-факту) даже формат get запроса разный:

            https://www.oreilly.com/library/view/http-the-definitive/1565925092/ch06s05.html


            1. xngel Автор
              17.06.2023 21:27

              У Nginx есть модули для forward proxy и модули lua для обработки , ясно что на стандарте никто делать не будет, а будет собирать Nginx отдельно для этого

              https://github.com/chobits/ngx_http_proxy_connect_module

              https://www.nginx.com/resources/wiki/modules/lua/


              1. Karroplan
                17.06.2023 21:27

                ну то есть вы предлагаете вместо того чтоб взять просто squid засесть за пересборку или вообще за программирование? ну так в принципе и хлеб напильником можно резать, да )


                1. xngel Автор
                  17.06.2023 21:27

                  Squid не решит задачу, пробовали уже) если бы не стояло ограничение на ос то я мог бы и написать данный инструмент, а в сборке не вижу ничего страшного, nginx изначально облегчен и все что нужно можно доставить)


        1. Karroplan
          17.06.2023 21:27
          -1

          nginx и haproxy - НЕ балансировщики ) это reverse proxy. например, из важных функций балансировщика у nginx и haproxy нет возможности кластеризации, т.е. обеспечения отказоустойчивости - надо городить что-то с помщью другого софта типа keepalived, pacemaker и т.п.

          F5 LTM, Citrix NetScaler, Kemp - это балансировщики.


          1. A1EF
            17.06.2023 21:27
            +2

            Вы не поверите, но..
            Вы не поверите, но..
            И nginx о себе такого же мнения
            И nginx о себе такого же мнения

            И ещё, кажется, вы путаете кластеризацию с отказоустойчивость.


            1. xngel Автор
              17.06.2023 21:27

              Спасибо, а то я даже дар речи потерял )


            1. Karroplan
              17.06.2023 21:27

              кластер - это несколько узлов выполняющих одну задачу. бывают кластеры для повышения производительности общей системы (hadoop), кластер для повышения емкости (кластер гипервизоров), так и кластеры для повышения отказоустойчивости (failover cluster, например у Cisco ASA или MS Failover Cluster).

              по поводу балансеров: балансер служит для двух целей - обеспечение бесперебойной работы того что он там балансирует и соответственно сам не должен становиться точкой отказа; распределение нагрузки. Ни nginx, ни haproxy не умеют ничего самостоятельно про второй пункт. А еще ngnix про себя сам думать ничего не может, потому что он F5 ;)

              ах, да - кто это сказал, что они best-in-class? хоть кто-то из софтовых решений умеет в аппаратное ускорение шифрования, например?


              1. A1EF
                17.06.2023 21:27
                +2

                кластер - это несколько узлов выполняющих одну задачу. бывают кластеры для повышения производительности общей системы (hadoop), кластер для повышения емкости (кластер гипервизоров), так и кластеры для повышения отказоустойчивости (failover cluster, например у Cisco ASA или MS Failover Cluster).

                Иначе говоря, "кластер" вовсе не тождественен "отказоустойчивому кластеру". И "возможность кластеризации" совсем не тождественна "отказоустойчивости".

                балансер служит для двух целей - обеспечение бесперебойной работы того что он там балансирует

                Балансер занимается балансировкой нагрузки. Это прям в его названии отражено. Обеспечение отказоустойчивости - это отдельная задача. Вы явно привыкли использовать проприетарные коммерческие продукты, где вам за деньги все функции заботливо уложили в одну коробочку. Что ж, пожалуйста. Только это никак не делает справедливым ваше суждение, что "nginx и haproxy - НЕ балансировщики".

                А еще ngnix про себя сам думать ничего не может, потому что он F5 ;)

                Ну зайдите по ссылке к скриншоту, посмотрите что за сайт и чей он.

                ах, да - кто это сказал, что они best-in-class? хоть кто-то из софтовых решений умеет в аппаратное ускорение шифрования, например?

                Спрашивать про аппаратное ускорение шифрования софтверных решений? Вы это серьезно?


  1. Neveil
    17.06.2023 21:27

    Не понял задачу. Опишите пожалуйста подробнее.

    «Условно мы обращаемся к серваку 100.1.2.101 по порту 8080 и просим от него данные о всех домах на определенной территории ,он в свою очередь по заданному сценарию коннектится к определенному proxy»

    Какая логика выбора сервера? Мы обращаемся снаружи и запрашиваем данные с одного из агентов?