“Все счастливые программисты похожи друг на друга, каждый несчастливый программист несчастлив по-своему”

Преамбула

Итак, вы используете low-code платформу - этот факт неоспорим и не подлежит пересмотру. Какие метания разума и духа привели вас(нас) на этот скользкий путь оставим за рамками данной статьи. 

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

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

Задачи

  1. Обеспечить поддержку окружений для разработки(dev), тестирования (staging), эксплуатации(production).

  2. Организовать версионирование кода процессов(workflows) и хранение их в git.

  3. Внедрить СI/CD автоматизации в нашу жизнь.

Проблемы

Основная проблема может возникнуть с данными аутентификации для нод, которые используются в ваших процессах. В N8N - это отдельная сущность, хранящаяся в базе данных под названием Credentials. Мы можем производить импорт/экспорт из одного окружения в другое - для этого нам нужно лишь использовать общий ключ шифрования (задается через переменную N8N ENCRYPTION_KEY), однако для разных окружений нам требуются отличные друг от друга пароли, ключи, секреты - что является общепринятой практикой безопасности - и в таком случае нужно думать об организации использования этих данных, потому что они привязываются к процессам, как минимум.

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

Реализация

Аудит

Довольно слов, пора засучить рукава и приняться за дело - я рекомендую начать с аудита ваших процессов и выделить все ноды, в которых вы обращаетесь к внешним ресурсам - базы данных, API, сервисы, а также данные авторизации для них. После чего, составить список переменных окружения(environment variables), в результате, получится примерный список:

Не забывайте использовать имя вашего сервиса(приложения) как префикс переменной - это позволяет изолировать ваши собственные переменные и избежать конфликта в будущем.

Docker

Следующим этапом я рекомендую создать свой Dockerfile в котором перечислить указанные выше переменные. Может показаться, что это совсем не обязательно, и переменные можно передать при запуске контейнера, но поверьте мне, с большой долей вероятности, собственный контейнер и возможность его изменять вам могут пригодиться в будущем. Альтернативным вариантом может являться указание этих переменных в docker-compose.yml.

version: '3.6'
services:
 n8n:
   image: n8nio/n8n:${N8N_ENGINE_VERSION}
   container_name: ${N8N_CONTAINER_NAME}_${SECNUM_ENV}
   restart: "on-failure"
   networks:
     - secnum_${SECNUM_ENV}
   ports:
     - ${N8N_PUBLIC_PORT}:${N8N_PORT}
#    depends_on:
#      - "postgres"
   
   labels:
     - production
   volumes:
     - ./n8n/workflows:/data/workflows
   environment:
     - DB_TYPE=${N8N_DB_TYPE}
     - DB_POSTGRESDB_DATABASE=${N8N_DB_POSTGRESDB_DATABASE}
     - DB_POSTGRESDB_HOST=${N8N_DB_POSTGRESDB_HOST}
     - DB_POSTGRESDB_PORT=${N8N_DB_POSTGRESDB_PORT}
     - DB_POSTGRESDB_USER=${N8N_DB_POSTGRESDB_USER}
     - DB_POSTGRESDB_SCHEMA=${N8N_DB_POSTGRESDB_SCHEMA}
     - DB_POSTGRESDB_PASSWORD=${N8N_DB_POSTGRESDB_PASSWORD}

.env

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

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

Обратите внимание, что если вы разрабатываете под MacOS, а затем используете этот проект под Linux - поведение Docker по чтению и интерпретации этих файлов различается.

В нашем проекте используется папка env, в которой лежат все эти файлы:

/project/env

├── dev.env

└── prod.env

Makefile

Используете ли вы классическую утилиту make или что-то ещё  - не столь важно, но использование такого подхода очень важно - и как средство для ленивых, и как часть концепции Docs-As-Code.

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

Здесь, сугубо для примера, я приведу команду запуска dev-окружения:

.PHONY: dev

dev:

   docker-compose --env-file ./env/dev.env --file docker-compose-dev.yml --project-name dev up

Обратите внимание, что в docker-compose мы передаем наш dotenv-файл - для иных окружений передавайте соответствующий файл.

N8N

Настало время привнести изменения в наши процессы (workflows) - это потребует некоторой их переработки.

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


.PHONY: export
export:
   docker exec n8n_${MYSERVICE_ENV} n8n export:workflow --backup --separate --output=/data/workflows

