Из лягушек в автоматизаторы — мое решение

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

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

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

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

Начало

Мой проект называется VRack (Virtual Rack), читается как «ВиРэк». Это инструмент, который упрощает написание сервисов для целей мониторинга, промышленной автоматизации, личной автоматизации или автоматизации бизнес процессов.

VRack — это self-hosted сервис, написанный на JavaScript (nodejs), основная задача которого — запускать в отдельных потоках сервисы, определенные пользователем.

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

Максимально упрощенная схема VRack:

4a2e6ff46c198b76ff8beb87f65477d0.png

  • External applications — Это внешние приложения, которые могу взаимодействовать с VRack с помощью WebSocket Data Provider.

  • API Master — Регистрирует команды других модулей для доступа к ним из External applications

  • Service Manager отвечает за запуск и контроль сервис-потоков, которые выполняют полезную работу.

При создании основного сервиса я старался придерживаться следующих требований:

  • Сервис должен жить максимально долго, без всяких sigfault

  • Сервис должен обеспечивать менеджмент других сервисов в отдельных потоках:

    • Запуск нового потока

    • Перезапуск при аварийном завершении

    • Получение и хранение ошибок при завершении потока

  • Сервис должен предоставить API для работы с сервис-потоками

  • Сервис должен быть легко расширяем. Он должен предоставлять все возможное для реализации конечного результата. Не нужен API сервер? отключаем. Не нравится WebScoket? заменяем. Нужен другой механизм авторизации? без проблем. Если у нас есть какие то особые требования, они должны легко удовлетворяться.

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

Пока разберем только основу — Service Manager

Сервис-потоки

Сервис-потоки — это сервисы, определнные пользователем, которые запускает Service Manager каждый в отдельном потоке.

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

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

Чтобы организовать сервис-поток, необходимо описать какие виртуальные устройства должны в нем запускаться и указать связи. Это делается с помощью схемы, которая представляет собой JSON файл, например:

{
   "devices": [
      {
         "id": "Interval1",         // Уникальный идентификатор устройства
         "type": "basic.Interval",  // Класс устройства
         "params": {                // Параметры устройства
            "timeout": 1000,
            "start": true
         }
      },
      {
         "id": "Debug",
         "type": "basic.Debug",
         "params": {}
      }
   ],
   "connections": [
      /**
      Указания связи описывается как:
      ИсходящееУстройство.исходящийпорт -> ВходящееУстройство.входящийпорт
      */
      "Interval1.gate -> Debug.debug1" 
   ]
}

Результат выглядит так:

2715f7ee9057345f676668ea719903cc.png

Для сервис-потоков были определены следующие требования:

  • Никакого «программирования мышкой» — Это не очередной визуальный язык программирования, low-code платформа или тп, все придерживается абсолютно других концепций и правил

  • Структура сервис-потока не может меняться без перезагрузки (то есть добавление новых устройств в уже работающий сервис-поток невозможен)

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

  • Модульность и расширяемость — Функционал сервис-потоков должны расширяться так же просто и эффективно, как и функционал основного сервиса

  • Генерация схемы сервис-потока — Как показала практика, генерация схемы сервис-потока позволяет решать сразу несколько проблем:

    • Наследование схем, очень помогает в решениях однотипных задач

    • Генерация схемы на основе конфигурационного файла

    • Лучше сгенерировать достаточно большую схему, чем набрасывать ее руками

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

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

Виртуальные устройства

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

Возникает закономерный вопрос, а зачем писать какие то там виртуальные устройства, когда можно просто писать код библиотеки и вызывать его? Подход через реализацию виртуальных устройств позволяет создавать интуитивные абстракции. Схема сервис-потока предоставляет понятную документацию и структуру конечного сервиса. Такой подход отличается концептуально от написания классического линейно/асинхронного кода. У нас нет более точки входа в приложение типа index.js, потому что каждое виртуальное устройство это и есть точка входа в приложение. Такое решение очень хорошо вписывается в работу nodejs, упрощает декомпозицию кода и контроль.

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

Индикация

