Как стать автором
Обновить
183.11

От NSX к OVN: 4 года подготовки и успешная миграция облака «на лету»

Уровень сложностиСредний
Время на прочтение25 мин
Количество просмотров1.1K

Привет, Хабр! Меня зовут Владислав Одинцов, я — техлид в K2 Cloud (ex Облако КРОК) и работаю в облаке с 2013 года, администрирую Linux и сети в нём, контрибьючу в Open Source (Open vSwitch, OVN и другие проекты). С 2015 года занимаюсь облачными сетевыми сервисами. В этой статье по мотивам моего доклада для конференции Highload++ 2024 расскажу об истории смены SDN в публичном облаке с проприетарного VMware NSX на Open Source решение OVN. Ввиду того, что текст статьи, в отличие от выступления на конференции не ограничен по времени, я решил разобрать некоторые моменты более подробно. Таким образом, даже если вы присутствовали на конференции или смотрели доклад в записи, вы, вероятно, сможете найти для себя что-то новое.

В K2 Cloud мы работаем по модели b2b, предоставляем сервисы IaaS и PaaS. Наше облако не базируется ни на VMware, ни на OpenStack, а разработка Облака идёт с 2009 года — тогда мы взяли за основу Open Source проект Eucalyptus, но со временем и от его кодовой базы остались только названия внутренних сервисов — всё остальное переписали. Так что на данный момент наше облако является полностью самописным.

Наши основные компоненты:

  • Linux в качестве операционной системы.

  • QEMU-KVM — эмулятор/гипервизор.

  • Python — на нём мы пишем наши сервисы.

  • MongoDB — основная база данных оркестратора.

  • Open vSwitch + OVN — основа наших виртуальных сетей.

  • Ceph.

  • и другие.

Мы стараемся использовать в основном Open Source технологии, но, где необходимо, не боимся применять и проприетарные решения.

Наше облако развёрнуто в трёх дата-центрах в московском регионе. Масштаб инфраструктуры:

  • 1,5+ тысячи серверов и коммутаторов в парке оборудования.

  • 50+ тысяч логических свитч-портов и 20+ тысяч роутер-портов в SDN.

Развитие сети в облаке: ретроспектива 2009-2014

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

Каждому пользователю в виртуальной сети соответствовал один VLAN ID. На выделенных сетевых узлах запускались LXC-контейнеры, которые каждому клиенту предоставляли изолированные сетевые сервисы. Там выполнялась маршрутизация, делался файерволлинг, DNS, бежал DHCP, и так далее.

Многие могут заметить проблему такого дизайна — эту инфраструктуру сложно масштабировать, потому что на большом количестве оборудования ровным слоем «намазано» огромное количество VLAN’ов. Такой дизайн подвержен проблемам, связанным с broadcast-штормами. Да и VLAN’ы — ресурс ограниченный, хоть на тот момент это ещё не виделось блокером к развитию.

И в 2011 году мы начали работать с этой проблемой, внедрив первый для нас SDN. Это было решение под названием Network Virtualization Platform (NVP) от компании Nicira — стартапа в Стэнфорде, который был, можно сказать, первопроходцем в SDN-строении.

В 2011 году мой коллега Лёша Фролов, перед обедом закоммитил первую поддержку NVP в нашем облаке и пошёл кушать.

Так выглядело облако на новой платформе по сравнению со старой на VLAN:

Мы отказались от VLAN между всеми хостами и поставили централизованный NVP-контроллер, который при помощи OpenFlow и OVSDB протоколов управлял агентами на хостах. Агент — это программный коммутатор Open vSwitch, который также разработан ребятами из Nicira, управляется этими протоколами и конфигурирует правила передачи трафика на конечных хостах. Трафик между виртуальными машинами и LXC-контейнерами на сетевых нодах бегает уже внутри оверлейных протоколов: STT и VXLAN, в отличие от предыдущего решения на VLAN, здесь используется IP-фабрика. Потому что пользовательский трафик упаковывается в оверлейный заголовок, затем — в UDP (в случае с VXLAN), и далее — в IP, чтобы быть отправленным как обычный пакет по underlay сети.

Обработка BUM-трафика (Broadcast, Unknown Unicast, Multicast) в NVP происходила на выделенных сервисных нодах. Такой трафик отправлялся им, и их задачей было размножить его в рамках L2-сегмента виртуальной сети.

Кроме того, мы предоставляем возможность клиентам из colocation подключаться к их собственной инфраструктуре в облаке из bare-metal-сегмента. После ухода от VLAN между всеми хостами мы уже не можем больше напрямую “прокидывать” VLAN в облако как раньше, и используем специальный коммутатор, который предоставляет L2 Gateway функции. Его задача — соединить VLAN клиента с его оверлейной сетью через VXLAN. Он пересылает пакетики между этими двумя сегментами.

Шло время, мы пользовались этим решением, а в 2012 году компания VMware приобрела очень быстро развивающийся стартап Nicira за миллиард долларов с небольшим.

В этот момент Nicira NVP-продукт переименовывается в VMware NSX-MH. Здесь MH (Multi Hypervisor) означает, что он может работать на множестве гипервизоров (QEMU, Xen, vSphere). Почти одновременно с этим появляется новый продукт VMware NSX-v, заточенный только на работу в рамках их vSphere-экосистемы. Время от времени мы, естественно, обновляем NSX, потому что нам нужно исправление багов, улучшение перформанса, получение новых фич. Но мы замечаем, что новые фичи появляются всё реже, стабильность продукта ухудшается, и как будто бы VMware смещает фокус с NSX-MH.

