Фёдор Борщёв

Облачная платформа для Wiren Board

Мы разработали облачную платформу для управления промышленными IoT-устройствами. Делимся, как нам удалось это сделать небольшой командой в сжатые сроки и при этом не потерять в качестве. 

Наш клиент — компания Wiren Board. Они делают модульные программируемые контроллеры на базе Linux. Это маленькие компьютеры, размером как два сложенных вместе айфона. Их используют для того, чтобы управлять вещами в реальной жизни. Умные жалюзи дома, климат-контроль в дата-центрах и теплицах, холодильники в магазинах и фритюрницы в ресторанах, оборудование заводов и даже управление баней — вы удивитесь, как часто вы пользуетесь контроллерами Wiren Board, не замечая их.

Задача

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

При этом, если вы покупали какое-нибудь умное устройство в последнее лет 5-10, от часов до робота-пылесоса, то наверняка производитель предлагал вам возможность управлять своим устройством через интернет. Аналогичная облачная платформа сильно упростит жизнь клиентам Wiren Board.

Мы решили начать с интерфейса, где пользователи смогут посмотреть все принадлежащие им контроллеры — чтобы можно было проверить показания датчиков и бизнес-метрик, а в случае проблем — подключиться к устройствам по SSH; напомню, наши пользователи — это сисадмины крупных компаний. Забегая вперёд, добавлю, что инструмент оказался полезным не только сисадминам, но и IoT-энтузиастам, которые держат 1– 2 контроллера у себя дома.

Клиент, как всегда, хотел запуститься как можно скорее. Для этого мы максимально использовали готовые opensource-решения.  IoT — уже довольно развитая область и это позволило нам запуститься в рекордные 3 месяца, а ещё не тратить время и деньги на изобретение и поддержку своих собственных велосипедов.

Метрики

Для сбора данных с IoT устройств есть целый ворох протоколов и решений. Мы остановились на связке Telegraf и InfluxDB

Telegraf — это агент для сбора данных, который живёт на устройстве, собирает несчётное количество параметров системы и передаёт их к нам в облако. В итоговом решении мы получаем через него стандартный набор системных метрик вроде состояния сервисов,нагрузки на CPU и количества свободной памяти, а также данные с MQTT-шины, на которой работает системное ПО Wiren Board.

В мире IoT очень важен размер программы. Telegraf — модульная система, так что мы выключили лишнюю функциональность при компиляции и собрали 15Мб образ вместо 211Мб.

Для бэкенда облака мы выбрали базу временных рядов InfluxDB. База временных рядов — это база данных, которая хранит изменяемые во времени значения. Если привычный PostgreSQL работает со статичными таблицами, как в справочнике, то InfluxDB хранит историю изменений — как в выписке из банка есть остаток на счёте после каждой операции. Такой формат позволяет делать быстрые выборки вида «как коррелирует время ответа сервиса с загрузкой CPU между 01:30 и 01:35 5 февраля».

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

В InfluxDB мы надежно разделяем данные пользователей путем создания внутри базы изолированных организаций, а также используем встроенный API для ротации токенов как на запись для агентов, так и для доступа к данным со стороны Grafana для конечных пользователей.

Часть самых важных метрик системы, а некоторые данные с MQTT-шины мы интегрировали с бекендом, который разработали на Django и выводим самую ключевую информацию прямо на странице контроллера:

Туннели

Обычно контроллеры живут в закрытых корпоративных сетях и подсоединиться к ним извне довольно тяжело — вряд ли кто-то будет давать белый IP-адрес железке, которая управляет фритюрницей. Решение — туннели: сам контроллер подключается к нашему серверу, а мы, при необходимости, используем уже установленное соединение, чтобы достучаться до контроллера.

Самый простой механизм для этого — VPN — виртуальная частная сеть, проложенная поверх интернета. Сегодня VPN ассоциируется с доступом к инстаграму, но изначально он был разработан как раз для решения подобных задач. Нам, к сожалению, VPN не подходит. Имеющиеся решения либо тяжеловесны, либо плохо работают на территории России — Роскомнадзор режет все VPN без разбора. А ещё они требовали от нас очень много телодвижений — настроить распределение IP-адресов в сети и изолировать сети клиентов  друг от друга.

Мы любим простые решения, поэтому остановились на frpFast reverse proxy — маленькая программа, которая очень хорошо решает одну конкретную задачу. Клиентская часть frp устанавливается на контроллер и соединяет выбранные локальные порты контроллера с сервером. Для подключения по SSH мы используем слегка доработанный веб-терминал WebSSH, который внутри сети облака соединяется с открытым портом frp через авторизационный прокси. Прокси нужен, чтобы пользователи не могли получить доступ к чужим контроллерам.

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

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

Схема работы frp с нашим агентом:

Итоги и планы

Весь проект удалось сделать всего двумя программистами и уже через 3 месяца после начала работ мы запустили тест с первыми настоящими пользователями.

Мы уже собрали первую обратную связь от пользователей и сформировали беклог продуктовых задач. Оказалось, что клиенты Wiren Board уже давно ждали такой инструмент и остались довольны тестированием MVP.

Сейчас мы помогаем Wiren Board нанять сотрудников в команду, которая будет поддерживать и развивать этот продукт дальше.