Виртуальное устройство может иметь обновляемую область памяти (Shares), которую можно получить с помощью API Master. Фактически это просто объект, который изменяет само устройство и после может оповестить через систему бродкастов всех заинтересованных. Таким образом, внешнее приложение может в онлайн режиме наблюдать изменения, которые происходят внутри виртуального устройства.

Органы управления

Органы управления в виртуальных устройствах VRack называются Action и представляют из себя специальные методы класса устройства, которые можно вызвать с помощью API Master. Все общение с устройствами происходит прозрачно, несмотря на то, что запрашиваются они у API Master основного процесса.

Порты

Порты нужны для общения между устройствами. По сути виртуальные устройства не знают ничего, что происходит вокруг них, они общаются между друг другом только посредством соединений портов. Это позволяет сосредоточиться на конкретной логике устройства и просто определить нужные точки входа для его работы.

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

Сервисные сообщения

У виртуальных устройств есть возможность отправлять специальные сервисные сообщения о разных событиях внутри устройства. Такие сообщения также отправляются через систему бродкастов, то есть их можно получать в онлайн режиме. Сервисные сообщения могут содержать в себе объект с произвольными данными, которые поддерживает JSON (необходимо для сериализации/десериализации).

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

Общие принципы работы

Выше были описаны основные компоненты работы VRack. Пришло время рассказать, как это работает.

Вся работа в VRack начинается с устройств. Например, у нас есть необходимость сделать счетчик, который будет считать сигналы, поступающие на вход, и показывать это нам в интерфейсе.

Для начала, нам нужно определить, какие нам нужны порты для этого устройства. Мое предложение сделать один вход для подсчета и один выход для отправки в него результата подсчета. Назовем их increment и sum. Сюда же я бы добавил возможность сброса счетчика, добавив порт reset.

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

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

Напишем устройство Counter.js:

const { Device, Port } = require('vrack-core')

module.exports = class extends Device {
  description = 'Ведет подсчет входящих сигналов'
  /** Определение портов устройства */
  ports () {
    return [
      new Port('reset').input().data('signal').description('Сброс счетчика'),
      new Port('increment').input().data('signal').description('Добавление счетчика'),
      new Port('sum').output().data('number').description('Текущее значение счетчика')
    ]
  }

  /** Обновляемая область памяти */
  shares = { counter: 0 };

  /**
   * Для каждого входящего порта необходимо создать метод вызова
   * Он именуется как inputPort и вызывается автоматически
  */
  inputReset () {
    this.event('Reset counter', this.shares) // Отправка сообщения типа `Event`
    this.shares.counter = 0
    this.render() // Ставим устройство в очередь на отправку данных this.shares подписчикам
  }

  // Инкремент счетчкиа
  inputIncrement () { this.shares.counter++; this.render() }
}

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

Устройство Interval.js:

const { Device, Port, Rule } = require('vrack-core')

module.exports = class extends Device {
  description = 'Формирует сигнал через заданное время'
  ports () {
    return [
      new Port('gate').output().data('signal').description('Сигнальный выход')
    ]
  }

  /**
   * Описание параметров, которые мы ждем из файла сервис процесса
   * */
  checkParams () {
    return [
      new Rule('timeout').required().default(1000).isInteger().expression('value > 0')
        .description('Интервал в ms')
    ]
  }

  /**
   * Данный метод запускается автоматически после инициализации всех устройств
  */
  process () {
    /** Отправляем сигнал через заданое время */
    setInterval(() => { this.outputs.gate.push(1) }, this.params.timeout)
  }
}

Напишем файл сервис потока:

{
    "devices": [
        {
            "id": "Interval1",
            "type": "guide.Interval",
            "params": {
                "timeout": 1000
            }
        },
        {
            "id": "Interval2",
            "type": "guide.Interval",
            "params": {
                "timeout": 10000
            }
        },
        {
            "id": "Counter",
            "type": "guide.Counter",
            "params": {}
        }
    ],
    "connections": [
        "Interval1.gate -> Counter.increment",
        "Interval2.gate -> Counter.reset" 
    ]
}

В итогде наша схема будет выглядеть так:

ab01ab16182dd1383a24e34df186e2e7.png

Можно посмотреть в онлайн режиме как будет работать наша схема, подписавшись на канал shares данных (render) устройства Counter:

011bbe0f97eed8f2208f34e4a8beb3da.gif

Если же подписаться на канал Event устройства Counter, то будем получать сообщения о сбросе счетчика:

a248b628a200c29ce498e6a359d21120.gif

Для устройств автоматически формируется документация, например, для Counter:

Hidden text

Counter

Ведет подсчет входящих сигналов

Входы:

Выходы:

Параметры

Нет информации по параметрам

Пример конфигурации

{
   "id": "Counter",
   "type": "guide.Counter",
   "params": {}
}

Функционал устройств на самом деле гораздо шире, например, у устройств есть специальный тип портов modeReturn, который позволяет вернуть результат выполнения метода порта. Также у устройств есть возможность создавать динамические порты, которые создаются на этапе запуска сервис потока.

Реальные примеры использования

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

Опрос котельной жилого дома

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

Вот сама схема (довольно большая):

Hidden text

f428eedc8a1287cc74584573da201a16.jpg

Первая блок схема (1)

Схема опроса реальных устройств. Тут участвуют сразу 2 схемы опроса реальных устройств:

  • Первая схема используется для опроса 3х устройств (DEV::19, 27, 30), которые работают по очереди используя преобразователь интерфейсов типа Ethernet-RS485. Когда Provider подключается к реальному преобразователю, он передает специальный класс соединения через селектор следующему устройству, когда устройство заканчивает работу с соединением, оно информирует провайдер через микшер о том, что управление было передано ему обратно и цикл повторяется

  • Вторая схема более простая, устройство Dev::20 работает через Ethernet напрямую и не требует особой схемы для опроса

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

[timestamp, number, 'metricid']

У провайдера (Provider::21) есть несколько выходов для получения дополнительных метрик опроса. Это очень полезные метрики и позволяют получить, к примеру, среднее время задержки опроса реальных устройств и, тем самым, решить периодически возникающие проблемы. Эти параметры также заведены в отдельный микшер метрик.

Вторая блок схема (2)

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

Третья блок схема (3)

Отправка метрик в разные базы данных или разные таблицы. Мы храним данные метрик в Graphite для отображения в Grafana и эти же данные мы складываем в сыром виде для хранения в Clickhouse, так же в отдельную таблицу складываются служебные метрики сервис-потока (работа провайдера, занимаемая память).

Укладка данных в базы происходит через устройство Buffer, которое копит в себе метрики, пока их кто-то не заберет. ClickhouseInserter проверяет соединение с базой данных, делает тестовый запрос, и, если все в порядке, отправляет в порт Buffer.slice количество, которое он хочет получить из буфера (обычно 500). Буфер же отправляет в порт entities метрики в количестве <= 500. После того как ClickhouseInserter отправил все метрики в базу, отправляет количество вставленых метрик в порт shift, что приводит к смещению и удалению данных в Buffer.

Это очень полезная схема, которая позволяет терять связь с базой данных. После того, как связь возобновится, данные из буфера попадут в базу в полном объеме.

Устройство timeseries.List используется для переименования названия метрик (маппинг названий), это делается для разных баз по разному (происходит на этапе формирования схемы сервис-потока)

Четвертая блок схема (4)

Формирует данные о потреблении памяти внутри данного сервис потока. Мы собираем эти данные для контроля утечки памяти. И, хотя в случае правильного использования устройств и написания кода утечек не бывает, по таким параметрам можно определить утечки не совсем очевидных мест. Таким образом были найдены ошибки утечек в самом VRack на масштабе аптайма где-то 6 месяцев.

Пятая блок схема (5)

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

С помощью system.Transmitter можно передавать данные в порты устройств других сервис-потоков.

Заключение к данной схеме

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

Мелкие вспомогательные сервисы

VRack очень хорошо подходит для разнообразных маленьких сервисов. Поскольку VRack сам заботится о запуске/перезапуске сервис-потоков, вам необходимо сосредоточиться на написании только самого сервиса.

