Хабрахабр

Мониторинг сетевого трафика на серверах в облаке

В Mars IS я отвечаю за мониторинг производительности приложений. Главный принцип, на котором основывается мониторинг производительности заключается в детальном централизованном анализе сетевого трафика между конечными пользователями и серверами, расположенными в наших дата-центрах. Он осуществляется в режиме реального времени и позволяет видеть объективную картину производительности как отдельных пользователей, так и приложений в целом. Подробнее о наших методах мониторинга и анализа можно прочитать здесь.

В частности, многие из приложений перемещаются на сервера в облачном дата-центре. Однако, мир не стоит на месте и наша компания, следуя общему тренду, начинает активно пользоваться облачными решениями. Думаю, результат может быть полезен и в других областях ИТ, где есть необходимость централизованного сбора и анализа сетевого трафика. Это перемещение вызывает определённые сложности в моей области и даже ставит под угрозу перспективу её существования.
В этой статье я хочу поделиться тем, как мне удалось разрешить проблему централизованного сбора и анализа сетевого трафика на серверах, расположенных в облачных дата-центрах.

Базовая конфигурация мониторинга

Интересующий нас трафик снимается со SPAN-портов граничных коммутаторов, стоящих между удалёнными пользователями и серверами дата-центра (см. Рисунок 1)


Рисунок 1

Там происходит распознавание отдельных приложений, пользовательских операций и самих пользователей, а также измеряется множество параметров их производительности. SPAN-порт отражает весь пользовательский трафик по заданным VLAN на сервер сбора статистики. Эти же сервера предоставляют доступ к статистике командам поддержки и отвечают за создание оповещений о неприемлемой производительности того или иного приложения. Собранные данные агрегируются и передаются на сервера для последующего анализа.

Конфигурация для мониторинга для серверов в облаке

Описанная выше система мониторинга удобно настраивается до тех пор, пока у нас есть доступ к сетевой инфраструктуре дата-центра. Метод перестаёт работать, как только приложение переезжает в облако. Установка физических серверов сбора статистики в облаке и настройка там SPAN-портов представляет собой дорогостоящую и трудно согласуемую задачу. Между тем, команды поддержки предпочитают иметь дело с единой системой мониторинга как для приложений внутри дата-центра, так и тех, что расположены в облаке.

Стандартно предлагается собрать конструкцию, показанную на рисунке 2. Оценив стандартные методы решения проблемы, предоставляемые сторонними компаниями, я понял, что меня не устраивает ни один из них.


Рисунок 2

Есть другое стандартное решение, основанное на средствах самой виртуальной среды (например, с помощью технологии Hyper-V). На виртуальные front-end серверы устанавливаются агенты vTAP (virtual network tap device), которые считывают и отсылают весь трафик на сервер сбора статистики посредством GRE-туннелей. Это допустимые подходы, но они весьма далеки от оптимального решения.

Конечно, можно было-бы терминировать GRE-туннели прямо на них, но нет гарантии, что собранный трафик не переполнит ёмкость линии связи между нашим дата-центром и облаком. Во-первых, нужно ставить дополнительный сервер сбора статистики на виртуальную машину в облаке, в то время как существующие серверы сбора статистики внутри наших дата-центров имеют существенный запас неиспользованной мощности. То есть, ошибка в конфигурации фильтра может привести к плачевным последствиям для всей инфраструктуры.
Во-вторых, нужно создавать отдельный туннель на каждый сервер приложения. Хотя решения и дают возможность фильтровать статистику на агентах vTAP, нет удобных механизмов контроля скорости создаваемого потока. Один из ведущих производителей требует 4Gb RAM и забирает одно ядро CPU. Управление большим количеством GRE туннелей представляет дополнительную задачу и потребует значительного внимания при поддержке.
В-третьих, удивляют системные требования для установки агентов vTAP. Помимо этого, нужно позаботиться о хостинге сервера для управления этим решением.
В-четвёртых, ежегодно нужно платить за каждый агент vTAP. Эти требования не сопоставимы со сложностью решаемой задачи. Поэтому стоимость решения становится слишком высокой для массового внедрения.

Наш метод

