Миллион PPS в секунду — связанность и балансировка


На последней конференции РИТ++ мне посчастливилось стать впервые докладчиком конференции такого масштаба и такой значимости. В этой статье я не просто хочу пересказать всё, о чём я докладывал. Выступать впервые перед такой большой аудиторией для меня было непривычно и я половину забыл рассказать, нервничал немного. Речь пойдет о создании с нуля собственной отказоустойчивой структуры для веб-проектов. Мало кому из системных администраторов дается возможность с нуля запустить в production крупный проект. Мне повезло.

Как я уже написал, я не смог рассказать всё, что планировал со сцены, в этой статье я восполню эти пробелы, да и для того, кто не смог там присутствовать — это будет приятно, видео с конференции так и не дали бесплатно всем. Да и стать пользователем Хабра я хотел давно, вот только не было времени. Майские праздники дали время и силы. Статья будет не столько технической с кучей конфигов и графиков — статья будет принципиальная, все пробелы мелких технических вопросов можно будет восполнить в комментариях.



Ставим задачу: нам нужно организовать на 99,9% отказоустойчивый веб-проект, который не будет зависеть от конкретного дата-центра (ДЦ), иметь двойное резервирование на коммутации в ДЦ, иметь отказоустойчивость на балансировке и очень быстро вводить и выводить Real сервера. При падении одного из ДЦ, второй должен принять весь трафик без дропов и ретрансмитов. Говорить мы будем только о фронт, об отказах, балансировках и немного о деплое такого количества серверов. У всех своя специфика backend.

Резервирование связанности

Проект, которым я занимаюсь очень критичен к даже минутным сбоям. Если просуммировать RPS 400+ топ проектов Рунета: Яндекс, Мейл, Рамблер, Афишу, Авто, и пр., получите наш суммарный RPS. В какой-то момент стало понятно, что если далее держать все яйца в одной корзине (ДЦ), можно очень хорошо и красиво упасть. Если вспомнить пыхи Чубайса по Москве — меня понять можно. Мы начали изучать вопрос разноса фермы серверов на несколько ДЦ. Главный вопрос — не раскидать сервера на несколько ДЦ, не связанных одним и тем-же магистралом, а быстро и незаметно для клиента переключать трафик с одного ДЦ на другой.



Сразу оговорюсь: выбранная нами реализация возможна только при наличии собственной AS минимум с маской /23, официально порезанных в RIPE на 2 сети по /24. Честно говоря я не в курсе, как в настоящий момент, но раньше были проблемы с анонсами на транзите у того-же ТТК сетей менее /24. Мы выбрали в качестве маршрутизаторов в ДЦ Cisco ASR 1001 с соответствующей начинкой и возможностью работать с EXIST-MAP, NON-EXIST-MAP. Рассмотрим пример 2х ДЦ. 2 ASR-а держат туннель просто для того, чтобы знать, что связанность обоих ДЦ жива. Из одного ДЦ мы анонсируем одну сеть класса С, из второго ДЦ — вторую сеть. Как только мы видим из второго ДЦ, что связанность нарушена, мы начинаем с помощью NON-EXIST-MAP анонсировать вторую сеть /24 из первого ДЦ. Желательно ДЦ разнести географически — но это на бюджет и цвет.

Тут важный момент, к которому мы вернемся немного позже: на серверах балансировки и на Real серверах должны быть постоянно одновременно подняты IP адреса из обоих сетей в обоих ДЦ, это важно. При корректной связанности всех ДЦ маршруты руководят, кто отвечает на адреса той или иной сети, поэтому нужен туннель между маршрутизаторами, чтоб голову не унесло. Прошу OpenSource сообщество не пинать ногами — я знаю как это реализовать на quagga с весами префиксов, но проект крупный и есть соответствующие требования. Я просто рассказываю, как это реализовано у нас.

При тестировании смен маршрутов, мы наткнулись на неприятную ситуацию: у многих на доступе стоят Juniper. Сейчас объясню почему неприятную. По-умолчанию смена маршрута анонсируется 1 раз в 3 минуты. Для нас — это потеря сотен гигабайт данных. Мы начали экспериментировать с уменьшением времени анонсов — до 20 секунд было все отлично: анонсы поднимались из разных концов нашей огромной родины отлично и все быстрее, но после уменьшения данного параметра мы потеряли один из ДЦ. Как оказалось у Juniper, который обслуживал один из аплинков менее 20 секунд считается флудом и не возможно уменьшить, это особенности прошивки. Итог: 21 секунда — самый оптимальный параметр.