К 2015 году мы приходим с целым букетом проблем, вызванных использованием SDN в нашем облаке:

  1. Нерешаемые баги

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

  2. Производительность stateful firewall’а

    Stateful firewall в NSX-MH работал на базе OpenFlow learn action. Обработка логики происходила в user space части Open vSwitch. То есть на каждое приходящее соединение в рамках разрешающего stateful правила происходил learn и в OpenFlow создавалось правило, которое разрешало обратный трафик в рамках установленного соединения. По сути, менялись source и dest заголовки L3/L4. На большом Connections Per Second рейте соединения клиентов могли устанавливаться не с первого раза, потому что OVS мог быть попросту перегружен работой и долго обрабатывал эти события. Open vSwitch был сильно нагружен по CPU, и мы теряли процессорное время на обработку сервисной нагрузки, вместо того, чтобы продавать это просто ядрами на гипервизоре под клиентские виртуалки.

  3. Поддержка

    Поддержка не помогала решать эти проблемы, либо игнорировала нас. Нерешаемые баги было сложно поймать. А история со stateful firewall вовсе носила архитектурный характер. В целом решением могла быть только смена технологии обработки соединений. Вообще, из работы поддержки было хорошо видно, что продукт NSX-MH находится совсем не в их фокусе. Либо мы были не в фокусе поддержки :). Однажды, мы пытались разобрать с поддержкой проблему виртуалок, которые запускались, и у них не работала сеть. Нужно было собрать архив с отладочной информацией — стандартный support bundle. Запускаешь команду на ноде, она какое-то время думает и в конце говорит, где взять архив для отправки в поддержку. Его нужно было приложить к тикету для дальнейших разбирательств вендором. Я скачал архив на свой ноутбук, пошёл в окно загрузки архива и приложил файл к тикету. Далее мяч был на стороне VMware. Через неделю молчания мы решили поинтересоваться, как дела с нашей проблемой, на что в ответ получили, что происходит анализ и нужно подождать.

    Спустя неделю история повторилась. И ещё раз. Далее наше терпение лопнуло и мы стали названивать менеджеру в VMware с вопросами, “ну сколько можно?”. Он эскалировал проблему и в течение пары дней нам пришёл ответ:

    Что в переводе на русский означает: мы только сейчас открыли присланный вами архив и не увидели в нём support bundle. Но зато нашли МРТ коленки Владислава! Вам нужно было аккуратнее кататься на сноуборде! Тут нужно немного пояснить: я, загружая архив в тикет, промахнулся при выборе файла и приложил не тот архив: вместо собранного дампа загрузил архив своего МРТ, который получил накануне.

    В общем, мы были [не]приятно удивлены, что нам рассказывали, как инжиниринг занимается нашей проблемой на протяжении нескольких недель, а по факту просто ничего не делал, хотя проблема для нас и клиентов была очень неприятная.

    Итак, это было небольшое лирическое отступление, давайте вернёмся к списку пунктов, почему нам перестал нравиться NSX-MH.

  4. Закрытое решение

    Поддержка нам не помогала, а мы, как слепые котята, даже не могли ничего сделать, потому что исходники были закрыты и было непонятно, как это всё работает изнутри. Также, нельзя было и развивать этот SDN, потому что максимум, что мы могли сами сделать, это написать feature request, который положат в тумбочку.

    Единственным положительным моментом было то, что Open vSwitch – это open source компонент и его исходники общедоступны. Когда мы разбирались с проблемой переполнения OpenFlow таблицы для того самого Stateful Firewall’а, о котором я рассказал выше, нам сыграл на руку этот факт. Мы смогли разобраться в проблеме и довольно быстро грязно подхакать починить код Open vSwitch. Дело было в том, что временные правила, разрешающие обратный трафик, создавались с idle_timeout=65k. В нормальной ситуации правило удаляется, если в рамках tcp-потока прилетит пакет с выставленным tcp FIN-флагом. Однако этот механизм по разным причинам срабатывал далеко не всегда (да и был ограничен только tcp-протоколом) и далее оставалось надеяться только на то, что правило удалится по истечении чертовски долгого idle_timeout. Мы переопределили в коде OVS максимально возможное значение этого timeout на 300 и стало сильно легче. Проблему подлатали.

  5. EoL

    Самое главное, что продукт стал End-of-Life. Мы окончательно убедились, что куда-то в любом случае придётся переезжать, и переезд будет очень тяжёлым.

  6. Нет нативной миграции на новое поколение NSX-T

    Кроме того, в отличие от NSX-v, VMware не предоставляла никакой возможности нативной миграции на их следующее поколение — NSX Transformers. Трудозатраты по переходу с NSX-MH на новое поколение были сопоставимы с переходом на любой другой SDN, и мы решили повыбирать.

Выбираем новое решение

Мы начали с требований к новому решению:

Поддержка всех используемых фич NSX. Обязательно нужна поддержка всей функциональности, которой уже пользовались на базе NSX:

  • L2 overlay;

  • stateful FW, чтобы предоставлять клиентам функциональность секьюрити групп;

  • высокопроизводительный L2 gateway, чтобы соединить colocation клиентов с виртуальной сетью в облаке.

Наличие дополнительных фич SDN. Кроме того, мы хотели не стоять на месте, а развивать наши сервисы, улучшать их качество, и смотреть вперёд, чтобы использовать уже новые, современные на тот момент фичи SDN:

  • Distributed Virtual Routing

  • NAT

  • Load Balancers with Health Checks

  • etc.