Сопоставив всё это с нашей текущей ситуацией, я решил действовать в соответствии со своим любимым правилом: «Не нравится, как есть – сделай как надо». Потратив около двух недель на разработку, мне удалось создать свой аналог vTAP – лёгкий, удобный, а главное, способный контролировать создаваемый поток данных. Я назвал его – ivTAP: Intellectual Virtual TAP device.
Принцип работы ivTAP показан на рисунке 3.


Рисунок 3

Она делает довольно простую работу. На каждый из серверов, где необходимо анализировать сетевую статистику устанавливается клиентская часть приложения – ivTAPclient. Каждый канал контролируется на предмет превышения предельно допустимой скорости. С помощью сетевого драйвера BPF и библиотеки libpcap/winpcap сканирует сетевые пакеты с заданным фильтром и пересылает их серверной части приложения – ivTAPsrv, предварительно упаковав их в канал UDP.

Сервер сбора сетевой статистики получает и обрабатывает эти пакеты наравне с теми, которые были сняты со SPAN порта коммутатора обычным путём. Серверная часть приложения извлекает пакеты из канала UDP и «подсовывает» их в любой из уже использующихся интерфейсов SPAN портов.

В результате получился продукт, решающий поставленную задачу и удовлетворяющий нашим требованиям:

  • Продукт не создаёт дополнительную угрозу ни для серверов приложений, ни для системы сбора сетевой статистики: не потребляет значительных ресурсов (CPU<1%; RAM <=512Mb) и не создаёт неконтролируемых выбросов нагрузки на линии связи WAN;
  • Данный метод снятия статистики не добавляет значимую ошибку в измерения производительности. В каждый момент времени максимальная ошибка определяется значением network jitter + 10ms на стороне ivTAPclient;
  • Программа работает устойчиво в течении продолжительного времени;
  • ivTAPclient запускается как обычный сервис на серверах Windows и не зависит от конфигурации среды сервера front-end;
  • Трафик, отражённый ivTAP, воспринимается сервером сбора статистики точно так же, как и полученный штатным путём через SPAN-порт;
  • Программа не нарушает никаких лицензионных соглашений: все манипуляции производятся на уровне операционной системы, не касаясь сервиса сбора и обработки сетевых пакетов;
  • При создании программы использовались только те библиотеки, которые допускают бесплатное коммерческое использование – LGPL, MIT, Apache v2.

Вот перечень инструментов и библиотек, которые я использовал для создания ivTAP:

Собственно, на чём и создавался продукт;
— Winpcap/libpcap;
— Библиотека jnetpcap. — Eclipse Java EE IDE. Обрабатывает один-единственный аргумент запуска программы;
— Бесплатный сервис-wrapper для Windows, чтобы с продуктом было удобно работать; Основа проекта, реализующая доступ к libpcap/winpcap из Java;
— Библиотека kohsuke.args4j.

Не вижу смысла переписывать здесь базовые принципы работы с этой библиотекой, а также приводить полный листинг программы. Для того, чтобы дальнейший материал был Вам полезен, рекомендую прерваться на этом месте и ознакомиться с описанием библиотеки jnetpcap. Сразу оговорюсь: я не профессиональный программист. Будет более разумно ограничиться демонстрацией нескольких наиболее важных и интересных выдержек. Заранее прошу прощения, если какой-либо фрагмент кода вызовет негодование со стороны уважаемых читателей.

В целом, концепция выглядит, как показано на рисунке 4:


Рисунок 4

Первое, что он делает в void main() – проверяет наличие ключа "–l" в аргументах запуска. Клиентскую часть программы реализует класс :IVTAP. Он выводит имена всех сетевых адаптеров, доступных на данном front-end сервере. Этот ключ используется один только раз для подготовки первого запуска программы. Метод представляет собой лёгкую модификацию примера, приведённого на сайте jnetpcap (см. Для дальнейшей работы нам понадобится выбрать тот адаптер, с которого мы хотим собирать статистику и указать его в файле конфигурации программы. Листинг 1).