Так. Всё. Failover для 2х ДЦ мы сделали, протестировали — это работает. Реальное время свичинга маршрутов по России — от 25 до 60 секунд — в зависимости от удалённости и транзита. На этом этапе у нас есть 2 голых ДЦ с маршрутизаторами. Начнём ставить начинку в оба ДЦ.

Резервировние коммутации внутри ДЦ



Тут я буду краток — коммутация внутри ДЦ сделана 2 на 2: 2 физических интерфейса в Bond смотрят на разные коммутаторы в vlan, обслуживающий реальные IP адреса, 2 физических интерфейса в Bond — на разные коммутаторы в vlan, обслуживающий backend. Режим бондинга мы выбрали самый простой mode=1 — на отказ, просто нулевые части бондинга мы разбросали на разные коммутаторы. Вообще, чем проще, тем надёжнее. На каждом сервере, участвующем в фронт и бэк — 4 физических интерфейса. На рисунке показан только фронт. Коммутаторы связаны не только по 10G, но и кабелем резервного питания и подключены к разным UPS. Mode=1 не требует специальной настройки коммутаторов, что упрощает управление всей инфраструктурой.

Балансировка



Собственно особых альтернатив не было, был выбран IPVS в режиме DR (Direct Routing). Как я говорил выше, этот метод балансировки менее затратен. Он лишает прелестей дампа соединений в обе стороны, как в NAT, но требует меньше ресурсов. Любителям notrack скажу — лучше так не делать, если Вам важно сохранить 100% соединений. IPVS — штатный в Centos и не требует особых шаманств при установке. Единственный нюанс с которым мы столкнулись — по-умолчанию IPVS готов принимать 4096 одновременных соединений — это 12 бит.



Чтобы балансировщик был готов принимать миллион соединений этот параметр нужно увеличить до 20 битов. Через options в управлении модулем (modprobe.d) это сделать не удалось — ipvsadm так же упорно твердил, что готов обслуживать 4096 соединений. Пришлось запустить свои ручки в src и пересобрать модуль. Для небольшого проекта это не столь важно, но если Вы обслуживаете сотни тысяч и выше соединений — это критично.

У балансировки есть много методов. Самый простой — RR (Round Robin) — отдавать на Real сервера входящие соединения «по-кругу». Но мы выбрали тип WLC — взвешенная балансировка с контролем соединений. Если мы параметр persistence на балансировщике синхронизируем с keepalive параметром в nginx на Real серверах, то получим гармоничность в соединениях. Просто если, например, persistence на балансировщике будет 90 секунд, а keepalive в nginx на Real — 120, то мы получим следующую ситуацию: балансировщик через 90 секунд отдаст все соединения клиента на другой Real, а старый Real будет ещё держать соединения клиента. Если учитывать, что при 30 секундном keepalive на 10 тысяч ESTABLISHED соединений мы имеем порядка 120 тысяч TIME_WAIT (это совершенно нормально, мы с Игорем Сысоевым считали в кулуарах технофорума мейла), то это достаточно критично.

Теперь по самой технологии балансировки, как это работает: IP адрес, который у нас прописан в DNS для домена мы вещаем на внешний физический интерфейс балансировщика и на алиасы loopback-ов Real серверов. Ниже будет пример конфигурации sysctl Real серверов — там нужно обязательно прикрыть arp аннонсы и, по желанию, source routing, для того, чтобы коммутация не сошла с ума от дублирования IP. Я не буду описывать весь sysctl — он достаточно специфичен для каждого проекта — покажу только параметры, обязательные для именно DR специфики.

Для балансирощиков:

# Fast port recycling (TIME_WAIT)
net.ipv4.tcp_tw_recycle = 1
net.ipv4.tcp_tw_reuse = 1
# Local port range maximized
net.ipv4.ip_local_port_range = 1024 65535
# Dont allow LB send icmp redirects on local route
net.ipv4.conf.eth1.send_redirects = 0
net.ipv4.conf.eth0.send_redirects = 0
net.ipv4.conf.lo.send_redirects = 0
# Dont allow LB to accept icmp redirects on local route
net.ipv4.conf.eth1.accept_redirects = 0
net.ipv4.conf.eth0.accept_redirects = 0
net.ipv4.conf.lo.accept_redirects = 0
net.ipv4.conf.default.send_redirects = 0
net.ipv4.conf.default.accept_redirects = 0
net.ipv4.conf.all.send_redirects = 0
net.ipv4.conf.all.accept_redirects = 0
# Do not accept source routing
net.ipv4.conf.default.accept_source_route = 0
# Ignore ARP
net.ipv4.conf.eth0.arp_ignore = 1
net.ipv4.conf.eth1.arp_ignore = 1
net.ipv4.conf.all.arp_ignore = 1
net.ipv4.conf.default.arp_ignore = 1


Для Real серверов:

# sysctl.conf
net.ipv4.conf.bond0.arp_ignore = 1
net.ipv4.conf.all.arp_ignore = 1
net.ipv4.conf.default.arp_ignore = 1


Маршрутизация для алиаса loopback-а с реальным IP на Real серверах с маской 255.255.255.255 должна быть прописана на IP маршрутизатора, обслуживающего вашу сеть и аннонсирующий её во вне, иначе запросы-то на балансировку придут, а вот Real не ответит. Некоторые мудрят с iproute2 и VIP таблицами, но это усложняет конфигурацию.

Теперь об отказоустойчивости балансировки и Real. Keepalived был выбран совершенно логично — сами разработчики IPVS рекомендуют использовать его в связке со своим продуктом. Штатная схема — MASTER + SLAVE подойдёт 99% проектам, но нам это не подошло, ибо в настоящий момент в одном ДЦ 5 балансеров, в другом 3. Средняя стоимость лезвия 120-140 тысяч, поэтому решили немного сэкономить. Если внимательно посмотреть в секцию конфигурации keepalived, где описываются группы, то станет понятно, что, если на 2х мастерах задать разные группы, а на 1м slave описать в конфиге обе, то 1 slave будет работать на 2 мастера по схеме: выпал 1 мастер, slave подхватил его IP, если же выпал второй, то и второй IP подхватывается на этот же slave. Минус конечно есть, при вылете обоих мастеров, на slave упадёт двойная нагрузка, но для балансировщика это не сильно критично, в отличии от Real.

И ещё одна маленькая хитрость: в конфиге keepalived предусмотрен CHECK Real серверов. Он может быть простым TCP, может дёргать по HTTP страничку для контроля 200того ответа, но есть MISC_CHECK. Штука удобная и функциональная. Если на Real сервер положить небольшой скрипт, не важно на чём написанный, который будет по Вашей логике вычислять текущий LA и генерить динамический вес Real сервера для балансировщиков, то Вы с помощью параметра MISC_DYNAMIC на балансировщике получите динамическое изменение веса Real-а.

Deploy

Немного о деплое. Деплой — это автоматизация развёртывания серверов. Если у Вас 2-3-4 сервера, конечно Вы не будете разворачивать автоматизацию, но если у Вас их десятки или сотни, то однозначно стоит, тем более, если сервера сгруппированы одинаковыми задачами.
У меня на фронте всего 2 группы — это балансировщики и Real сервера с nginx, которые собственно и отдают контент пользователю.

# file top.sls
base:
  '*':
    - ssh
    - nginx
    - etc
  'ccs*':
    - ccs
  'lb*':
    - lbs


Есть много систем деплоя — puppet, chief ..., но мы выбрали salt (saltstack.org). Как это работает: на сервере salt в конфигурации Вы создаёте группы серверов и для каждой группы расписываете план деплоя — он включает в себя системных пользователей и групп, которые требуются для функционирования сервисов, конфигурационные файлы, которые должны быть на всех серверах, установленный софт…

# file /srv/salt/etc/init.sls
/etc/hosts:
  file:
    - managed
    - source: salt://files/hosts

/etc/selinux/config:
  file:
    - managed
    - source: salt://files/selinux.config