Open Source. Нам была важна независимость, живое комьюнити и наличие референсных внедрений. Мы однозначно поняли, что не хотим больше зависеть от дядь из заморских компаний, нам нужен технологический суверенитет. А ещё хотелось, чтоб продукт где-то уже работал до нас, чтобы мы не были первопроходцами, которые словят все zero-day баги.

Выбор SDN

В 2016 году мы плотно занялись выбором SDN. Выбирали из трёх популярных на тот момент Open Source решений. Ниже представлена таблицы, скорее, для устрашения. На ней приведена лишь часть критериев, по которым мы выбирали SDN:

  1. OpenDayLight

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

    Самое главное, что решение, по крайней мере, на тот момент времени, не поддерживала Port Security — вид файрвола на уровне портов виртуальных машин.

    Всё, что делает OpenDayLight в нужном нам варианте, — при помощи плагина управляет Open vSwitch по OVSDB и OpenFlow (почти то же, что делает NSX и OVN). У него есть core engine, написанный на Java, куда плагинами можно устанавливать разные расширения. 

    OpenDayLight не впечатлил, и мы продолжили поиски. 

  2. Open Contrail / Tungsten Fabric

    Сейчас у него уже третье название — OpenSDN. 

    На момент выбора проект переходил из OpenContrail в Tungsten Fabric под эгиду Linux Foundation и испытывал трудности с деплоем вне OpenStack экосистемы — мы потратили приличное количество времени. Обращались к ребятам из комьюнити и нам подсказали, что для нашего решения нужны доработки как по деплою, так и по самому Tungsten Fabric. Кроме того, проект очень громоздкий, у него огромное количество строк кода, он написан на разных языках программирования.

    К слову, впоследствии, летом 2023 года, из-за низкой активности сообщества сообщил о приостановке всей инфраструктуры проекта (официальная версия).

    Но нашлись добровольцы, которые по сей день тащат проект вперед — можно пожелать ребятам только успеха!

    А мы же продолжили искать.

  3. Open Virtual Network (OVN)

    Я для себя считаю OVN, "нативным" SDN для OVS. Он родился в рамках Open vSwitch комьюнити, и писали его те же ребята, которые изначально разрабатывали Open vSwitch (соответственно, NVP и NSX). Отчасти поэтому он похож на NSX, в котором те же ресурсы и компоненты. OVN оказался достаточно легковесным, легко задеплоился. У Open vSwitch и OVN хорошо написанная документация, достаточно активное сообщество, которое лидирует RedHat, а также принимают активное участие NVidia, Canonical, stackit.cloud и другие. Впоследствии, мы тоже присоединились. Итак, мы проверили на Proof-of-Concept, что облако на OVN предоставляет всю функциональность, и удовлетворяет всем требованиям которые нам важны. Так наш выбор пал на OVN.

Архитектура OVN

Немного погрузимся в то, что OVN из себя представляет. Вспомним архитектуру NSX.

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

Есть «северная» база данных OVN_Northbound, которая предоставляет API для взаимодействия с OVN из облака (CMS — Cloud Management System). Чтобы с ним взаимодействовать, в нужных таблицах создаются определённые записи, декларативно описывающие сетевую виртуальную топологию, которая нам нужна.

Есть ещё «южная» база данных (OVN_Southbound), которая хранит «кишочки» SDN — внутреннюю информацию о том, какие существуют гипервизоры, под какими адресами они зарегистрированы и так далее.

Кроме того, существует сервис ovn-northd, который вычитывает инфу из «северной» базы данных, компилирует логические flow и складывает их в «южную» базу данных. Далее этой информацией пользуются уже агенты на гипервизорах, конфигурируют в локальных OVS’ах правила обработки трафика — при помощи OpenFlow. При необходимости ovn-northd «наверх» в «северную» базу данных записывает какие-то данные, которые могут быть считаны CMS.

Агенты OVN — непосредственно на вычислительных и сетевых хостах. Это ovn-controller’ы, которые подключаются «наверх» к «южной» базе данных, репортят о себе необходимую остальному кластеру информацию: IP-адреса, типы поддерживаемых инкапсуляций, помечают порты (port-bindings) как «затребованные» (claimed) на данной ноде и так далее. Затем вниз по OpenFlow и тому же OVSDB протоколу конфигурируют Open vSwitch так, чтобы на данном хосте корректно обрабатывался входящий трафик к/от виртуальных машин, передавался в туннели и так далее.

В целом, можно сделать вывод, что архитектура облака на NSX и OVN весьма схожа. И в OVN, и в NSX есть центральные компоненты: NVP Controller в NSX и «северная» и «южная» базы + ovn-northd в OVN:

Есть агенты на хостах, управляющие трафиком. Есть L2 Gateway, который занимается пересылкой трафика между клиентскими VLAN и VXLAN сегментами. Он, кстати, работает по стандартизированной схеме, которая называется Hardware VTEP. От железных коммутаторов, работающих с NSX, в принципе, можно ожидать, что они заведутся даже с OVN (но по факту нужно тщательно тестировать).

Теперь давайте интегрируем OVN с облаком.

Дружим облако с OVN

Сначала подумаем, как его задеплоить.

Deploy: кластеризуем центральные компоненты