private static void listDevices() Iterator<PcapIf> itrdev = alldevs.iterator(); while(itrdev.hasNext()) { PcapIf device = (PcapIf)itrdev.next(); StringBuilder sb = new StringBuilder(); sb.append(device.getName()); sb.append(";"); sb.append((device.getDescription() != null) ? device.getDescription() : "No description available"); sb.append("; MAC address:"); try { if (device.getHardwareAddress() != null) { byte[] mac = device.getHardwareAddress(); for (int i = 0; i < mac.length; i++) { sb.append(String.format("%02X%s", mac[i], (i < mac.length - 1) ? "-" : "")); } } else { sb.append("No MAC address"); } } catch (IOException e) { System.err.printf("Can't read MAC address"); } sb.append("; IP address(es):"); List<PcapAddr> addrs = device.getAddresses(); Iterator<PcapAddr> itraddr = addrs.iterator(); while(itraddr.hasNext()) { PcapAddr pcapAddr = (PcapAddr)itraddr.next(); sb.append(pcapAddr.getAddr().toString()); } System.out.printf("%s\n", sb.toString()); } return;
}

Листинг 1

Выбираем интересующее нас устройство и указываем его в файле конфигурации ivTAPclient.properties: В результате получаем перечень имён устройств, доступных для снятия статистики с их MAC и IP адресами.

sIntName=\\Device\\NPF_{7B767B766-A093-46CD-8000-EEEEEFFFFF88}
filterString=tcp port 80
srvAddr=<ivTAPsrv IP address>
srvPort=< ivTAPsrv UDP channel port number>
speedLimit=2000000
bandwidthCheckInterval=10
bandwidthBreachIntervals=6

Листинг 2

В данном примере программа завершит свою работу, если в течение шести измерений подряд, сделанных с 10-секундным интервалом скорость передачи будет выше 2 Mbps. Помимо этого, файл конфигурации содержит строку BPF фильтра для интересующего нас трафика, адрес и порт серверной части приложения ivTAPsrv, предельно допустимую скорость передачи (в битах в секунду), интервал проверки этой скорости (в секундах), а также, предельно допустимое количество последовательных нарушений speedLimit, требуемое для остановки программы во избежание переполнения линии связи WAN.

Программа предусматривает создание бесконечного цикла записи сетевых пакетов:

pcapIn.loop(-1, jpacketHandler, "ivTAP");

Создание экземпляра pcapIn:Pcap ничем особенным не примечательно, за исключением параметра timeout. Я выставил его в 10 миллисекунд, что даёт дополнительную погрешность, описанную выше. В идеале, было бы хорошо вообще обойтись без таймаута, но это может конфликтовать с базовыми принципами libpcap/winpcap. В документации написано, что установка нуля переводит Pcap в режим, когда данные не передаются пока не будет заполнен весь буфер записи. Величина этой задержки спорна и взята скорее из моих представлений о работе системы как наименьшая из безопасных.

