Сага о геолокации и как сделать гео-вебсервис на NGINX без движка базы данных и без программирования

    Сегодня мы поднимем довольно старую тему про геолокацию по IP-адресу и новую про быстрые веб-сервисы без «языков программирования» . Также мы опубликуем готовый образ контейнера, чтобы вы за 5 минут могли развернуть такой веб-сервис у себя.

    Наша компания занимается созданием Интернет-магазинов запчастей на собственной SaaS-платформе (ABCP.RU), а также у нас есть несколько связанных проектов, например, сервис поиска запчастей 4MyCar.ru.
    Как и многие другие веб-проекты, мы в своё время пришли к пониманию необходимости геолокации по IP-адресу. Например, сейчас она используется на 4MyCar.ru для определения региона (при первом входе на сайт регион автоматически устанавливается именно так).



    Аналогично производится выбор ближайшего к клиенту филиала магазина на сайтах клиентов платформы ABCP.



    Когда перед нами впервые возникла задача геолокации, мы только начинали изучать этот вопрос. Собственно говоря, на тот момент, кроме баз MaxMind, особых альтернатив не было. Попробовали, поигрались и забросили. В реальной работе несколько раз воспользовались MaxMind GeoLite для того, чтобы отфильтровать особо назойливых ботов, пытавшихся уложить сайты наших клиентов

    (хватило фильтрации по стране в nginx, примитивная проверка в if, см. документацию ngx_http_geoip_module). Бесплатные базы не давали достаточной точности в RU, содержали названия городов в латинице и поэтому не очень нам подходили для других целей.

    Через некоторое время один из наших сотрудников обнаружил отличный сайт ipgeobase.ru, позволяющий “скачать” базы геолокации для России и Украины, а также воспользоваться его XML веб-сервисом через простой http запрос. Например, переход на сайт 4mycar.ru по фразе “масляный фильтр купить в урюпинске” из соответствующего города вызывал в итоге примерно такой запрос к веб-сервису http://ipgeobase.ru:7020/geo?ip=217.149.183.4. В результатах были названия города и региона на русском языке, что было очень удобно. В очень короткие сроки работу с веб-сервисом задействовали в коде, определяющем ближайший филиал магазина. Однако, после запуска в продакшн выявилось несколько проблем:
    1) обычно запрос к веб-сервису требовал некоторого небольшого времени (сотые доли секунды в нормальном состоянии из датацентра в Москве), но вот из офиса разработчиков в регионе задержки были уже выше (где-то полсекунды);
    2) изредка (по нашим наблюдениям, в “часы пик”) это время было ощутимо больше, что вызывало уже неприятные задержки при ответе нашим клиентам;
    3) так уж вышло, что для одного и того же клиента несколько раз требовалось провести геолокацию, отсюда возник вопрос о кешировании геоданных;
    4) своими неоптимальными запросами мы создавали нагрузку на веб-сервис ipgeobase, что было нехорошо по отношению к владельцам сервиса;
    5) для других стран (не RU и не UA) геолокация не работала.

    Для решения этих проблем мы быстро “собрали совещание”

    и получили два основных варианта решения: взять базы и написать свой веб-сервис (периодически скачиваем базы ipgeobase, импортируем в свою базу данных, отдаём через http с кешированием, например, в memcached) или сделать кеширование геоданных в memcached или redis (запрашиваем данные в ipgeobase и сохраняев в кеш). Навскидку оба варианта требовали достаточно много столь дефицитных человеко-часов разработчиков, и в итоге нашелся третий вариант: мы несколько снижаем точность (заменяем последний октет в ip адресе на 0 и исходим из того, что у провайдеров одна подсеть /24 находится в разных городах не слишком часто) и делаем на своём оборудовании кеширующий прокси на nginx с большим временем кеширования и маленькими таймаутами при запросах к ipgeobase. Этот вариант оказался очень эффективным, в разы снизил нагрузку на ipgeobase и время геолокации. Вариант с собственным веб-сервисом был отложен на неопределенное время.

    Через некоторое время нам вновь понадобилась геолокация в nginx (да, опять эти боты, но теперь уже много из RU), поэтому фильтрации по стране по данным баз MaxMind оказалось недостаточно.

    Потребовалось это срочно, поэтому воспользовались другим geo модулем (ngx_http_geo_module) и вывели из базы ipgeobase номер региона в переменную. Этого хватило, чтобы “заткнуть дыры”.
    Вскоре нам попался скрипт ipgeobase2nginx.php, который создавал базы для nginx и, в итоге, получили человекочитаемую информацию о городе в переменной. Эти данные, а также данные MaxMind, можно уже было выводить в логи или передавать в заголовках на backend, что, в принципе, всех устраивало.

    Всё это время мы периодически задумывались о дальнейшем развитии. Планы по созданию собственного веб-сервиса пылились в списках TODO и всплывали изредка в виде “а я тут вечером хочу изучить python/erlang/haskell/etc, что бы написать следующим после ‘Hello world’?”, но дальше хотелок не двигались.
    Внезапно, сначала в виде шутки за чаем (just for fun), возникла идея на основе имеющихся в nginx наработок сделать веб-сервис, аналогичный ipgeobase, но без движка базы данных и использования скриптовых языков.
    Быстрый анализ того, что мы имеем, дал такой результат:
    1) в свободном доступе есть базы GeoLite в csv и ipgeobase в тексте;
    2) модуль ngx_http_geo_module умеет выставлять значения переменных по IP-адресу, а также делает это ужасно быстро (даже использует для ускорения binary geo range base);
    3) для RU и UA мы доверяем ipgeobase, но, по возможности, хотим видеть и данные MaxMind;
    4) в nginx отлично реализованы ssi (ngx_http_ssi_module), причем не только для text/html, но и для других типов файлов;
    5) nginx может взять ip адрес из заголовка запроса и считать, что это IP-адрес клиента (ngx_http_realip_module), а значит, передать его модулю geo.
    Осталось добавить несколько “наколеночных” скриптов, которые из файлов csv и ipgeobase сделают требуемые куски конфигов для nginx.

    Вот что у нас получилось:
    https://yadi.sk/d/QsNN87nMesXo8 — конфиги и скрипты.

    Для того чтобы показать веб-сервис в работе, мы временно развернули его на VDS, доступном по ссылке http://muxgeo-demo.4mycar.ru:6280/muxgeo/.

    Чтобы быстро запустить такой сервис у себя, вы можете загрузить готовый образ LXC — https://yadi.sk/d/1WrvV2RyesYFM (логин: пароль — ubuntu:ubuntu).

    Приведем краткое описание работы скриптов, в LXC мы располагаем их в /opt/scripts.

    В подкаталоге /opt/scripts/in нужно положить файлы, полученные из MaxMind и ipgeobase, и немного их обработать (примерно так):
    iconv -f latin1 GeoLiteCity-Location.csv | iconv -t ascii//translit > GeoLiteCity-Location-translit.csv

    Для работы требуется дополнительный файл от MaxMind с названиями регионов:
    dev.maxmind.com/static/csv/codes/maxmind/region.csv

    Теперь сами скрипты:
    GeoLite2nginx.pl — генерирует файлы out/nginx_geoip_*
    ipgeobase2nginx.pl — генерирует файлы out/nginx_ipgeobase_*

    Нам потребуется наложить диапазоны IP-адресов в geoip и ipgeobase. Для этого первые два скрипта при выполнении создали файлы с целочисленным представлением IP-адресов ( out/nginx_geoip_num.txt и out/nginx_ipgeobase_num.txt ). Мы вручную сделали файл in/nginx_localip_num.txt, в который положили список зарезервированных диапазонов (локальные сети и т.п.). Дополнительно из результирующих списков исключить диапазон multicast адресов.

    Как мы это делаем:
    Скрипт make-dup-ranges.pl проходит по списку и для каждого четного ip (начало нового диапазона) добавляет в список предыдущий (конец предыдущего диапазона), а для каждого нечетного — следующий. Дальше список сортируем, убираем дубликаты.

    Скрипт make-ranges.pl создает такой конфиг с диапазонами для nginx.

    Теперь у нас есть конфиги для nginx, надо их подключить.

    Схема у нас будет состоять из frontend и backend (frontend передает запросы на backend с преобразованием заголовков и кешированием). Сделаем всё это на ubuntu 14.04 в контейнере LXC, nginx возьмем с официального сайта.

    Содержимое out положим сюда:
    /etc/nginx/muxgeo/data/

    Сделаем “обвязки”, которые установят необходимые переменные:
    /etc/nginx/muxgeo/muxgeo.conf
    /etc/nginx/muxgeo/muxgeo-geoip.conf
    /etc/nginx/muxgeo/muxgeo-ipgeobase.conf

    А также примитивную логику для backend:
    /etc/nginx/muxgeo/muxgeo_site.conf

    Конфиги для frontend и backend находятся здесь:
    /etc/nginx/conf.d/muxgeo-frontend.conf (слушает порт 6280)
    /etc/nginx/conf.d/muxgeo-backend.conf (порт 6299)

    Также нам понадобится файл, допустим, index.html, в котором мы выведем в нужном нам формате данные с помощью SSI в nginx. Расположим его в каталоге
    /opt/muxgeo/muxgeo-backend/muxgeo

    Таким образом, запрос к
    http://muxgeo-demo.4mycar.ru:6280/muxgeo/?ip=217.149.183.4
    транслируется на backend с подменой ip адреса на 217.149.183.4, а backend вставит информацию в нужные места html текста.

    Но html страничка — это немного не то, что нам хотелось, нужен xml, как у ipgeobase. Просто заполняем шаблон выводом соответствующих полей, смотрите пример в файле muxgeo.xml

    По ссылке
    http://muxgeo-demo.4mycar.ru:6280/muxgeo/muxgeo.xml?ip=217.149.183.4
    получим “такой же, но лучше”, чем у ipgeobase вывод xml, да еще и в utf-8

    Надо JSON — без проблем. По аналогии шаблон, и готово:
    http://muxgeo-demo.4mycar.ru:6280/muxgeo/muxgeo.json?ip=217.149.183.4

    Хочется экзотики — давайте выведем в виде ini-файла:
    http://muxgeo-demo.4mycar.ru:6280/muxgeo/muxgeo.ini?ip=217.149.183.4

    Для того, чтобы протестировать работу, можно, например, создать гео-базу по адресам всех стран в формате, похожем на результат упоминавшегося выше (ipgeobase2nginx.php). Сделаем текстовый файл с шаблоном (muxgeo_fullstr.txt) и простой скрипт, который прочитает данные для всех имеющихся диапазонов.

    Небольшое замечание. В примерах frontend и backend работают на одном nginx. В случае большой нагрузки имеет смысл разнести их на разные nginx, так как воркер для backend с геоданными потребляет больше памяти, чем минимальный nginx с proxy_cache.

    Каково дальнейшее развитие этого проекта? Можно, например, добавить другие источники данных, совсем немого усложнив конфигурацию, а также подключить свои гео-базы, в которые поместить “уточнения, полученные из достоверных источников :) ”.

    NodaSoft

    33,80

    Компания

    Поделиться публикацией
    Комментарии 51
        0
        Посмотрим в логах, что это такое. Вероятно данные из maxmind geoip.
          0
          Ваш адрес из Нокиа :)

          org-name: NOKIA Corporation

          Почему-то в MaxMind GeoLite название организации посчиталось названием города.
          Попробуйте сообщить в MaxMind об ошибке.
            0
            Я в курсе :)

            Вот тут — www.maxmind.com/ru/geoip2-precision-demo они мне правильно кстати определяют.
              +1
              Почитайте внимательно статью, мы просто взяли бесплатные базы в свободном доступе и придумали способ работы с ними без организации тяжелой обвязки (фактически, требуется только nginx).

              Если будут доступны базы с лучшим качеством определения, вполне получится использовать их.
                0
                Да, читал, скорее всего в бесплатной базе просто это не будут править, она ведь и предоставляется as is.
                  0
                  Нужно попробовать GeoLite2, там и названия городов на нескольких языках.
                    0
                    В следующей версии muxgeo так и будет.
        +3
          +1
          Основная особенность: нет скриптовых языков при работе сервиса, всё работает быстро на модулях nginx.
          Другая особенность: скачайте себе скрипты и конфиги и у вас есть такое же сервис.
          Мы же выложили даже готовый образ системы «для ленивых», запустить его — 5 минут.
            +1
            Третья особенность — формат выдачи можно легко настроить под ваш софт.

            А sypexgeo.net — проект отличный.
              +1
              Подразумевалось что это на удивление внятная замена для ipgeobase и maxmind.
                +1
                sypexgeo.net/ru/editions/ версия free, конечно, порезана, но в api все данные есть.
                в принципе, можно сделать по аналогии с тем, что топикпастер сделал с кешем ipgeobase и относительно спокойно пользоваться.
            0
            Автор забыл указать пароли для доступа в контейнер LXC. Оказалось, это стандартные ubuntu:ubuntu :)
            Ну или можно просто положить свои публичные ssh ключи и сразу подключаться без пароля как root.
              +1
              Да, действительно. В комплекте стандартный образ, который получили примерно такой командой

              lxc-create -t ubuntu -n ubu1404c0000 -- -r trusty
              


              и добавили в него nginx из официального репозитория.

              Ну и конечно, наши скрипты.
              0
              Это все интересно, но если выкладываете код, то делайте это через GitHub
                +1
                Там то и кода нет. Фактически, просто описание принципа работы с примерами скриптов. Пожелание учли.
                0
                респект за проделанную работу, очень нужная штука :)
                0
                полезно.
                каждый раз свой велосипед приходится делать.
                никакого стандарта к сожалению нет.
                  +1
                  но вобще идея понравилась, поставлю у себя, потестирую производительность
                    0
                    спасибо.
                    будет возможность, кидайте сюда отзывы.
                  0
                    +1
                    Неужели и правда уволили?
                    Это же и xml интерфейс у них может в любой момент пропасть.
                    Тогда наш вариант реализации xml сервиса становится более актуальным.
                      0
                      Н-да, и у баз на сайте последнее обновление 03-02-2015

                      Блин, только я для их баз свой сервис (https://github.com/davidmz/ipgeobase) сделал:(

                      Придётся на базы sypexgeo.net переходить, что ли…
                        0
                        Да может всё обойдётся. Базы скорее всего создаются по выгрузкам из RIPE и процесс автоматизирован. Может быть доступ станет платным, ну тогда сообщество скинется и купит 1 копию базы и размножит её ;)
                          0
                          Так не обновляется же с 3 февраля, а раньше обновлялся ежедневно. Значит, если и был процесс автоматизирован, то его отключили.
                            0
                            тогда да, плохо.

                            можно попробовать вытянуть из sypexgeo информацию, вроде бы там актуальное.
                              0
                              Сегодня мы позвонили в техсаппорт RU-Center. Несмотря на то, что домен ipgeobase.ru принадлежит Руцентру и расположен на их хостинге, сотрудник саппорта это вначале отрицал. Потом сказал, что поддержка этого сервиса осуществляется исключительно через email, указанный на самом сайте.
                                0
                                Ура, базы обновились, всё нормально!

                                ipgeobase.ru/cgi-bin/Archive.cgi
                                  0
                                  Ура!!!
                                    0
                                    Скоро сделаем с GeoLite2, будут русские названия зарубежных городов.
                                    0
                                    с тех пор и не обновлялись ((
                          0
                          Что за отвратительная система «антибот» на этом сайте? Она меня промурыжила отвратительными значками ожиданиями, заставила подтвердить что я не робот и потом подождать чёртовых 130 секунд! Это вообще что за дела?
                            +1
                            Я так понимаю, вы про сайты на платформе.
                            По нашим данным на вашем IP адресе висит tor exit node, поэтому вы попали на капчу.
                            Так как наши недруги боты никак не успокаиваются и в последнее время любят tor, мы теперь показываем капчу на таких IP адресах.
                              0
                              В этой стране вообще вредно exit node устраивать, при плохом стечении обстоятельств будут задавать много вопросов, а может что и похуже.
                                0
                                У меня exit node, действительно, но на ней стоит реджект портов 443, 80, 8080, 23, 25 и 3128. Так за что меня так?
                                  0
                                  В публичном списке нод, к сожалению, нет списка портов. Поэтому вынуждены фильтровать все такие ноды. Если подскажете, как получить список нод с разрешенными портами, мы с удовольствием переделаем капчу.
                            0
                            www.telize.com/ не смотрели?
                            Хотя я у себя использую только координаты…
                              0
                              Не доводилось с этим сервисом сталкиваться. Спасибо за наводку, изучим.

                            Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

                            Самое читаемое