Централизованный компонент (две базы и ovn-northd) нет жёсткой необходимости деплоить отказоустойчиво/высокодоступно. Сеть будет работать корректно и без центрального компонента, но есть нюансы. Например, базы будут недоступны, и в облаке произойдут какие-то изменения — создастся новый порт или изменится правило Security Group или ВМ переедет между гипервизорами. В этом случае пока БД будут недоступны, эти изменения не отразятся на остальных хостах, и сеть будет работать непредсказуемо. Мы всегда деплоим только HA-кластер с RAFT, потому что один раз написали код деплоя и забыли об этой задаче, тем более, что деплоится это всё очень несложно.

ovsdb-server нативно поддерживает кластеризацию. То есть серверы можно запустить в нескольких экземплярах, объединить единый RAFT-кластер, в котором будет один лидер и несколько follower’ов:

При этом все операции записи происходят на лидере, а чтение можно производить и с фолловеров и с лидера.

ovn-northd тоже умеет быть высокодоступным. Это очень примитивная кластеризация. Он работает в Active-Standby режиме. При запуске northd пытается в «южной» базе взять lock (блокировку). Если ему это удаётся, он переходит в Active режим. Если нет, значит, lock уже держит другой northd и в кластере есть активный экземпляр, значит, этот экземпляр переходит в Standby. Далее он периодически будет пытаться взять lock в надежде, что другой (активный) northd остановился либо крашнулся по какой-либо причине.

Deploy: подключение агентов

Мы подключаем ovn-controller’ы к «южной» базе по сети при помощи TLS. Бонусом получаем возможность контроля доступа на основе ролевой модели на OVSDB-сервере.

Зачем это нужно? Допустим, какой-нибудь злоумышленник прорвался из виртуальной машины на гипервизор, получил root-доступ, а вместе с ним — доступ к приватному ключу и сертификату (чтобы такого не произошло, в нашем облаке есть целый отдел, занимающийся ИБ, а здесь мы рассматриваем гипотетическую ситуацию, чтобы объяснить работу RBAC в OVN). Он может с их помощью подключиться к «южной» базе данных и внести в неё изменения, чтобы навредить всем остальным клиентам, сломать их инфраструктуру и так далее. С включённым ролевым контролем доступа на OVSDB сервере будет проверяться hostname в сертификате, с которым пришёл клиент (поле Common Name). Максимум, что злоумышленник сможет изменить, — записи, которые относятся непосредственно к этому серверу. А если попытается изменить записи чужих гипервизоров, получит permission denied.

Deploy: Hardware VTEP

Мы используем коммутаторы от Mellanox NVIDIA. На борту у них — Cumulus Linux, это сетевая операционная система, в настоящее время разрабатывающаяся внутри NVIDIA исключительно для их коммутаторов. Эти коммутаторы, по сути, whitebox серверы с x86 процессором внутри и подключённым к нему ASIC на большое количество портов.

Мы размышляли, где лучше запустить ovn-controller-vtep, который будет конфигурировать этот коммутатор. Если сделать это на сервисных виртуальных машинах, значит для него нужно кластерное решение по типу Pacemaker, с которым связываться у нас не было совсем никакого желания. Но позже у нас родилась идея — а не запустить ли ovn-controller-vtep просто в контейнере прямо на этом коммутаторе? Он у нас x86, а значит, даже не нужно будет разбираться с компиляцией под другую архитектуру и операционную систему. И внутри контейнера мы можем использовать наш стандартный дистрибутив в качестве окружения. Таким образом, мы смогли довольно банальным способом решить проблему обеспечения доступности управляющего агента (ovn-controller-vtep) для HW VTEP коммутатора.

Изменения в коде облака

Изменения в коде облака, которые нам необходимо было сделать:

  • Старый код управления NSX «обернуть» в NSXDriver.

    В первую очередь мы взяли старый код работы с NSX и «обернули» его в абстракцию в NSXDriver. Имплементировали для него стандартный интерфейс, который дальше будем имплементировать для нового SDN.

  • Написать новый код в OVNDriver, используя библиотеку ovsdbapp.

    Мы пишем на Python и используем библиотеку ovsdbapp, которая предоставляет достаточно удобный интерфейс работы с высокоуровневыми OVN-сущностями (Logical Switch, Logical Router, ACL и т.д.).

  • Добавить в облако возможность выбора драйвера на уровне отдельной AZ.

    На последнем этапе добавили возможность выбора драйвера, на котором сейчас должна работать Зона Доступности облака. Мы деплоили один SDN-кластер в одной availability-зоне, соответственно, решили, что на уровне конфигурации этой AZ будем описывать выбор SDN. На тот момент (примерно 2020-2021 год) для нас это было полезно. Ведь мы как раз вводили третью зону доступности в облако, и решили сразу запускать её на OVN, чтобы не пришлось мигрировать новую зону на новое SDN-решение в будущем.

Соединение с другими AZ

Взаимодействие разных зон доступности происходило внутри LXC-контейнеров на тех самых сетевых нодах, которые стоят на границе SDN и предоставляют к нему универсальный маршрутизируемый доступ с т.з. удалённых зон доступности.

Например, ВМ в зоне доступности 1 (под управлением NSX) хочет пообщаться с виртуалкой в другой зоне на OVN. Она просто маршрутизируется через сетевые ноды, а меж-АЗетная связь между ними управляется нашим кодом, без SDN, при помощи OpenFlow. Таким образом, они могут спокойно взаимодействовать, даже не зная и не догадываясь, какие SDN-решения находятся в других зонах доступности.

Тестирование

Прежде чем внедрять это в продакшен, мы хотели проверить и убедиться в доступности для клиентов в рамках OVN всей необходимой функциональности, которую мы предоставляем с NSX. Помимо стандартных юнит- и end-to-end тестов, мы придумали connectivity тесты.