Обратите внимание на переменную MYSERVICE_ENV - передавая её в командной строке мы можем обращаться к различным окружениям запущенным на одном сервере. Естественно, что именуются сервисы при запуске также с помощью этой переменной.

В результате вызова этой команды мы получим набор из JSON файлов, в которых находится все ваши процессы - теперь мы можем работать с ними в VSCode - массово искать и заменять текст, а самое главное - сохранить их в репозитории

Кстати, команда импорта файлов выглядит так:

.PHONY: import
import:
   docker exec n8n_${MYSERVICE_ENV} n8n import:workflow --separate --input=/data/workflows

Собственно, процесс работы теперь выглядит так: 

  • Изменили workflow в интерфейсе dev-окружения

  • Выгрузили файлы

  • Создали commit с изменениями

  • Загрузили файлы в prod (staging, whatever)

Что же нам необходимо изменить в наших процессах?! Давайте посмотрим на картинку:

Переменные в процессе
Переменные в процессе

Это наш ключ к успеху - мы обращаемся к переменным окружения docker-контейнера, в который при старте были переданы значения из env-файла.

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

"value": "={{ process.env.MYSERVICE_API_URL }}"

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

.PHONY: activate
init:
   docker exec n8n_${MYSERVICE_ENV} n8n update:workflow --all --active=false
   docker exec n8n_${MYSERVICE_ENV} n8n update:workflow --all --active=true

.PHONY: execute
execute:
   docker exec n8n_${MYSERVICE_ENV} n8n execute --id $(WORKFLOW_ID)

CI/CD

Здесь я не буду давать каких-то рекомендаций, скажу ли только, что мы используем Gitlab CI/CD и локальный Runner - для того чтобы реагировать на изменения в репозитории - тестировать, применять изменения в различных окружениях и так далее. Имея встроенные механизмы изменения переменных - это не так уж сложно сделать в любой системе.

Заключение

Статья не претендует на какие-то откровения, мне бы лишь хотелось сократить время затрачиваемое на поиск истины тем, кто ступил на этот путь. Развитие Low‑Code платформ решает одни проблемы и создаёт новые. Наш опыт говорит нам — «инструмент под задачу» и для многих задач такое подход вполне обоснован!

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

Комментарии (5)


  1. VitalySh
    14.05.2023 03:42
    +6

    Из статьи я не понял главного - что это за инструмент? Что он решает, о каких нодах речь? Какую low-code платформу используют авторы? Если это low-code платформа для которой нужен дополнительный софт в виде N8N, VScode, Gitlab, Docker - где там вообще этот low?

    Какие есть аналоги этому приложению? Это что-то вроде Hashicorp Vault?

    Почему у статьи так много плюсов?


    1. Keirichs
      14.05.2023 03:42

      К нам в компанию как-то подселяли этих ребят, чтобы они рассказали про преимущества low-code, ... и их нет.
      Нам показывали как chatGPT генерирует сценарии, а потом вы эти сценарии вставляете в условный Jenkins(они все на него похожи) и вот вам low code.
      Различия в обычном конвеере в том, что вы вместо написания if ... else ставите красивые блоки.


      1. sashker Автор
        14.05.2023 03:42

        "Ведь, если звезды зажигают — значит — это кому-нибудь нужно?". Я думаю что ChatGPT тут пока из области фантастики, но для самих этих платформ есть область применения, особенно там где нет команды, кучи написанного кода, настроенных процессов. И для меня тут дело не в логике и в оформлении процессов как таковых, а скорее в обвязке - системе запуска, визуальном контроле, уведомлениях, истории и пр.


    1. sashker Автор
      14.05.2023 03:42

      N8N -это средство автоматизации бизнес-процессов(аналог Zapier и прочих). Мы его используем в том числе, так как Workflow там составляются существенно проще, чем в той же Camunda(или других BPMN системах).Собственно, материал этот для тех кто использует N8N(или аналоги), чтобы показать подход, а не обзор платформы. Любая low-code платформа, когда нужно разворачивать её у себя и поддерживать, требует более широкого спектра компетенций нежели использование в облаке.


  1. Stavr666
    14.05.2023 03:42

    2 хипстерск концепта по цене одного. Круто, круто.
    Когда нибудь кто-то (другой) и настоящей разработкой займётся, но почти бесплатно (деньги уже будут попилены на модные devops, scrum и lowcode).