В проекте участвовали:

  • Ведущий разработчик и автор этого текста — Алексей Чудин,
  • фронтендер — Тимур Брачков,
  • бекендер — Николай Кирьянов,
  • руководитель проекта — Настя Шаркова.

Day One → Obsidian

Несколько месяцев назад я решил отказаться от Day One, которым пользовался 5 лет для ведения личного дневника. Причин было несколько:

  • Невозможные тормоза. Дело тут даже не в электроне, или на чём он там написан: кажется Day One — это просто плохой софт. Удивляюсь, откуда у разработчиков берётся энергия, чтобы писать столько кода, чтобы тормозило даже на новом макбуке, где давно уже даже ноушен летает.
  • Фокус на метаданных. Day One с самого начала обвешивает привычку писать дневник всякими ненужными вещами, которые не представляют интереса уже через день — предлагает логать погоду, место и даже музыку, которую я слушаю. Чтобы ненужных данных было ещё больше, Day One ломает паттерн, привычный людям уже несколько сотен лет — вместо обычных ежедневных заметок, вас заставляют писать по одному посту на каждое событие. Добавил фотку — пост, записал мысль — ещё пост.
  • Переусложнённый формат хранения. Возможно следствие предыдущего пункта, но внутри у Day One довольно сложный формат хранения данных. Вроде бы и маркдаун, но он зарыт внутри довольно сложного JSON, который непонятно как парсить. То есть я как бы владею собственными данными, но вот считать их без Day One я скорее всего не смогу. А если и смогу сегодня — никто не гарантирует, что я смогу повторить это через 10 лет, когда формат переживёт ещё 3 инкарнации, а контента в дневнике станет в 4 раза больше.

Для замены Day One я выбрал Obsidian — он показался мне самым надёжным из всех инструментов для ведения заметок. Самое главное преимущество Obsidian перед специализированным софтом — все данные хранятся в виде MD-файлов на диске. То есть я владею своими данными, бекаплю их как хочу, и они никогда и никуда от меня не денутся.

От встроенной синхронизации решил отказаться, потому что iCloud работает быстрее, а на экосистеме Apple я сижу давно и плотно. Так же пришлось почистить слегка перегруженный интерфейс — помогло, что Obsidian проектировали как инструмент для программистов. Довольно легко мне удалось убрать всё лишнее — отключить штатные плагины вроде Canvas или записи аудио, попрятать ненужные детали интерфейса при помощи Hider.

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

Самое главное — теперь я уверен, что полностью владею своими данными — пачку маркдаун-файлов можно будет открыть хоть на квантовом компьютере.

Почему я не люблю GraphQL

Будучи разработчиком, я не любил GraphQL с самого его появления — из-за ноды, привязки к Apollo, отсутствия APM (если не покупать подписку у Apollo) и сложного, вложенного языка запросов. В принципе до сих пор ничего изменилось, но в этом посте я хочу отдельнло поговорить о своей любимой теме – тестировании.

GraphQL замечательно умеет делать одну вещь: вытаскивать данные с сервера в произвольном формате, удобном в каждый конкретный момент. «Выгрузи мне все книги — название, количество страниц, пару интересных цитат и автора, а у каждого автора покажи ФИО, фотографию и список популярных произведений» — это типичный запрос для GraphQL, с которым он справляется идеально. В REST для такой специфичной выборки пришлось бы писать отдельный эндпоинт, следить за ним и поддерживать. А тут даже кода писать не надо — один раз описал схему данных, и всё работает из коробки.

Минус в том, что вместо понятного набора эндпоинтов с заранее определённым поведением, мы получаем один эндпоинт на все наши данные. Тестировать такой эндпоинт очень больно. Первая проблема –  контракт между бекендом и фронтендом: раньше мы могли написать батарею юнит-тестов, которые проверяют все возможные запросы, аутентификацию и авторизацию, а затем копипастить их для разных эндпоинтов, добавляя локальную специфику. Тесты для GraphQL так не могут — мы же не знаем, что у нас запросят в каждый конкретный момент, а значит не можем написать тесты, которые отражают реальность. Остаётся только делать тесты для типичных запросов, и надеяться, что когда фронт или бек что-то поменяет, программисты сами договорятся и друг-другу ничего не сломают.

Сложно тестировать не только контракты, но и прозводительность. Раньше мы для каждого эндпоинта могли предсказать количество тяжёлых запросов с джоинами, и даже ограничить их количество в тестах или в рантайме (по ссылкам питон, в вашем стеке будут другие инструменты). Теперь мы каждый раз не знаем, что у нас запросят, а значит можно выгрузить хоть всю базу за один запрос.

Конечно, есть бизнесы, которые выигрывают от умения отдавать по 20 сущностей на один запрос, и делают это своей core-ценностью — к примеру облачные BaaS или высокоуровневые headless CMS вроде Sanity. Но если вам надо просто отдавать на фронт десяток известных сущностей, даже если вы планируете добавить в будущем ещё пару десятков — скорее всего GraphQL обойдётся вам намного дороже чем обычный REST, потому что с самого старта вам придётся тратить намного больше сил на тесты.

Обхожусь без нетворкинга

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

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

Да, без нетворкинга не работает биздев  — если вы продаёте b2b товары или услуги, и не придумали как взломать систему, то вкладывать кучу времени в знакомства с почти нулевым результатом — это ваша святая обязанность.

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