Connectivity-тесты 

Мы собрали [почти] все возможные кейсы построения связанности в облаке — всю функциональность и варианты деплойментов инфраструктур клиентов с точки зрения сети. Затем автоматизировали их создание. Далее тест заходит по SSH на виртуальную машину и гоняет пинги, копирует файлы по SSH, проверяет работу Jumbo и так далее.

Так мы покрыли функциональность:

  • VPC;

  • VPNaaS;

  • Внешние Сети (тот самый L2 Gateway сервис между облаком и физической инфрой клиента).

Тут всплыл интересный момент — как раз благодаря этим тестам мы увидели разницу в поведении stateful firewall между NSX и OVN. Дело в том, что OVN использует под капотом Linux conntrack для отслеживания соединений, а NSX использует OpenFlow learn action. Linux conntrack показал гораздо более агрессивную фильтрацию пакетов по сравнению с OpenFlow learn action: он анализирует гораздо больше факторов, например, сверяет, что полученный пакет в рамках соединения приходит с правильными TCP-флагами. Или что TCP-соединение открывается корректной последовательностью пакетов. Но в нашем случае его срабатывание было ложноположительным из-за особенностей ассиметричного прохождения трафика внутри виртуальной сети. Нам нужно было немного подкрутить настройки OVN — отключить параметр use_ct_inv_match. Мы были рады, что заметили эту особенность как раз благодаря нашим connectivity-тестам до того, как развернули систему в продакшен.

Нагрузочные тесты control plane

О проблемах control plane на OVN не слышал, наверное, только ленивый — что он плохо скейлится, что у него не всегда быстрая и стабильная сходимость в конфигурациях, что есть потенциальные проблемы масштабирования, связанные с высокой нагрузкой на:

  • ovn-northd (долгая сходимость оверлейной сети);

  • ovsdb-server, обслуживающий "южную" базу данных + потенциальные проблемы с горизонтальным масштабированием;

  • ovn-controller (долгая сходимость конфигурации на конечном сервере).

Мы решили это проверить на своём скейле. Для этого подняли дамп прода в лабе на OVN. И… часть проблем, которые мы проверяли, подтвердились! Нужно было разобраться в них.

Проблемы с таймерами

Большая часть решения проблем свелась к тюнингу различных таймеров. Было два основных набора: таймеры, связанные с OVSDB, и таймеры, связанные с OpenFlow.

OVSDB

RAFT cluster election timer

Этот таймер указывает время, через которое каждый член кластера должен отправлять проверки и, если проверка не прошла, нужно провести голосование для выбора нового лидера. Так как ovsdb-server — однопоточное приложение (было на тот момент времени), стоит выбирать election timer таким, чтобы блокирующие операции сервера не приводили к ситуации, что внутреннее сообщение RAFT heartbeat не успело отправиться за этот самый election timer. Мы для своего деплоймента используем значение в 20 секунд (вместо дефолтной 1 секунды), но, например, на крупных обновлениях схемы данных, зная, что конвертация может занять больше времени, чем election interval и при этом самостоятельно следя за кластером, временно увеличиваем это значение до 60, 120 и даже 180 секунд.

В итоге, с такими настройками у нас корректно работают несколько боевых кластеров ovsdb OVN_Southbound базы, на них не происходят false-positive перевыборы лидера. Ниже график переключений leader/follower для одного из них за неделю.

Из графика видно, что переключения в кластере происходят до трёх раз в сутки — это нормальная работа кластера. Такое поведение связано с выполнением процедуры «схлопывания» (compaction) лога БД. Дело в том, что изначальный файл с данными OVSDB хранит один JSON-объект всей базы данных. При обработке изменений в базе ovsdb-server дописывает в файл изменения, применённые относительно первичного создания БД. Таким образом в процессе работы сервиса, его файл с БД «отрастает». По алгоритму compaction запускается на ноде, если объём изменений в логе превысил половину размера БД, либо с момента последнего compaction прошло более 24 часов.

К слову, для работы больших кластеров крайне рекомендуется использовать ovsdb-relay. Это отдельный набор серверов, который позволяет убрать большую часть нагрузки на OVSDB-кластер (которая в OVN приходится на чтение). Поддержка релеев появилась в версии OVS 2.16, улучшилась в следующем релизе 2.17. А процесс compaction с версии 3.0 происходит в отдельном треде, что делает работу кластера ещё стабильнее. Даже не смотря на то, что перед «схлопыванием» базы (если оно происходит на лидере), лидер отправляет в кластер сообщение о необходимости выборов нового лидера. Так обеспечивается, что compaction происходит всегда только на follower’е.

OVSDB inactivity probing

Inactivity probing — это процесс проверки между двумя сторонами соединения. Он нужен, чтобы определить, что соединение по какой-то причине больше не работает и его нужно закрыть и попробовать подключиться к другому серверу. В иной ситуации, если не проверять живость соединения, то можно вечно ждать данных из него — получения апдейтов или ответа на отправленную транзакцию. При этом ovn-controller не выставляет тайм-аут на OVSDB транзакции (можно зависнуть в ожидании навсегда).

Каждый inactivity probe интервал одна из сторон OVSDB соединения отправляет ping внутри OVSDB сообщения и ожидает получить в ответ pong. Процесс проверки «живости» соединения является двунаправленным, поэтому inactivity probe конфигурируется индивидуально с каждой стороны соединения: с активной стороны – та, которая инициализирует подключения и с пассивной – та, которая ожидает подключения к себе.