Ниже приведен пример небольшого сервиса:

Hidden text

5eb3112b6491f2ed2c388e031f993ec7.jpg

В его задачи входит отслеживание SNMP Трапов коммутаторов локальной сети и отправка информации о них в телеграм. Если посмотреть внимательно на схему, то часть ее мы уже видели, а конкретно часть с отслеживанием сообщений внутри сервис-потока. К этой схеме был дописан TrapReceiver и вспомогательные устройства TrapManager и TrapAlert, которые в конечном итоге формируют сервисное сообщение типа alert, которые и отправляется к нам в телеграм.

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

Автоматизация с управлением

До этого были приведены примеры мониторинга и сбора данных. Справедливо будет привести пример автоматизации с управлением реальными устройствами.

Схема управления автоматизации шлагбаумами въезда/выезда КПП:

Hidden text

aa090fa17106c0a01eab78a6fa335c86.jpg

Первая блок схема (1)

Принимает информацию о фиксации камерой наблюдения подъезда автомобиля. Там приходят такие данные, как: номер, направление, камера, и тп. В зависимости от направления и принадлежности камеры, они распределяются на соответствующие выходы ReceiverAdapter.

Вторая блок схема (2)

Кажде 15 минут из внешнего приложения приходит новый список разрешенных автомобильных номеров, используя HTTP запрос. Проверить наличие номера в списке можно через входы Manager.number1… Эти входы работают в modeReturn и возвращают результат выполнения метода push, таким образом контроллеры узнают разрешения для номера авто.

Третья блок схема (3)

Состоит из 3х однотипных блоков управления шлагбаумами. Gateway собирает информацию о шлагбауме и передает ее в Controller. Когда в Controller приходит информация о подъехавшем автомобиле, он анализирует состояние шлагбаума и проверяет, можно ли его открыть. Если все соответствует нужным условиям, контроллер отправлять нужные сигналы на его открытие.

Четвертая блок схема (4)

Отправка в базу данных Clickhouse информацию о проезде автомобиля.

Пятая блок схема (5)

Модуль ввода вывода, который, замыкая контакты, управляет непосредственно кнопками пропуска транспортных средств.

Заключение к данной схеме

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

Интерфейсы

Как только я познакомился с Vue2, тут же начал писать интерфейс для управление VRack. На данный момент этот интерфейс находится в достаточно плачевном состоянии, но может выполнять практически все основные функции.

Выглядит он примерное так:

Hidden text

87806d9e89c4153eb6e7a5de5986cdec.png

Сверху расположено меню серверов, слева выбор дополнительного функционала и выбор сервиса, справа управление выбранным функционалом/сервисом.

После выбора включенного сервиса доступны 3 вкладки:

  • Управление — Основная информация/метаданные и кнопки управление (логическая проверка, запуск, перезапуск, остановка, удаление ошибок падений)

  • Устройства — Работа с устройствами, мониторинг, отслеживание соединений, вызов методов устройства

  • Схема — Карта устройств и их подключения

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

Hidden text

32ff8f0b8d4d92ac224728682a1f1890.png

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

Hidden text

12fb6a2bf80abd398bbe744be02a1582.png

Для каналов shares есть дополнительная вкладка Данные, в которой можно в онлайне наблюдать за обновлениями специальной области памяти устройства.

Hidden text

f7bd6c6e680aee7f38d1d6f240f78d30.png

Для вызова методов устройства можно открыть через кнопку play специальное окно запроса. Функционал минимальный, заполнить ручками action c набором параметров и выполнить. Потенциально, тут заложено гораздо больше, можно было бы сделать выпадающее меню с выбором доступного метода, но, к сожалению, даже на красивый разбор ответа руки не дошли.

Hidden text

a4db504b9e34846926eb6acbfc5544ed.png

Во вкладке Схема можно расставить устройства в нужные места и сохранить структуру. Построение схемы сделано на SVG, позволяет изменять масштаб схемы с помощью колесика мыши и перемещать ее для более удобного просмотра.

Hidden text

32ea2aca48b797d003ab4fba55e1060d.png

Свои интерфейсы

