Порядка 600 операторов в 4 странах, обрабатывающих звонки клиентов на американские (и немного на российские) номера.
Примерно 200 одновременных разговоров в пиковое время.
Примерно 15 000 звонков в день.
Возможность за несколько минут масштабировать это решение в несколько раз (по нашим прикидкам до ~1000 параллельных звонков, прежде чем начнутся проблемы).
Ну и конечно же, плотная интеграция с внутренними системами (CRM, сопровождение покупок, приоритеты операторов и клиентов и много-много других плюшек).
Кому интересно как это это работает и почему именно так — добро пожаловать
Сразу оговорюсь, вся эта конструкция росла и развивалась более 6 лет параллельно с постоянно меняющимися потребностями бизнеса, если бы мы её начали строить сейчас, может быть всё сделали бы иначе, но… оно такое какое оно есть и оно работает.
Для начала общая схема:
Провайдер телефонии по SIP подаёт звонки на наши сервера. Сервера, на данный момент, на Амазоне, но мы не сильно привязаны к нему, просто есть некие удобства, которые нам помогают решать некоторые задачи проще и элегантнее. Со стороны провайдера телефонии настроен failover: если не ответил один сервер, звонок автоматом пойдёт на другой, если и он – то на третий, и так далее (прописано реально 4 IP, используется сейчас 2, но, в случае чего, есть возможность быстро поднять новые основные ноды без шаманства с IP-шками и переписки с провайдером).
Сервер получивший звонок (один из основных серверов), смотрит какие сервера сейчас доступны и где какая нагрузка на них в данный момент, и либо обрабатывает звонок сам, либо кидает провайдеру SIP-redirect на другой наш сервер, который должен заняться этим звонком. Всего серверов сейчас 2+N:
2 – это два полностью взаимозаменяемых основных сервера, знающих как балансировать нагрузку, а так же держащие на себе некоторые дополнительные сервисы, такие как распределённая файловая система (glusterfs) для файлов, которые должны быть идентичны на всех нодах, memcache для локов, и всякую прочую мелочь. Эти две ноды запущены постоянно. N — остальные ноды, они запускаются по мере необходимости (по достижении неких пороговых значений количества одновременных звонков), на данный момент их тоже 2.
Каждый сервер, получив звонок, совершенно независимо от других, по мере необходимости, проводит его через IVR, в зависимости от разных факторов, прогоняет через различные очереди, закидывает звонок на voicemail (автоответчик), либо на конкретного человека (оператора) в одном из офисов, после чего держит этот звонок и пишет логи и статистику, ну и сам разговор, если разрешено, тоже пишет. В общем, проводит его через всю бизнес логику, которая реализована частично в MySQL-конфиге, частично в скриптах через agi интерфейс (которые опять таки смотрят в MySQL), частично curl-запросами.
Список звонков на каждом сервере свой, а вот IVR и очереди (и операторы, ожидающие звонка в очередях) дублируются на всех серверах. Соответственно, для некоторых действий, как например передача звонка из очереди свободному оператору, сервер сначала вешает лок на соответствующие объекты (на этого оператора) посредством curl-запроса на некий внутренний URL, на котором отвечает простенький скрипт с memcache за ним, и только если запрос вернул OK, это действие выполняется. Таким образом, к примеру, оператору не может поступить несколько новых звонков из очереди одновременно с разных серверов, но и нет необходимости синхронизировать между машинами полностью всё их состояние.
Оператор в свою очередь имеет на своём столе VoIP-телефон, у некоторых Soft-Phone, но это не приветствуется. При Soft-Phone страдает качество, нет нормального звонка и, если агент снял наушники и отвернулся от экрана (пьёт чай), он может пропустить звонок. Кроме того, тогда нужно более дорогое железо для компа агента (сейчас это low-end бездисковые станции на базе ubuntu, ~300$ за комплект) и заметно сложнее (а следовательно дороже) становится поддержка. Поддержка на Soft-phone дороже потому, что железные телефоны, в отличие от Soft-Phone, получают IP-адреса и ссылку на свою конфигурацию по DHCP. Конфигурацию им отдают всё те же сервера на Амазоне, после чего телефоны радостно и полностью самостоятельно регистрируются на серверах через VoIP-прокси стоящий в локалке. То есть работа по ручной настройке сведена почти к нулю (спасибо provisioning-у, который поддерживают все телефоны, что мы используем). Почти вся поддержка — это просто замена телефона. Софтверные же телефоны, как показала практика, в масштабах компании съедают довольно много времени на консультации пользователей, установку и настройку, решение проблем то со звуком, то ещё с чем-то, в общем 600 человек может создать нескончаемый поток проблем, если дать им инструмент для их создания… :)
Всё общение телефона с внешним миром происходит только через VoIP-прокси (+ прозрачный HTTP-прокси, для получения конфига). А вот VoIP-прокси уже общается с серверами на Амазоне, которые в свою очередь общаются только со со списком заранее прописанных на них проксей, с провайдером, и ни с кем более.
VoIP-прокси представляет собой тот же астериск, только в максимально упрощённой конфигурации: максимально упрощено и выкинуто всё что можно, его задача только пропускать траффик между телефонами и серверами через себя, пропихивая его через jitter-buffer (заметно повышает качество, когда траффик галяет через пол-глобуса), снимать часть нагрузки с серверов (и траффика) общаясь с телефонами локально, отвечать на всяческие тесты для отлова проблем, ну и всякая мелочь. Это же точка входа для операторов, работающих из дома (как правило линки домашних пользователей до Амазона заметно менее стабильны и быстры чем наш офисный, а вот линк до ближайшего офиса обычно в пределах той же страны и как правило очень хорош) для улучшения качества и упрощения схемы (все телефоны только через прокси). Кроме того, это ещё и «первая линия обороны». Хотя в ближайшем будущем мы скорее всего всё же перейдём на VPN подключения для таких пользователей, благо сейчас это уже не так дорого в плане железа как 5 лет назад.
Сами телефоны полносью взаимозаменяемы и практически не требуют ручной настройки: надо только присвоить ему IP и тип в DHCP, после чего всё происходит само: телефон получит IP и ссылку на конфиг, скачает его, затем, уже зная что и где, зарегистрируется через прокси на серверах. При этом новые телефоны автоматом получают некий новый внутренний номер (extention). Звонки с этого номера делать нельзя, звонки на него тоже не поступят, он может делать только некоторые совсем базовые вещи, среди которых возможность севшему за него оператору залогиниться на этот аппарат своим личным телефонным номером, после чего звонки для номера этого оператора начнут поступать именно на этот аппарат, и перестанут поступать туда, куда поступали раньше (если же оператор не залогинен нигде — звонки идут на его голосовую почту). Если по аналогии с сетевыми технологиями, extention телефона — это что-то вроде MAC-адреса, на который, после логина оператора вешается нечто вроде IP адреса — личный extention оператора. Таким образом достигается отвязка логики работы колл-центра от его физической составляющей: вся бизнес логика, отчёты, приоритеты и прочее производятся с extention-ом оператора (который по сути своей не является чем-то физическим, это скорее логический объект), extention самого аппарата — это просто точка, где в данный момент залогинен этот оператор.
Несколько слов об отказоустойчивости.
Общение с провайдером телефонии, как уже упоминалось, имеет механизм failover'a с их стороны. К сожалению нет фейловера в плане самого оператора. Во первых не так уж много самих операторов, способных предоставить всё что нам нужно, качественно, с поддержкой да ещё и по разумной цене. Во вторых, в телефонии, в отличие от IP-сетей, так просто, одним движением роутера на другого провайдера не перейдёшь. Перевести свои номера на другого провайдера — это время и деньги, не много, но при примерно 1500 номерах, это уже пара суток и ощутимая сумма денег на каждое переключение — дешевле дождаться решения проблемы. К счастью (к чести телефонистов) сбои у них бывают очень редко и очень ненадолго. Так что провайдеров дающих входящие звонки только два, один в США, один в России.
Cервера (астериски) всю, какую только можно (и немножечко остальной) конфигурацию кладут и читают как «realtime» в/из MySQL. Дабы заметить изменения сделанные другими серверами и через веб-интерфейс. Сначала это немного глючило, а астериск падал раз в несколько часов, но после нескольких патчей нам удалось сделать его более-менее стабильным. Таким образом, какая бы нода и каким бы образом ни сделала изменения в конфиге, следующее действие, зависящее от этого места конфига все ноды делают уже зная об изменении.
Каждые несколько секунд все ноды пишут своё состояние (количество звонков, load average, активные сервисы, и т. п.) в MySQL. Соответственно, ноды которые распределяют звонки, на основании этих данных определяют которые из серверов онлайн и кому лучше отдать звонок (а нагрузка меняется довольно плавно). Таким образом, пока жив mysql и хоть одна из основных нод, звонки будуи идти, всё будет работать и нагрузка будет распределена почти поровну между доступных машин (± небольшие пики, но как правило всегда есть некий запас). Если же, по какой-то причине, нагрузка на ноде начинает превышать некие разумные пределы, нода (посредством простеньких скриптов) сама начинает останавливать или блокировать на себе некоторые менее важные сервисы, дабы не допустить потери качества и стабильности собственно звонков. Так, например, первыми становятся недоступными всяческие тяжёлые отчёты.
В качестве MySQL у нас амазоновский RDS с включенным Multi-Availability Zone. И конечно же собственные off-site бекапы. В теории — если RDS Instance умрёт, всё должно переключиться само и без нашего участия. Не знаю, происходило ли это на практике или нет, уведомлений не получали, но пока всё работает как часики. Но даже если RDS умрёт совсем, в течение получаса можно снова запустить всё с ближайшего бекапа. Не будет статистики и логов (коей террабайты) и следовательно часть отчётов будут неверными, но вся конфигурация будет не старее полутора часов (ибо она-то как раз весьма компактна).
VoIP-прокси в офисах у нас стоят на роутерах под Linux. Роутеров 2 в каждом офисе, с heartbeat'ом запущенным между ними. Если один дохнет — все сервисы (включая и VoIP прокси) тут же начинают работать на втором роутере с тем же IP.
Провайдера у нас тоже 2 в каждом офисе. Если один дохнет/глючит — переходим на второго. Тут начинаются проблемы с регистрациями телефонов — IP ведь меняется (сервер должен знать на какой IP посылать звонки). Для этого каждый прокси раз в минуту ходит на сервера на Амазоне на некий специальный URL в веб-админке и рассказывает, что «мой текущий IP такой-то». С той же стороны, скрипт сравнивает это значение с тем что в базе и, если вдруг оно изменилось, обновляет в базе всё что с этим связано (напрмер текущие регистрации телефонов). Так как большая часть конфигурации realtime — уже следующее действие над многими объектами будет оперировать верным IP — звонки в/из этого офиса снова пойдут.
Вся эта конструкция (сервер со всем софтом, voip-proxy, и т. п. у нас оформлены в виде проектов в svn со своими инсталляционными скриптами, и разворачивается на свежем чистом линуксе просто методом svn checkout … && ./install.sh. Конечно требуется некое количество конфигурации, но оно довольно мало. Для серверов же на Амазоне есть ещё и up-to-date снепшоты, из которых можно быстренько развернуть +1 основной либо дополнительный сервер.
На каждой ноде есть apache+php и полный набор php скриптов и в плане веба все ноды полность идентичны (все ходят в один mysql, все используют memcached на основных нодах). Управлять и смотреть в realtime что происходит со всей этой конструкцией можно через веб-интерфейс на любой из машин. Веб интерфейс работает с тем же MySQL. Кроме того, некоторые команды посылаются asterisk-у на нужной ноде напрямую. Например «хочу послушать о чём говорят сейчас вот этот оператор и клиент» в веб-панели руководителя группы или департамента контроля качества (что незаменимо при обучении новых операторов и отлове недобросовестных — с деньгами же работаем).
На отдельном маленьком инстансе крутится nginx, который разруливает все внешние запросы к вебу (авто-конфигурация телефонов, обновление IP от проксей, доступ к админке/статистике) на реальные сервера. У этого инстанца есть снепшот и есть скриптик, который за несколько секунд может поднять новую такую же машину и навесить на неё старый IP, ежели будет такая необходимость. Эта машинка настолько простая и примитивная, что глючить и дохнуть там просто нечему, кроме железа. Последнее и решается вышеупомянутым скриптиком (пока кроме как для теста ни разу не применяли).
Ну и естественно мониторинг всего и вся с уведомлениями на э-мейл и на смс (о критичных ситуациях). Мониторим с помощью NetXMS + кучи самодельных скриптов, для сбора информации о компонентах всей этой конструкции.
О нагрузке.
Как ни странно, основную нагрузку создают вовсе не разговоры. Собственно разговоры с клиентами — это 15-25% нагрузки. Очень ресурсоёмкие действия с точки зрения астериска, это прежде всего music on hold и IVR. Когда клиент общается с оператором астериск всего лиш прокидывает траффик. Когда же клиент в очереди и слушает музыку и заверения в том как он важен и ценен, всё это время мы для него на лету создаём этот поток аудио. Если мы имеем 50 человек в очереди — мы вынуждены на лету создавать 50 потоков аудио для них, и уже эти потоки слать клиентам (это упрощённо, на практике часто это разные очереди, разные IVR, и т. п., то есть один поток им всем сразу не отдашь, как делали на некоторых телефонных станциях).
Довольно дорого обходится конвертация записей звонков (или голосовой почты) в mp3. Но если есть желание слушать это в браузере / на рабочем компе, а не только с телефона — то от этого не уйти. А желание есть. Да и место на дисках опять-таки экономится.
Ещё дороже обходится веб-интерфейс. Всяческие отчёты и realtime-статистика — это чёрная дыра по ресурсам. Везде где только можно понатыкано море agi-шек, которые пишут логи и собирают статистику для её дальнейшего использования во всяческих отчётах. Порядка 5MB разных данных на звонок, включая полный SIP-Trace звонка на случай, если что-то было не так.
Когда всё это обработано, можно получить море полезной информации. «Из каких штатов звонили этому агенту в вечернее время за последние 3 месяца?», «каков % пропущенных звонков у вот этого департамента?», «который раз нам звонит этот клиент в этом месяце?», «какой средний процент потерь SIP-пакетов при звонках в Пакистан?» и так далее.
Каждая внутренняя система компании (CRM, управление рекламой, фронтеды) запрашивает периодически всяческую realtime статистику. Например фронтенд проекта yaturist.ru показывает телефоны по которым можно поговорить с оператором только в том случае, когда очередь звонков этого проекта в данный момент обслуживает хотя бы 1 агент. В остальных случаях показывается формочка для подачи заявки в электронном виде.
Админка каждого проекта тоже может делать массу нетривиальных вещей через XMLRPC вызовы. Например, агенты которые лучше обслуживают клиентов получают больший приоритет при обслуживании очереди, но, после обработки некого дневного лимита продаж, получают меньший приоритет и получают звонки только если никого больше не осталось. Таким образом, им приходится более основательно работать с каждым клиентом, а не «брать за счёт оборота». Звонки VIP-клиентов, поступают только операторам имеющим право с ними работать, а если их нет на месте — то лучшим операторам из имеющихся онлайн. И всё это, на основании постоянно запрашиваемой статистики постоянно «подкручивается» из недр этих самых систем посредством кучи XLMRPC запросов, постоянно («на лету») “подкручивая” конфигурацию астериска (в MySQL).
Если запретить отчёты и показ статистики, отключить запись разговоров / голосовую почту и music on hold, временно заблокировать XMLRPC, по идее, даже в пиковое время, мы могли бы запустить это всё на одной ноде, и ещё остались бы ресурсы. Но… если бы мы это сделали бы, это потеряло бы смысл, ибо, по моему мнению, астериск как раз и прекрасен возможностью создания таких глубоких интеграций с бизнес-нуждами, которые можно постоянно «докручивать» одновременно с развитием компании. Как показала практика — это платформа, на базе которой можно построить что угодно, начиная от мини-атс на 3 человек, и до огромных инсталляций почти любого уровня сложности.
Закючение
Это скорее обзорная статья, многие технические детали остались «за кадром». Мы все слишком разные, я не знаю что именно Вам будет интересно. Если есть интерес к этой теме, задавайте вопросы. С удовольствием отвечу или даже опишу заинтересовавшие технические детали в отдельной статье.
Автор: Yozhiks