Настройки пробинга с разных сторон соединения могут быть разными: например, сервер может проверять своих клиентов раз в две минуты (или не проверять вовсе), а клиент может проверять сервер каждые 10 секунд. Таким образом, пробинг будет выглядеть так: каждые 10 секунд клиент отправляет ping на сервер, а сервер отвечает pong. Так как в данном примере значение inactivity probe сервера больше чем у клиента, то между ними будут ходить ping сообщения от клиента и pong от сервера. Серверу отправлять свои ping’и нет никакой необходимости — он и так из клиентского ping знает, что тот жив.

Значение по умолчанию в 5 секунд подходит лишь для небольших кластеров или тестовых сетапов. В более-менее крупных кластерах нужно увеличивать это значение. Мы когда-то давно увидели рекомендацию от RedHat про 60 секунд, решили её попробовать и до сих пор живём с этим значением без особых проблем. Однако, в ситуации, если в ovsdb-сервер прилетает какая-то очень большая транзакция и так случилось, что ему нужно разослать её сразу многим клиентам, может произойти, что ovsdb-server (в нашем случае relay) под очень большой нагрузкой не успевает разослать все апдейты + разослать свои пинги клиентам + ответить на пришедшие пинги. В результате соединение разрывается и клиент, который не дождался ответа переподключается к другому релею. Иногда это может иметь эффект снежного кома – клиенты переподключаются к серверам, перегружают их и проблема усугубляет сама себя, поэтому крайне важно правильно сконфигурировать значение inactivity probe на всех участках, где есть OVSDB:

  • CMS ⇿ NB DB

  • NB DB ⇿ ovn-northd

  • ovn-northd ⇿ SB DB

  • SB DB ⇿ SB DB Relay

  • SB DB Relay ⇿ ovn-controller, ovn-controller-vtep

Кстати, выставление очень большого значения в inactivity probe не является чем-то запретным. Главное, для соединений по сети не отключать его совсем. Нужно помнить, что это — время реакции на возможный обрыв соединения между клиентом и сервером.

OpenFlow inactivity probe

Для OpenFlow, который бегает между ovn-controller и ovs-vswitchd, была актуальна та же проблема с inactivity probe. У него такой же дефолт — 5 секунд. И тут с ним возник интересный нюанс. Мы тюнили его дважды. Ну ладно, если бы просто сначала подняли до недостаточного значения, но нет! Сначала мы его увеличили до крупных значений (120 секунд). А потом, разобравшись в теме глубже поняли, что его здесь вообще нужно отключать! Всем рекомендую отключать OpenFlow (да и OVSDB) пробинг на Unix сокетах, потому что это и так надёжное соединение, его не нужно пробить! В случае, если какая-то из сторон отвалится, другая об этом моментально узнает. А вот если вдруг вы решите использовать такую фичу OVN как ha-chassis-group с более чем одним chassis, пусть вас не посетит мысль настроить пробинг на OpenFlow!

Я об этом подробнее расскажу в каком-нибудь отдельном докладе/статье. Но суть в том, что при false-positive срабатывании таймаута inactivity probe на OpenFlow соединении на хосте, обслуживающем “primary” ha-chassis-group, триггернётся механизм HA. В результате вы получите false-positive переезжание Gateway-портов со всеми вытекающими фейерверками в виде сетевой недоступности, красного мониторинга и писем “счастья” от клиентов. В общем, я вас предупредил и отключил пробинг на стороне OVN по умолчанию. Не забудьте отключить пробинг со стороны OVS!

Сюрприз с OVSDB IDL

OVSDB IDL (Interface Definition Language) — in-memory-копия базы данных. Отправляет модификационные запросы в ovsdb-server и парсит ответы, обновляя реплику БД в памяти. Все операции чтения происходят локально, мы обращаемся к своей памяти. А если хотим что-то изменить, отправляем транзакцию на OVSDB сервер.

Раньше с NSX у нас был HTTP REST API. Мы на каждый запрос инстанцировали HTTP-сессию, отправляли к серверу (NSX Controller’у) запрос и возвращали ответ клиенту. Изначально взяли этот же подход в работе с OVN. На маленьких скейлах всё было хорошо, но когда мы начали тестировать на более крупных масштабах с сотнями и тысячами ресурсов в базе данных, появилась проблема.

  1. Мы (сервис виртуальной сети) получаем запрос на какую-то модификацию, например, хотим создать порт виртуальной машины.

  2. Поднимается IDL

    1. отправляем запрос на получение БД. Говорим базе: «Уважаемая база, хотим к вам подключиться!».

    2. получаем большой ответ с содержимым БД: ovsdb-server нам присылает содержимое запрошенной базы. Мы сериализуем весь JSON, который она присылает, в объекты python.

  3. Отправляем запрос в ovsdb-server на модификацию данных.

  4. Получаем подтверждение, обновляем реплику в памяти, возвращаем к клиенту.

Чем больше база, тем дольше второй этап. Это не очень масштабируемый подход, который мы решили починить так:

Написали новый сервис, который инициализирует IDL лишь один раз на старте и предоставляет внутренний JSON-RPC API поверх http (назовём его просто OVN API), в который обращается сервис виртуальной сети. Время холодного старта нас, по большому счёту, не сильно волнует, поэтому пусть база себе растёт, мы подождём, пока она загрузится в память, ведь на случай перезагрузки у нас есть другие экземпляры этого сервиса, спрятанные за active-backup балансировщиком — он [почти] полностью stateless! С новым подходом работа выглядит следующим образом:

  1. Отправляем в сервис виртуальной сети запрос на модификацию.

  2. Пересылаем запрос в OVN API, там у нас обычный HTTP endpoint.

  3. Транзакция отправляется в OVSDB сервер.

  4. Нам возвращается ответ, мы его дальше по цепочке возвращаем клиенту.