Листинг 3). Перед созданием бесконечного цикла записи надо позаботиться об описании обработчика каждого записанного пакета (см.

public class PHandler<T> implements PcapPacketHandler<String> { //Avoid excessive instantiations within endless loop private Tcp tcp = new Tcp(); // Preallocate a Tcp header private Ip4 ip = new Ip4(); // Preallocate a IP header private int size; @SuppressWarnings("unused") private T user; private static Logger log = Logger.getLogger(PHandler.class.getName()); public PHandler(T user) { this.setUser(user); } public void setUser(T user) { this.user = user; } @Override public void nextPacket(PcapPacket packet, String user) { if (packet.hasHeader(ip) && packet.hasHeader(tcp)) { if (log.isLoggable(Level.FINE)) { log.fine("Received packet len=" + String.valueOf(packet.getCaptureHeader().wirelen()) + " source_IP=" + FormatUtils.ip(ip.source()) + " source_port=" + String.valueOf(tcp.source()) + " destination_IP=" + FormatUtils.ip(ip.destination()) + " destination_port=" + String.valueOf(tcp.destination())); } //preparing to send size = packet.size(); ByteBuffer byteBuffer = ByteBuffer.allocate(size); packet.transferTo(byteBuffer); IVTAP.bytesTransferred += size; if (IVTAP.bytesTransferred >= 8000000000000000000L) { IVTAP.bytesTransferred = 0L; } //sending UDP byteBuffer.flip(); try { IVTAP.udpChannel.send(byteBuffer, IVTAP.dstaddr); } catch (IOException e) { log.log(Level.SEVERE, "Exception: ", e); } } }
}

Листинг 3

С серверной стороны, наоборот: получаем пакет по UDP, отправляем в заданный сетевой интерфейс функцией sendPacket (см. Всё просто: читаем пакет и посылаем его по udpChannel в сторону серверной части приложения ivTAPsrv. Листинг 4).

try { DatagramSocket listener = new DatagramSocket(bindsocketaddr); try { DatagramPacket udppacket = null; System.out.println("waitnig for packets..."); byte[] message = new byte[65536]; udppacket = new DatagramPacket(message, message.length); while (true) { try { listener.receive(udppacket); if (pcapOut.sendPacket(udppacket.getData(),0,udppacket.getLength()) == -1) { System.err.println(pcapOut.getErr()); if (pcapOut.getErr().equals("send: Message too long")) { System.err.println("Disable TCP segmentation offload at the source interface"); } return; } } catch (IOException e) { e.printStackTrace(); } } } finally { listener.close(); }
} catch (SocketException e) { e.printStackTrace();
}

Листинг 4

Поскольку на клиентской и серверной частях приложения используются бесконечные циклы, нужно прописать ShutdownHook для соответствующих потоков и закрыть там все открытые устройства, сокеты и дочерние потоки. Ещё один важный момент, который надо учесть – это создание обработчиков завершения работы программы.

Через равные интервалы времени, определяемые в файле конфигурации, этот поток проверяет количество переданных данных в единицу времени. Функционал контроля нагрузки на линию связи реализован на клиентской части приложения ivTAPclient в отдельном потоке. При достижении граничных условий он просто останавливает запись и закрывает программу, делая соответствующие пометки в логе.

Используя подобный подход для снятия и централизованного анализа сетевого трафика, нужно учитывать некоторые ограничения, которые он накладывает:

  1. Мы фактически удваиваем нагрузку на линию связи, создаваемую пользователями измеряемых систем. Хотя front-end трафик отдельных приложений обычно не велик (мне почти всегда хватает лимита в 2 Mbps), нужно заранее позаботиться о проверке свободной полосы пропускания из облака к серверу сбора данных, расположенного в вашем дата-центре.
  2. Если сетевой jitter между ivTAPclient и ivTAPsrv превышает порог необходимой точности измерений, вы не сможете использовать это решение.
  3. На front-end сервере, где установлено приложение ivTAPclient нужно выключить network offload. Иначе собранный вами материал будет существенно искажён.
  4. Трафик, снятый со SPAN-порта, попадает в приёмную очередь пакетов сетевого адаптера (Rx). Трафик ivTAPsrv можно добавить только в исходящую очередь (Tx). Я так и не смог решить задачу перенаправления пакетов ivTAPsrv в Rx queue. Средствами libpcap/winpcap она не решается, нужно переписывать сетевой драйвер. С другой стороны, это означает, что если на сервере используются стандартные сетевые драйвера, то и сторонний сервис сбора статистики примет данные из обоих очередей. Д
  5. Если между пользователем и front-end сервером стоит reverse-proxy (например, на балансировщике нагрузки), то в анализируемом трафике вы не увидите реальных TCP-сессий пользователей. Соответственно, будет невозможно измерить их сетевые метрики (см. Рисунок 5)


Рисунок 5

Безопасность

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

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

Перспективы и выводы

Несмотря на серию успешных внедрений, я не собираюсь останавливаться на достигнутом. Есть ряд необходимых улучшений, которые помогут перевести эту разработку в категорию enterprise-приложений.

В частности, хотелось бы избежать необходимости обратной связи от ivTAPsrv к ivTAPclient. Прежде всего, нужно добавить средства мониторинга и оповещения о нештатных состояниях на ivTAPsrv, избежав при этом избыточного усложнения логики клиентской части приложения. В них указать объём считанной информации и время посылки этого контрольного пакета. Для этого будет достаточно периодически встраивать в поток передаваемых данных контрольные пакеты. Можно также создать web-интерфейс управления для ivTAPsrv, не утяжелив его чрезмерно. Таким образом, на сервере ivTAPsrv можно было бы обнаруживать нештатные отключения агентов ivTAPclient, оценивать временную погрешность или потерю UDP-пакетов статистики. Да и сам код, наверняка нуждается в свежем взгляде и оптимизации в соответствии с промышленными стандартами программирования.

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

Теги
Показать больше

Похожие статьи

Добавить комментарий

Ваш e-mail не будет опубликован. Обязательные поля помечены *

Кнопка «Наверх»
Закрыть