С появлением реактивных фреймворков типа Vue2/3 писать веб приложения для отображения данных в онлайн режиме стало действительно просто. Я предпочитаю делать небольшие веб интерфейсы для мобильных телефонов, но есть и приложения, написанные с использованием Electron или просто для настольных ПК.

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

Выглядит как-то так:

Hidden text

9798000929480299fec075d703746f2e.png

После перехода на главную, происходит подключение к VRack и формируется интерфейс на основе структуры сервис-потока.

В данном случае это интерфейс для парковок Vector-AP, он используется для оперативного реагирования на события, которые происходят на парковке.

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

Вот пример отображения стойки въезда:

Hidden text

a51cd33b2aa000e89208cbf263285355.png

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

Что еще?

Я постарался описать достаточно поверхностно основные вещи, которые реализованы в VRack, и примеры того, что на его основе можно сделать.

На данный момент есть какая-никакая официальная документация, которую я старался поддерживать в актуальном состоянии — GitLab VRack, там же можно найти документацию на API и ссылки на остальные значимые проекты, связанные с VRack.

Есть демонстрация работы VRack в онлайне. Отображает состояние некоторых железок. Также на ней можно понажимать на виртуальные кнопки, которые управляют реальными устройствами. Очень большая просьба не взламывать ее.

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

Положительные особенности

Хотелось бы сказать про положительные особенности VRack, которые я могу сам отметить не как разработчик VRack, а как пользователь.

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

Второй особенностью хотелось бы выделить его простоту. В нем нет 1000 зависимостей или кучи кода. Все максимально прозрачно и просто, с минимальным количеством кода. Да, код далек от идеала, поскольку я начинал его писать еще с 0 опыта программирования на JS, но он уже прошел проверку временем.

Еще огромным плюсом можно выделить вполне понятный деплой. В 90% случаев, чтобы перенести сервисы с одного сервера на другой, нужно просто скопировать одну папку и выполнить одну команду.

Нет никаких автообновлений и тп. Код очень просто поддерживать и также просто его не поддерживать. О чем это? У нас есть пару сервисов, которые работают на очень-очень старой версии VRack, с виртуальными устройствами, которые уже давно не поддерживаются, но этот сервис работает, трогать его никто не собирается. В случае бэкапа, необходимо заархивировать одну папку, а в случае восстановления, разархивировать и выполнить пару команд.

Классы устройств, например, добавляются не через npm в node_modules. Каждый набор устройств лежит в собственной папке с собственным package.json и собственной папкой node_modules. Такой подход позволяет использовать разные версии библиотек для разных наборов устройств.

В VRack уже решено много не очевидных проблем, например, вы можете держать в git схемы сервис-потоков. Но в сервис-потоках обычно могут храниться пароли или api-key. Для того, чтобы не хранить в git конфиденциальную информацию, вы можете подменять нужные вам параметры специальным файлом, который дублирует структуру сервис-потока, но подменят им только то, что там указано. Такие файлы обычно создаются отдельно и игнорируются в .gitignore.

Что дальше

Несмотря на то, что прошло уже достаточно много времени, я так и не нашел никакой альтеративы VRack. Изначально, когда я начал его писать, у меня не было ни малейшего желания тратить на него время, я пересмотрел разные варианты, но так и не нашел ничего, что бы удовлетворило меня. Не поймите неправильно, была получена масса удовольствия от написания собственного продукта. Проблема тут пожалуй в том, что я бы предпочел больше разрабатывать на VRack, чем разрабатывать сам VRack.

VRack и все, что было для него сделано — сделал я один. Очень много времени было потрачено на тестирование концепций, переписывание и доработку библиотеки устройств и тп. А, поскольку разрабатываю я на нем один, у меня совсем нет мотивации доработать все до представительного вида.

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

Так что же дальше? Мне интересно узнать мнение других людей. Может кто чего дельного подскажет. Может кто-то заинтересуется. Может кто-то почерпнет для себя некоторые идеи. Любой результат — тоже результат. В любом случае, хотя бы для меня будет закрыт этот гештальт.

© Habrahabr.ru