Привет. Я Марат Сибгатулин — сетевик в Яндексе, работаю в команде Yandex Infrastructure. И сегодня я расскажу вам одну поучительную историю.
Жили были два сервера. Да и не сервера вовсе, а виртуальные машины. Жили не тужили, добро наживали, скриптами разными обрастали. Три года они трудились на славу облака да во имя автоматизации. Пока не наступили чёрные дни для RAID-массива на гипервизоре.
Это присказка, не сказка. Сказка будет впереди.
В далёком 2017 году, когда публичное облако только-только обретало свои самобытные черты, мы в сетевом отделе обзавелись парой физических машин в каждом дата-центре — итого шесть. Роль их была простая и в то же время важная: нести на себе груз задач, которые возложить больше не на кого.
И дальше началась первая фаза «исторически сложилось». Я расскажу, как в нашей внутренней инфраструктуре появились два God-Box. Как годами они обрастали пышными мхами и копили пролежни. Как один из них пал смертью ленивых, но тем самым поднял волну, всколыхнул наш отдел и отправил его на подвиги. И с тех пор за нами закрепляется фраза «netinfra bleeding edge», поскольку мы и по сей день используем передовые подходы и первыми берёмся за технологии, о которых другие отделы пока лишь задумываются.
Это будет история о том, как мы перешли от деплоя курильщика к CI/CD, закрыли всё зонтиком IaC — от облачной инфраструктуры до систем мониторинга. Это будет история с сюжетом, которого я постараюсь избежать в следующий раз.
Cloudvessel или два God-Box
Инвентарная система
Шесть лет назад мы начинали с нуля: грин-филд и фига за пазухой. Нам были нужны инвентарная система, система для управления адресным пространством, мониторинг, генераторы конфигурации, бэкапилки конфигурации, сетевая файлопомойка и куча прочего инструментария.
Так у нас появились две виртуальные машины, нежно названные Cloudvessel. На них мы и планировали разворачивать всё-всё-всё. Для эксплуатации ВМ на гиперах мы использовали KVM и virsh. Бо́льшая часть ресурсов была занята виртуальными сетевыми функциями, а точнее, VNF — Virtual Network Function. Мы запускали там маршрутизаторы, обеспечивающие выход из облака во внешний мир. А на оставшихся разместилось то, что нужно для эксплуатации сети.
Оглядываясь назад, я не могу найти ответы на два вопроса: почему две ВМ, а не три при всей нашей любви к этой цифре, и почему мы сразу не начали описывать их в плейбуках Ansible, Salt или хотя бы Bash. Оба факта поставили нас в будущем в весьма неудобное положение.
Оба Cloudvessel день за днём, неделя за неделей начали обрастать мшистыми скриптами и системами. Сначала мы запустили Netbox — систему для инвентаризации и управления IP-адресами всей сети Облака. Это питоновское приложение за Gunicorn и nginx с Postgres в качестве БД.
Никаких наших облачных сервисов тогда ещё не существовало. Но были их прародители, поэтому PostgreSQL мы запустили в яндексовом Managed DB, а два экземпляра фронтенда поставили за балансировщик нагрузки большого Яндекса.
Так у нас появилась инвентарка.
Мониторинг
Как только появилось первое сетевое оборудование, его сразу нужно было ставить на мониторинг. Мы придумали и написали собственную систему, которая собирает ровно то, что нам нужно. Например, помимо системных и интерфейсных метрик, нам хотелось знать о состоянии и времени жизни BGP-сессий, числе активных членов в LAG, работоспособности модулей питания, вентиляторов.
Почему своё? Мы намеренно отказались от SNMP и от вендорских решений по сбору метрик и воспользовались SSH и протоколом NETCONF, который, по сути, является XML API для взаимодействия с оборудованием. Благодаря этому, мы можем добавлять любые интересные нам метрики.
Эта система (мы назвали её незамысловато monapp
) собирает данные с оборудования разных вендоров, приводит их к одному виду, потом отсылает с помощью локально установленного агента в общеяндексовую Time-Series DB (известна в облаке под именем Monitoring
), в которой на основе формул мы уже создаём уведомления.
Поэтому на оба наших «весла» нужно было поставить monapp, агента для пересылки данных и etcd — контролирующий активный экземпляр.
Вот так это выглядело и ощущалось.
Cron-скрипты
Как только началась эксплуатация сети, потребовались скрипты, собирающие разные вспомогательные данные с сетевых устройств. Например, вот такие:
сборщик бэкапов конфигурации, складывающий их в Git;
сборщик версий ПО на оборудовании, чтобы мы всегда знали, каков зоопарк на нашей сети;
сборщик коммитов, с помощью которого мы узнаём когда, кто и что настраивал на любой коробке;
сборщиков MAC-адресов и LLDP-соседей, чтобы понимать, где и что находится в сети.
Примерно в то же время мы решили реализовать механизм блокировок нежелательного трафика: мошенники, источники DDoS атак, неблагонадёжные клиенты, фродеры. Для этого есть прекрасный протокол — FlowSpec, который через BGP умеет распространять правила фильтрации. Так вот: мы разместили в гите yaml-файл с нужными правилами, при обновлении перечитываем их скриптом и вносим в конфигурацию одного из отражателей маршрутов, который доставляет на бордер актуальные правила фильтрации.
Все эти скрипты мы разместили на «вёслах» и запустили по крону. А чтобы мониторить их работу, поставили локально ещё один агент, который принимает в себя события и передаёт их в систему событийного мониторинга.
Ещё через какое-то время стало невыносимо неудобно без централизованного хранилища логов. И как вы думаете, куда поехал syslogd?
Вам страшно? Мне — да.
VLANToggler toggles your VLANs или Network API
Для снижения рутины мы написали простенькое приложение на Flask. Оно представляло из себя интерфейс для управления VLAN на стоечных свитчах. Мы поставили его за тот же самый nginx на «вёслах» (разумеется, где же ещё?). Потом появилось приложение для сбора активных MAC-адресов для обнаружения серверов. А затем и инструмент для полуавтоматической обработки проблемных линий в дата-центрах. К тому же наша автоматизация по настройке сетевого железа с ноутбуков инженеров, где каждый должен был поддерживать окружение, начала переезжать в такое же WEB-приложение.
Со временем количество таких приложений росло, и каждое запускалось как новое. В какой-то момент их количество переродилось в Network API, который помогал абстрагировать сложность сети за единым интерфейсом с разграничением прав, а также реализовывать сложные сценарии по взаимодействию с кучей систем: железом, Netbox, трекером задач, системами мониторинга, графиками дежурств и плановых работ, Git.
Network API, включающий в себя все наши сервисы, написанный на FastAPI, мы запустили там же на Cloudvessel. Сделали для него отдельный балансировщик (тоже яндексовый).
Почти на месте.
Инструменты автоматизации
Набор скриптов для ввода в эксплуатацию нового оборудования мы развернули на тех же серверах, чтобы не мучиться с переменными окружения на каждой отдельной машине инженера. Сюда входило:
заведение устройства в системах;
наполнение систем данными об интерфейсах, IP-адресах, BGP ASN и прочей информацией;
рендер и применение целевой конфигурации.
Настолько всё было плохо?
Ну да, настолько.
Вот что болело сильнее всего:
Всё окружение настроено и поддерживается вручную.
Код сервисов обновляется через подключение по SSH и git pull. Ни про какое версионирование речи не было.
Для каждого запущенного сервиса балансировщик нагрузки требовал ручной настройки туннелей и IP-адресов сервиса на бэкендах.
Появились даже некоторые завязки на то, что Netbox работает на той же самой машине, что её клиенты.
Со временем мы начали обнаруживать удивительные артефакты на MAC-ах: очень редко случалось так, что запросы к Netbox таймаутили, хотя на Linux всё было хорошо. Пришлось нехило покопаться, чтобы обнаружить проблему в MTU и поправить MSS на машине.
Осознание сложности бытия
Естественно, мы отдавали себе отчёт и с каждым новым подставленным костылём всё яснее, какой глубины яму, наполненную техническим долгом, мы роем, и какую гору рисков из изъятого грунта складываем. Заглядывали друг другу в глаза исподлобья и стыдливо отводили взгляд.
Две абсолютно неавтоматизированные машины с тонной разных приложений, пакетов, ручных настроек, без должного мониторинга и с фрагментарной документацией. И это всё к тому же без нормального релизного цикла.
Мы понимали, что нужен третий экземпляр.
Мы понимали, что нужны плейбуки.
Мы понимали, что нужна система CI/CD.
Мы понимали, что если накроются диски на одной машине, мы останемся с единственным экземпляром всех инструментов.
Без автоматизации машины были как снежинки - каждая по-своему уникальна.
Ты ли это, Яндекс?
Давайте я дам короткий комментарий, как такое вообще могло случиться в IT-компании, где работает несколько тысяч разработчиков, которые при найме проходят четыре секции с кодингом.
Команда сетевой инфраструктуры очень долго была маленькой и состояла только из сетевых инженеров. Собственно всю сеть облака в прод в 2018 году мы запустили командой в четыре человека. Поэтому вся автоматизация и мониторинги разрабатывались нашими же силами зачастую по остаточному принципу.
Задача была запускать и развивать сетевые проекты: фабрики дата-центров, междата-центровые каналы, внешнюю связность с интернетом, Cloud InterConnect, AntiDDoS. Каждый из них сам по себе состоит из десятка крупных проектов, которые, в свою очередь, раскладываются на кучу рутины. И вот для оптимизации операционки и появлялись скрипты, плавно перерастающие в системы.
Программная инфраструктура была остро недофинансирована. В урагане запусков и дежурной текучки заниматься построением инфры времени не хватало. И все мы знали, что когда-то за это придётся поплатиться. Поэтому тикетов на вынос систем с «вёсел» у нас был полный бэклог с горкой. И на нетбокс, и на крон-джобы, и на мониторинги, и на всё-всё-всё.
И этот день настал
Ошибки RAID посыпались как градины с безоблачного неба. Все виртуальные машины в AZ-A начали вываливаться из мониторинга и покраснели проверки работы скриптов. Быстрая и печальная диагностика показала, что у нас осталось каждой твари по одному экземпляру всего в одной зоне. Пока летим, но один отказ — и мы без наших сервисов.
И даже после этого отказа, конечно, облако продолжит работать, сеть будет функционировать, ни одна вышестоящая услуга IaaS-не шелохнётся, ни один пользователь не моргнёт. Но встанет обслуживание, операции — мы останемся без глаз. Также будут затронуты смежные команды, которые уже завязались на Netbox и Network API.
Итак, облако не сможет вводить новые стойки, уводить сервера на обслуживание, фиксировать аварии на сети. И это проблема — уходить на выходные было очень тревожно. Без шуток, следующие без малого три недели мы занимались восстановлением, начиная с замены дисков и нагрузочного тестирования сервера и заканчивая применением настроек Linux-стека. Всё то, что нарастало в течение трёх лет, нужно было воспроизвести как можно быстрее да так, чтобы когда мы вернём нагрузку на балансировщике на этот бэкенд, никто не заметил отличий. Боязливо.
Сетевики учатся практикам DevOps или первый шаг трансформации
Отрицание. Гнев. Торг. Депрессия. Выделение ресурсов на бэклог.
Действительно, дальше было примерно полгода, в течение которых мы вырабатывали подходящее для нас решение. При этом наша инвентарная система Netbox не смогла бы выдержать такой срок. Для неё нужно было придумать что-то очень быстро.
Параллельно с восстановлением Cloudvessel в AZ-A, мы запустили машину в нашем публичном облаке Yandex Cloud, установили на неё Netbox, всё необходимое окружение, настроили мониторинг, логирование, туннели и адреса для балансировщика нагрузки и кучу всего другого. Собрали из этого Golden Image, описали в Terraform и с его помощью запустили нужное количество бэкендов за считаные минуты.
Это был первый переломный момент: хотя бы один сервис мы выселили с God-Box. Никогда ещё проводить работы на Netbox не было так легко и приятно: при необходимости обновления мы делаем это на одном из бэкендов, сохраняем, как новый Golden Image, и перезапускаем группировку. И это через terraform apply
. А раньше мы на Netbox дышать боялись.
Всё сложнее с Network API
Как насчёт compliance?
В отличие от статического кода Netbox, новые версии нашего Network API мы выкатываем иногда по трижды в день. А выкатываем мы, я напомню, просто с помощью git pull
и перезапуска приложения, насколько бы дико для вас это ни звучало.
Очевидно, что рано или поздно мы попали бы под радары группы Комплаенса. Аудиты требуют контролируемых релизных процессов, сборки в изолированном окружении, артефактов сборки и релиза — всего того, что у нас начисто отсутствовало.
Попали на радары мы, естественно, рано, но просто договорились, что у нас есть тикет в бэклоге — и мы им займёмся (и благодаря почившему RAID мы им занялись).
Время собирать докер-контейнеры
Раньше мы писали только на Python, поэтому самым логичным способом доставки был докер-контейнер со всем необходимым окружением: приложениями, пакетами, конфигурациями и секретами. Впрочем, и безотносительно к Python контейнеры удобны.
Собрать контейнер — скажем по-честному, дело не хитрое. А вот обеспечить весь цикл от коммита до выкладки на прод, да ещё и с IPv6, да ещё и аудируемый — задачка на несколько месяцев (с точки зрения сетевиков). Мы решили, что выдумывать здесь что-то своё новое мы не будем. Поэтому взяли на вооружение существующие системы и подходы, которыми пользуются другие команды. Честно, логичным это выглядит лишь при взгляде через пространство и время. Вы не представляете, насколько непреодолимо природное любопытство сделать что-то своё, когда ты неопытен и оттого кругозор размером с игольное ушко.
Для управления зависимостями в Python у нас уже давно используется Poetry в связке с Pyenv. Для CI мы взяли уже развёрнутый и активно используемый на тот момент многими командами TeamCity, а контейнеры загружали в Container Registry — сервис Yandex Cloud.
Тут был нюанс: если каждый раз ставить весь список зависимостей, то сборка будет занимать каждый раз по 30 минут. Неприятно, учитывая, что в нём что-то меняется довольно редко. Поэтому мы по мере необходимости собираем базовый образ с окружением, а отдельной джобой добавляем в него нужную версию приложения. Так время сокращается минут до пяти.
Ещё нам нужен был контроль за машинами, на которых запускаются контейнеры. И мы стремились к Immutable Infrastructure, глубоко задетые тем, сколько времени заняло разворачивание нового «весла», в котором всё время чего-то не хватало. Мы хотели, чтобы любое изменение запускало сборку и пересоздание объектов. Так мы будем уверены, что нет каких-то ручных или неучтённых изменений, про которые мы забудем при следующей выкладке, и приложение сломается.
Мы взяли в качестве базы образ ОС, в котором уже были необходимые нам пакеты и настройки: OSlogin для централизованного управления авторизацией, агенты мониторингов и логирования, инструменты аудита безопасности. И в этот образ с помощью Hashicorp Packer мы доустанавливали приложение из Container Registry и nginx. Этот образ TC кладёт в образницу в нашем инфраструктурном фолдере Yandex Cloud и затем используется для запуска приложения.
То есть вся цепочка выглядит следующим образом:
Работа приложения
А из чего мы смоздрячили инфраструктуру для приложения?
Всё довольно просто: три абсолютно одинаковых экземпляра виртуальных машин с приложением в контейнере собраны в Instance Group и поставлены за L4 Load Balancer.
Вот немного подробностей:
авторизация через OSlogin;
мониторинг ресурсов штатными средствами в Cloud Monitoring;
мониторинг ответов nginx через парсинг логов lua-скриптами и проброс в stdout;
логирование в Cloud Logging;
и всё это на голой и чистой шестёрке (IPv6 — да, внутри мы так можем).
Катим в прод
По сути — terraform apply
.
Терраформом описан практически весь деплой: создание облака, фолдера, сервисного аккаунта, ролей, нетворков и сабнетов, инстанс-групп и балансеров, виртуалок и реестра контейнеров. Стейт терраформа хранится в облачном S3, поэтому вопрос рассинхронизации стейта между инженерами не стоит. Новые версии требуют исправления рецептов терраформ, аппрувов и мёрджа в мастер перед выполнением terraform apply.
В итоге по такой схеме мы вынесли в облако с God-Box инвентарную систему, Network API, систему мониторинга, хранилище разного, вроде софта для железа, крон-скрипты. И на «вёслах» теперь крутят свои последние обороты скрипты автоматизации для ввода в эксплуатацию нового оборудования.
Почему не k8s, чёрт побери? Это единственное, что меня интересует!
Тут стоит дать пояснение в чём разница между публичным облаком для клиента и для внутренних нужд.
Клиент публичного облака получает всё как сервис. Чтобы это стало возможным, мы создаём оверлейную сеть (то есть, наложенную), в которой одновременно живут все клиенты, но при этом друг друга не видят. Это даёт масштабируемость и гибкость ценой добавления роутер-агентов на каждый хост, виртуальных маршрутизаторов aka Cloud Gateway, обеспечивающих выход из облака во внешние сети, балансировщиков нагрузки и нескольких API, обеспечивающих Control Plane.
Но работает эта виртуальная сеть поверх самой что ни на есть реальной — физической. Это та, за которую мы сами и отвечаем. Называется она андерлей — нижележащая.
Так вот, для внутренних нужд виртуальные машины могут запускаться с интерфейсом в андерлейной сети, чтобы не зависеть от работы всех этих сложных компонентов. Это, по сути, разрыв кольцевых зависимостей: система управления серверами не может жить в оверлейной сети, работа которой зависит от серверов.
Из тех же соображений системы автоматизации и мониторинга сети мы не хотели бы помещать в оверлей. Описанная схема даёт возможность иметь андерлейный интерфейс, а вот Managed Kubernetes в публичном облаке — это целиком оверлейная штука и там нет даже опции как-то добавить андерлейную сеть.
Но сложно спорить с тем, что k8s — это очень удобный способ деплоя, и мы научились всю связку компонентов разворачивать. Но это уже совсем другая история.
N.B. Да, мы отдаём себе отчёт, в том, что Облачный L4 балансировщик тоже оверлейный, но мы надеялись, запустив быстрое решение, сесть и думать над правильным. Однако научная мысль ушла совсем в другую сторону — отдельная вынесенная железная инсталляция, чтобы не зависеть даже от андерлейной сети.
Что дальше
Мы очень основательно вложились в программную инфраструктуру. Теперь она воспроизводима, соответствует всем требованиям внутренних и внешних аудиторов, мониторится, выкатывается по кнопке, есть CI, есть CD, запускается как на виртуалках, так и в Kubernetes.
Следующий вклад будет в амбициозные задачи:
Полностью автоматический конвейер ввода в эксплуатацию нового оборудования (и тогда прощай, Cloudvessel). Часть текущих компонентов, в том числе Network API, написаны на Python, часть — на Go. Нужно всё это собрать воедино и заставить работать вместе.
Автоматизация жизненного цикла сетевого оборудования: обновления конфигурации и ПО, вывод и возврат нагрузки. Для этого у нас есть сетевой сетевой таск-процессор, который умеет распределять задачи по сегментам сети, чтобы не разломать её.
Разработка систем автоматизации и мониторинга для так называемых Whitebox-коммутаторов — устройств с Linux на борту, вместо проприетарного ПО с фиксированным окружением. Задачка на острие технологий: мало кто в проде использует их сейчас и мало кто реализует для них современные интерфейсы gRPC/gNMI.
Развитие Network API, чтобы предоставлять больше возможностей для управления сетью через REST API и/или gRPC.
И конечно, железная инсталляция Kubernetes. Для окончательного разрыва кольцевых зависимостей, выезда из оверлея и вообще облака, мы приняли решение разворачивать весь стек инструментов на отдельном небольшом флоте серверов. Это позволит нам собрать изолированный контур, который не ломается одновременно с облаком и позволяет эксплуатировать сети новых регионов Yandex Cloud.
Какой можно сделать вывод из этой истории? Сделали бы мы что-то иначе? Учтём ли мы ошибки на будущее? И да, и нет. Пожалуй, единственное, что бы я действительно исправил, так это добавил в самом начале третий экземпляр Cloudvessel.
Дело в том, что это был этап бурного роста из ничего в продукт. Этап, когда всё, что не первостепенно получает остатки внимания. Так и должно быть. Все наши силы были посвящены запуску и развитию сети, а дизайн систем автоматизация и инфраструктуры — когда-то потом.
Стоило ли нанять нужных специалистов тогда? Мне кажется, нет (в силу объективных причин: бюджеты и распыление внимания). Стоит ли нанимать сейчас? Да, теперь Yandex Cloud — это уже зрелый продукт с большим хедкаунтом. Пришло время разбирать техдолг из бэклога. Ну и главное, ситуация сама провоцирует наводить порядок и заняться производительностью: быстрый рост в существующих датацентрах, запуск новых, региональные инсталляции облака.