/etc/snmp/snmpd.conf:
  file:
    - managed
    - source: salt://files/snmpd.conf


Если в конфигурационных файлах требуются уникальные для каждого сервера данные, в salt присутствует шаблонизатор pillar, который умеет генерить на лету уникальные конфиги. Я перед тем, как развернуть всю ферму, начал именно с сервера деплоя, чтобы после окончания формирования фермы серверов быть уверенным, что деплой написан корректно. И Вам так советую.

Это лишь часть доклада. Обязательно выберу время и напишу отдельную статью по методикам тестирования web фермы, «выжимания» максимума из nginx без дропов и ретрансмитов, разницам синтетических и продакшн тестов.

Удачи в highload!
Поделиться публикацией
Похожие публикации
AdBlock похитил этот баннер, но баннеры не зубы — отрастут

Подробнее
Реклама
Комментарии 55
  • +1
    Если не трудно, можете рассказать почему решили остановиться на salt?
    • +4
      Главная причина — человеческий фактор. Тяжеловесы puppet и chief более инертны, чем молодой проект salt. Плюс солт многопоточен (написан на питоне) — если паппет деплоит кластер за 50 секунд на какой-то задаче, то солт справится с аналогичной задачей секунд за 15-20, не более. Быстрая реакция вбагтреке. Думаю перечисленного достаточно. Да и все задачи, которые нам требуется решать, солт выполняет на 100%.
      • 0
        Полагаю, что прироста в скорости уже достаточно.
        • 0
          Посмотрите еще ansible — тоже очень хорошая система и, главное, она простая и предсказуемая. Тоже на питоне.
    • +1
      Анонс сети /24 может вызвать проблемы со связанностью, потому, что некоторые провайдеры фильтруют черезмерно короткие префиксы (а /24 считается коротким)
      • 0
        Коротким /24 не считается — это сеть класса С, а вот менее (например /28) — да.
        • +2
          Более-менее нормальным считается анонс от /22. Сеть /28 будет отфильтрована с почти 100% верноятностью.
          • +2
            Это как раз та самая граница.
            /24 чаще всего проходит, но в рамках дефолтов. Всё, что меньше — фильтруется. Всё, что больше — проходит. Но зачастую, большие провайдеры и /24 фильтруют.
            • 0
              /24 может не пройти, если в RIPE /23 не порубить на 2 по /24 — такое случалось. Да и не всех магистралов это качается — многие и /28 пропускают.
              • +1
                Как раз об этом и говорю.
                Но если ваш магистрал пропускает /28, то это какой-то странный магистрал.
                Очень странный.
                Либо договорённость.
                Но скорее всего, это просто странный магистрал =)
                • 0
                  Наши не пропускают)
                  • 0
                    Отчего же??
                    Вот кто по простой договорённости пропускает:
                    AS48625, AS50952, AS56336, AS43106, AS39792, AS9002, AS29076, AS8359…
                    Ну эт так, на вскидку.
                    Ещё RunNet (AS не помню) легко.
                    ТТК, Голды, Прометей — по письму.
                    • 0
                      Когда я писал «наши» — я имел в виду те, с кем мы работаем напрямую, а не «Российские»
                      • 0
                        Ну, пардоньте =)
                        Под нашими я понимаю именно «наших».
                        Кстати, вот именно с операторами РФ легче всего договориться, нежели с операторами европейскими.
        • 0
          А какие датацетры используете в своем проекте?
        • +2
          Спасибо за статью, было интересно почитать.
          Посмотрел на Salt — с удивлением для себя понял, что она доступна только в платной пописке, и не OpenSource.
        • 0
          А что делает проект? Как-то цифры с буквами не сходятся.
          Cisco ASR 1001 тянет 5Gbps. Один датацентр может тянуть весь траффик, то есть 5Gb = 640 MB в секунду. И это на миллион PPS (pages per second?)
          получается 640 байт на страничку?
          Даже если отвечать empty gif'ом, есть же ещё и входящий траффик с сотнями кук, SYN, ACK и т.д.

          Тут на 30-40 тысяч запросов в секунду потребление трафика лезет в небо, а миллион…
          • +2
            Ну во-первых циска не одна) Вот описание проекта — tns-counter.ru/
            Вот результаты — tns-global.ru/rus/data/ratings/index/index.wbp
            Контент очень специфичный — приходит GET (пример можно посмотреть на том же Яндексе в теле любой страницы), отдаётся прозрачный gif и кука.
          • +1
            Если у Вас 2-3-4 сервера, конечно Вы не будете разворачивать автоматизацию

            Не так уж и «конечно». Даже при наличии одной вдски для какой-нибудь джумлы на средства автоматизации деплоя как самого сервера, так и приложения, стоит посмотреть именно в целях минимизации простоев при отказах — даже для виртуалки в супер-пупер облаке может потребоваться быстро развернуть второй аналогичный инстанс. А надеясь на записи или, тем более, память, легко получить ситуацию «вроде всё то же самое, но почему-то не работает».

            Пускай это даже будет самописный шелл-скрипт, в котором всё захардкожено и ничего не прокомментировано и который даже нельзя без внимания оставить, потому что будет спрашивать "?(yes/no)" постоянно, но немало нервов и/или денег он может спасти. Главное поддерживать его в актуальном состоянии, для чего есть разные методики, но самая простая — ничего не делать на сервере ручками.

            Только не следует забывать, что инструменты деплоя и бэкапа служат для разных целей и не стоит на один накладывать функции второго, использовать их следует совместно, но совсем необязательно в рамках одной «сессии», хотя лично у меня типичный сценарий восстановления: задеплоили среду (установили весь софт из публичных репов, условно, репов ОС), задеплоили приложение (из своего приватного репа), накатили бэкап данных приложения (база и пользовательские файлы типа аватарок, фоток или документов).
            • +2
              Единственный нюанс с которым мы столкнулись — по-умолчанию IPVS готов принимать 4096 одновременных соединений — это 2 бита.
              Чтобы балансировщик был готов принимать миллион соединений этот параметр нужно увеличить до 12 битов.


              Кол-во соединений ограничено доступной памятью, а 4096 — это размер хеш таблицы для соединений. Не понятно что такое 2 бита. Если вы имеете ввиду этот параметр
              CONFIG_IP_VS_TAB_BITS
              , который задает размер хеш-таблицы размером 2^CONFIG_IP_VS_TAB_BITS, то он и так 12 бит.
              Хотя для систем, обрабатывающих большое кол-во соединений, я бы его увеличил. С одной стороны у нас чуть больше будет вымываться кеш, но с другой стороны мы получим сильный прирост в производительности, так как для лукапа соединения в среднем придется итерировать по более коротким связным спискам.
              • 0
                Да, tab_bits. По-умолчанию значение этого параметра = 2.
                • 0
                  Хм, странно, посмотрел вплоть до 2.6.32, везде 12 бит, у вас на картинке вроде тоже (log 4096), ну да не суть. =)
                  • 0
                    да, 4096 — это и есть 2 бита. 12 бит — это помоему 1048576.
                • 0
                  Выходит наложение патча не влияет на ограничение по количеству соединений?
                  • 0
                    Если речь идет об изменении CONFIG_IP_VS_TAB_BITS, то оно не влияет на максимальное кол-во соединений, на это влияет только кол-во доступной ядру памяти.
                    • 0
                      IP_VS_TAB_BITS — IPVS connection table size (the Nth power of 2).
                      • 0
                        Вы пропустили ключевое слово hash. Это размер для хеш таблицы коннектов, сами коннекты аллоцируются динамически и добавляются в связные списки, по одному (списку) в каждый бакет хеш таблицы.
                        Вы бы лучше открыли код, я ж давал выше ссылку в нужное место.
                        Таблица аллоцируется тут
                        ip_vs_conn_tab = vmalloc(ip_vs_conn_tab_size * sizeof(*ip_vs_conn_tab));
                        внутри функции с говорящим названием ip_vs_conn_init
                        где ip_vs_conn_tab_size это размер хеш таблицы
                        static int ip_vs_conn_tab_bits = CONFIG_IP_VS_TAB_BITS;
                        ip_vs_conn_tab_size = 1 << ip_vs_conn_tab_bits;
                        А ip_vs_conn_tab — указатель но голову списка
                        static struct hlist_head *ip_vs_conn_tab __read_mostly;
                        Убедил? =)
              • 0
                А зачем, имея /23, заморачиваться с EXIST_MAP?
                Ведь можно с каждой площадки отдавать /23 и свою /24. Тогда если вторая площадка отвалится, пропадёт её /24 и клиенты потекут на первую через /23 маршрут.
                • 0
                  Я же писал, что на кваге и так можно реализовать с разнымы весами на /24 и /23, но у нас вот так.
                  • 0
                    Под весами подразумевалась длина префиксов? Тогда пардон, не понял. У меня вес как-то проассоциировался с параметром weight.
                    И уточните, пожалуйста, что за параметр у Juniper при меньше 20 секунд считается фладом? Время, которое проходит между потерей маршрута и анонсом этой потери соседу по BGP, или что?
                    • 0
                      Да да, описался. Про параметр Juniper-а не отвечу, так как он у нас в одном из ДЦ аплинком и не принадлежит нам -естественно доступа на него нет. У нас в хозяйстве только Cisco.
                      • 0
                        Скорее всего имелось ввиду это BGP Flap Damping
                        • 0
                          Да, я тоже так предположил, потому и удивился. Эта функция ведь отключается. И выключена по умолчанию.
                          • 0
                            Да, по умолчанию выключена, но на EBGP пирах ее все же рекомендуют включать.
                          • 0
                            У нас этим занимается отдельный сотрудник — сетевой инженер, но термин «зафлапали» я слышал.
                    • 0
                      А какие коммутаторы в ДЦ используете?
                      • 0
                        Cisco Catalyst 3750-X
                        • 0
                          А почему выбран именно этот коммутатор?

                          И еще вопрос
                          «Как только мы видим из второго ДЦ, что связанность нарушена, мы начинаем с помощью NON-EXIST-MAP анонсировать вторую сеть /24 из первого ДЦ.»

                          А возможна у вас ситуация split-brains, то есть первый ДЦ думает что второй лежит и наборот? Если да, каким образом планируется из нее выходить?

                          И еще вопрос — чем вы все это мониторите?
                      • 0
                        А почему выбран именно этот коммутатор?

                        Даже не задумывались. Как бы я не ответил на этот вопрос — будет воспринято неоднозначно.

                        А возможна у вас ситуация split-brains

                        Очень маленький процент, включающий в себя отказ сразу одновременно нескольких точек мониторинга. Для этого существует многоступенчатый мониторинг, включающий не только опросы по сети, а и GSM модемы и пр.

                        чем вы все это мониторите?

                        Ничего удивительного — snmp, zabbix, графики — cacti
                        • +4
                          Фраза «PPS в секунду» звучит примерно как «км/ч в час».
                          • 0
                            Может, автор об ускорении говорит. Трафик растет типа.
                            • 0
                              1000000 pps/s — это, простите, пипец…
                          • 0
                            Дмирий, спасибо за упоминание Salt. Очень понравилась эта система.
                            Я бы сказал больше, но это уже будет глубоко ненормативная лексика — настолько велик мой восторг :)
                            • 0
                              Разработчики позиционируют ее как remote execute систему кстати, а не deploy. Я уже давно не захожу на машины — все делаю с сервера деплоя из строки salt.
                              • 0
                                В моем случае я смотрю на эту систему, как на альтернативу связке Puppet+MCollective. Так что как раз попадаю в ЦА авторов :)
                                • 0
                                  Для Puppet remote execute как альтернативу Mcollective можно использовать например fabric + puppetdb — довольно удобно.
                                  • 0
                                    А по сравнению с ansible она (Salt) как?
                                    • 0
                                      Наверно вопрос не ко мне, но имхо salt симпатичнее, но сильно молодой. Для уровня проекта пойдет, но не enterprise wide. Хотя смотря какой enterprise конечно…
                            • 0
                              Крайне интересно было бы услышать про тестирование такой системы.
                              • +2
                                В процессе. Готово процентов на 60%. Рвусь между сравнительной статьёй и статьёй про SALT.
                              • +1
                                Ну что, господа, начинаем бузить) Мы сделали свой прототип DCaaS.

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