Мы внедрили это в продакшн в третью (новую) зону доступности, и всё «полетело».

Миграция прода

Нас не покидала мысль, что очень дорого и страшно поддерживать сразу два SDN в продакшене с точки зрения разработки и тестирования, да и риск ошибок выше. Поэтому решили перевозить все инфраструктуры клиентов из NSX в OVN.

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

  • Безопасность. Нельзя нарушать принципы изоляции сетей или как-то менять правила firewall’а.

  • Незаметность. Клиент не должен ощутить, что у него под ногами меняется фундамент, максимум какое-то стандартный даунтайм на обслуживание (live миграцию).

  • Атомарность. Мигрируем минимально возможными цельными порциями, чтобы в случае ошибки failure domain был минимальным. Перевозим по одной ВМ сразу со всеми её портами.

  • Надёжность и управляемость. Нужен качественный тулинг с возможностью отката в любую точку. Должны быть предусмотрены роллбеки и реакции на нештатные ситуации, чтобы максимально снизить потенциальное влияние на инфраструктуру клиента.

Таким образом, в нашем облаке появляется «Миграционный режим»: в одной AZ работаем сразу с двумя SDN, поддерживаем связь между разными ВМ, подключенными к сети в разных SDN. Облако знает о всех SDN в зоне доступности и умеет работать с виртуалками одного VPC, одной сети в разных SDN, обеспечивая им постоянную сетевую доступность.

Переезд в OVN

Переезд в OVN выглядит так. Есть момент 0, когда все ресурсы находятся на хостах под управлением NSX. Внизу на схеме — наш underlay (серверы, коммутаторы), в верхней части — overlay (VPC, виртуальные машины, сети, LXC контейнер, в котором происходит маршрутизация).

Напротив этого мы строим OVN кластер, то есть под управлением OVN находятся серверы-гипервизоры сетевые ноды и HW VTEP коммутаторы, через которые  будут соединяться виртуальные сегменты в разных SDN.

Далее VPC за VPC мы перевозим в OVN. Если кто-то не знает, VPC — это Virtual Private Cloud в терминах облачных провайдеров — изолированный сетевой сегмент, виртуальный роутер с сетевыми сервисами, где клиенты запускают свои сервисы.

Первым делом мы «растягиваем» каждую сеть через отдельный VLAN в новый SDN, обеспечивая L2-связанность между ними.

Далее мигрируем LXC-контейнер. Это стандартная операция, когда контейнер переезжает между нетворк-нодами. Разница лишь в том, что при этой миграции контейнер отсоединяется от старого SDN и подключается уже к новому. С этого момента трафик в Интернет, VPNaaS, маршрутизация и другие сетевые сервисы для виртуальных машин, оставшихся в NSX, работают через OVN сегмент.

Далее инстансы (они же виртуальные машины) мигрируем в live-режиме с гипервизора под управлением NSX на гипервизор в OVN, вынимаем порты из старого SDN и вставляем в новый.

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

Проблемы на пути к OVN

Естественно, у нас были проблемы на пути к OVN. Одни легко решались просто cherry-pick’анием коммитов из более новых версий, другие мы описывали на mail-листе разработчиков, нам присылали патч и это помогало. Но были проблемы, с которыми мы оставались наедине.

Именно о последних я хочу рассказать подробнее. Первый набор проблем связан с L2 Gateway, тем самым коммутатором Hardware VTEP, который обеспечивает трансляцию VLAN в VXLAN.

L2 Gateway

L2 Gateway — сервис, помогающий объединить виртуальный L2-сегмент в облаке с VLAN’ом клиента в colocation или на удалённой площадке. Дело в том, что там тоже были баги, причем как в ovn-northd, так и в ovn-controller-vtep и самом агенте, который занимался низкоуровневой конфигурацией коммутатора.

Баги, которые мы повстречали на стороне OVN:

Ссылки для интересующихся, но в принципе на новых версиях OVN (>= 21.06) эти проблемы уже не актуальны.

Проблемы с Cumulus Linux agent (ovs-vtepd):

  • Некорректно работает с BUM (broadcast, unknown unicast, multicast)-трафиком из-за разных схем репликации в NSX и OVN. Дело в том, что агент был изначально написан под NSX. С OVN он работал только на маленьких сетапах, а на более крупных были проблемы с обработкой BUM-трафика из-за разных схем репликации BUM-трафика в NSX и в OVN.

  • SegFault’ы, когда виртуальная сеть «расползается» больше, чем на 15 гипервизоров. 

  • Мы сделали вывод, что такое решение непригодно для боевой эксплуатации с OVN. Наши хорошие отношения с представителями NVIDIA и Cumulus дали нам работающий билд, но такое в продуктиве использовать не стоит.

Мы решили заменить это решение: написали на Python собственного агента vtepy и запускаем его в контейнере прямо на коммутаторе. Агент при помощи той же библиотеки ovsdbapp вычитывает информацию из Hardware_VTEP базы. Затем через библиотеку pyroute2, по сути, через netlink протокол, конфигурирует на коммутаторе VXLAN интерфейсы, forwarding таблицы (FDB) и назначает VLAN’ы на интерфейсы.

Схема этого решения выглядит так:

Кстати, чтобы нормально завести решение, приходилось патчить не только ovsdbapp, но и pyroute2: раз, два, три и четыре.

OVS ⇿ Linux Conntrack BUG

Вторая проблема скрывалась на связке между OVS и Linux Conntrack, то есть в районе stateful firewall.

Мы смигрировали одного нашего клиента в OVN. Через некоторое время он обратился к нам в поддержку с проблемой. На нагруженном HTTP-балансере, где был включен параметр ядра net.ipv4.tcp_tw_reuse, то есть достаточно агрессивно переиспользовались одни и те же 5-tuple’ы, некоторые сессии до бэкендов устанавливались по три секунды вместо ожидаемых околонулевых значений. Выглядело это так:

На балансере:

20:21:09.442333 IP 172.31.32.4.22222 > 172.31.32.5.http: Flags [S], …
20:21:10.443204 IP 172.31.32.4.22222 > 172.31.32.5.http: Flags [S], …
20:21:12.447260 IP 172.31.32.4.22222 > 172.31.32.5.http: Flags [S], …
20:21:12.447950 IP 172.31.32.5.http > 172.31.32.4.22222: Flags [S.], …
20:21:12.448235 IP 172.31.32.4.22222 > 172.31.32.5.http: Flags [.], …

На бэкенде:

20:21:12.446949 IP 172.31.32.4.22222 > 172.31.32.5.http: Flags [S], …
20:21:12.447252 IP 172.31.32.5.http > 172.31.32.4.22222: Flags [S.], …
20:21:12.447781 IP 172.31.32.4.22222 > 172.31.32.5.http: Flags [.], …

Из дампа видно, что отправлялись 3 TCP SYN запроса, которые как раз занимали три секунды, и только лишь на третьей попытке успешно устанавливалось соединение. При этом видно, что до получателя доходит только третий SYN.

Чтобы понять проблему, надо погрузиться в то, как работает OVN и OVS с Conntrack. Представьте, что мы отправляем с виртуальной машины 1 на виртуальную машину 2 HTTP-запрос с указанием source-порта, 22222, например. В OVN для этого сетевого интерфейса выделена специальная «комната» в глобальной таблице conntrack.

«Комната» — это зона, в которой создаётся запись об установленном соединении с описанием стейта и параметров соединения. Трафик доходит до второго сетевого интерфейса, и там, в другой зоне сетевого интерфейса второй ВМ в conntrack, тоже создаётся такая запись.

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

Проходит тайм-аут (1 секунда), отправляем ещё один пакет, он доходит уже до conntrack зоны второго (destination) сетевого интерфейса. Там происходит то же самое — запись удаляется, пакет отбрасывается. И только третий TCP SYN полноценно может пройти сквозь зоны обоих сетевых интерфейсов и обработаться, соединение успешно устанавливается.

Мы описали эту проблему в mail-листе, нам ответили: «У нас не воспроизводится» — класс! Значит, проблема где-то в нашей специфике. Мы сразу знали, где стоит попробовать поискать:

Дело в том, что мы используем не стандартный модуль ядра OVS, а Out-Of-Tree OVS kernel module — мы собираем его из исходников и устанавливаем на хосты. Выгрузили, проверили со стандартным — всё корректно работает!

Начали искать ближайшие версии OOT модуля openvswitch, где проблема воспроизводится, а где нет. Пробежались по коммитам, изучили все изменения и нашли, что в новой версии ядра поменялось поведение метода nf_conntrack_in() и эти изменения были бекпортированы в OVS, а потом compat-код удалили. Вместе с нашей тогдашней версией ядра это дало такой эффект, что соединение только удалялось и не устанавливалось заново. Мы починили это, заапстримили и раскатили обновление на сегмент OVN.

Итоги и выводы

Три года ушло на подготовку: рефакторинги, добавление новой функциональности, тестирование, исправления, продумывание и написание тулинга миграции.

Один год группа эксплуатации героически мигрировала инфраструктуры клиентов между SDN. Это были две зоны доступности облака в Москве и одна в турецком публичном облаке у нашего партнера, где наше облако установлено в качестве отчуждаемой инсталляции.

Вывод: сменить SDN в облаке, даже публичном, дорого, сложно, но можно.

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

  • Перенесли роутинг из LXC на OVN (активировали Distributed Virtual Routing, OVN Interconnection инфраструктуру) и получили серьёзный буст перформанса между подсетями и зонами доступности. Возможно, об этом, если будет интерес, расскажу на какой-нибудь конференции.

  • Внедрили Transit Gateways (прямое соединение VPC, а-ля VPC peering), когда различные VPC можно соединять между собой внутри платформы и не ходить по публичным адресам или строить IPSec VPN между VPC.

  • Внедрили Network Load Balancers и многое другое.

Для меня приятнее всего было ощущение, когда из кода после окончания миграции мы нещадно выпиливали legacy (код поддержки старого SDN и тулинг миграции). Эти красные строки удаляющегося кода в git — просто кайф!

После того, как мы проделали довольно большой путь в работе с OVS и OVN мне не хватало площадки, где для обмена опытом могут собраться люди, использующие или просто интересующиеся этими технологиями, и в новогоднюю ночь с 2023 на 2024 год я создал чатик в Телеграм, куда приглашаю всех интересующихся!

Теги:
Хабы:
+11
Комментарии0

Публикации

Информация

Сайт
k2.tech
Дата регистрации
Численность
101–200 человек
Местоположение
Россия