Локальный запуск
Workflow-движок распространяется как open source, и его можно полностью запустить локально — это самый быстрый способ его попробовать. Понадобятся установленные Docker и kind (Kubernetes in Docker): каждый запущенный сценарий выполняется в собственном поде локального k8s-кластера — тем же механизмом, что и в боевом окружении.
Быстрый старт
Заголовок раздела «Быстрый старт»Создайте локальный kind-кластер:
Скрипт идемпотентен — его можно перезапускать повторно, например после изменений в коде раннера, чтобы пересобрать и перезагрузить его образ в кластер../scripts/kind-dev-setup.shСкопируйте
.env.exampleв.envв корне репозитория. Для чистого локального запуска можно ничего не менять — у каждого секрета (JWT_SECRET,CREDENTIALS_ENCRYPTION_KEYи т.д.) есть значение по умолчанию для разработки (dev-compose также задаётSEED_DEFAULT_ADMIN=true; сам backend по умолчанию используетfalse). ПриNODE_ENV=productionbackend не запустится, покаJWT_SECRET,DB_PASSWORDиCREDENTIALS_ENCRYPTION_KEYне заданы нестандартными значениями длиной не менее 32 символов.Поднимите стек:
docker compose -f docker-compose.workflow.yml up –buildОткройте
http://localhost:5175и войдите какadmin/admin(учётная запись создаётся автоматически при первом запуске backend’а) либо зарегистрируйте новый аккаунт со страницы входа.- Создайте проект, затем в нём — папку и документ типа «Функция»: задайте имя и тело функции.
Нажмите «Build & Run» на панели инструментов. Это скомпилирует все функции проекта и создаст под в kind-кластере для их выполнения — под очередью задач вида
workflow-dev-<projectId>(её имя отображается на панели инструментов рядом с индикатором статуса, а также командойkubectl -n workflow get pods).Откройте функцию и выберите Тестовый стенд → Запустить функцию на панели инструментов (если у функции есть параметры, их спросят): иконка, на которой идёт выполнение, подсвечивается на холсте, а боковая панель показывает статус и историю. Запустить выполнение можно и через CLI администратора Temporal:
docker compose -f docker-compose.workflow.yml exec temporal-admin-tools \ temporal workflow start --task-queue workflow-dev-<projectId> --type <functionName> \ --workflow-id demo-1 --input '{}'Ход выполнения можно наблюдать в Temporal Web UI:
http://localhost:8080.Кнопка «Publish» переводит текущий код в стабильную версию (общую очередь задач
workflow-<projectId>) — в модальном окне «Версии» доступны откат («Активировать») и остановка более старых версий («Остановить раннер»). Версия с меткой «Текущая» — та, на которую идут новые запуски и триггеры и которую поднимает «Запустить»; откат сохраняется после Стоп/Запуск и пока простаивающий раннер спит.
Из чего состоит стек
Заголовок раздела «Из чего состоит стек»docker-compose.workflow.yml поднимает:
- самостоятельно размещённый сервер Temporal (версия 1.32, с собственной базой Postgres, разовыми заданиями создания схемы и namespace
default), его админ-CLI и веб-интерфейс; - отдельную базу Postgres для backend’а (проекты, папки, документы, пользователи);
backend — сервис, который хранит проекты, компилирует функции и управляет запуском/остановкой подов выполнения через Kubernetes API;
- client — веб-редактор схем (приложение администратора открывается по адресу
/admin); MinIO — S3-совместимое объектное хранилище для файлов (консоль —
http://localhost:9001);- media — сервис на ffmpeg для интеграции Медиа;
activepieces — вспомогательный сервис для части интеграций (см. Интеграции).
Каждый опубликованный сценарий выполняется не отдельным процессом внутри контейнера backend’а, а собственным подом в Kubernetes — именно поэтому нужен локальный kind-кластер.
Изоляция проектов в Temporal
Заголовок раздела «Изоляция проектов в Temporal»По умолчанию (TEMPORAL_TENANT_ISOLATION=shared) все workflow проектов выполняются в единственном
namespace default Temporal без авторизации — этого достаточно для локальной разработки и для
экземпляра, где все проекты принадлежат доверенным людям. Runner-под исполняет код самого проекта, поэтому на
экземпляре с несколькими пользователями включите режим per-project: у каждого проекта свой
namespace (falang-<projectId>), runner-поды аутентифицируются короткоживущим JWT,
действительным только для этого namespace, а межнеймспейсные команды на сервере выключены. Запуск (ключ нужно
держать одним и тем же между запусками):
export TEMPORAL_JWT_PRIVATE_KEY="$(openssl genpkey -algorithm RSA -pkeyopt rsa_keygen_bits:2048)"
export PROJECT_TOKEN_SECRET="$(openssl rand -hex 32)"
TEMPORAL_SERVER_CONFIG=server.yaml TEMPORAL_TENANT_ISOLATION=per-project \
docker compose -f docker-compose.workflow.yml up --build
Публичный ключ Temporal берёт у backend на внутреннем порту (/internal/temporal/jwks.json), поэтому
для смены ключа достаточно перезапустить backend. В этом режиме веб-интерфейс Temporal на порту 8080 войти
не сможет; используйте админ-CLI — он ходит во внутренний frontend (порт 7236, на хост не публикуется):
docker compose -f docker-compose.workflow.yml exec temporal-admin-tools \
temporal workflow list --namespace falang-<projectId>
Переменные backend’а, на которых держится этот режим:
TEMPORAL_TENANT_ISOLATION—shared(по умолчанию) илиper-project. В режимеper-projectbackend не запустится безTEMPORAL_JWT_PRIVATE_KEY, а при старте проверит, что Temporal действительно отклоняет запросы без токена и с токеном, который он не может проверить (если нет — backend откажется стартовать: такая изоляция ничего не стоит).TEMPORAL_JWT_PRIVATE_KEY— приватный RSA-ключ в формате PKCS#8 PEM (подпись RS256). Необязательные:TEMPORAL_JWT_KEY_ID(по умолчанию — отпечаток ключа),TEMPORAL_JWT_PREVIOUS_PUBLIC_KEY(прежний публичный ключ, публикуется рядом с новым на время ротации),TEMPORAL_JWT_TTL_SECONDS(срок жизни токена пода, по умолчанию 3600; поды обновляют его сами).PROJECT_TOKEN_SECRET— ключ, из которого выводятся токены проектов, с которыми runner-поды ходят в backend (в production — не короче 32 символов и не значение по умолчанию; на всех репликах backend’а одно и то же).TEMPORAL_NAMESPACE_RETENTION_DAYS— сколько хранятся завершённые выполнения в namespace проекта (по умолчанию 7).TEMPORAL_TLS=true— если frontend Temporal работает по TLS.RUN_JOURNAL_RETENTION_DAYS_DEV/RUN_JOURNAL_RETENTION_DAYS_PROD— сколько дней журнал запусков хранит записи тестовых и опубликованных запусков (по умолчанию 14; журнал живёт дольше собственной истории запусков движка).
Namespace удалённого проекта backend удаляет примерно через сутки после удаления проекта. Переключение
существующего экземпляра с shared на per-project не переносит старые выполнения из
default; расписания, оставшиеся там, backend ставит на паузу, а переопубликованный проект начинает
работать в своём namespace.
Известная особенность
Заголовок раздела «Известная особенность»При самом первом запуске docker compose up после (пере)создания сети
falang-workflow-network контейнер backend’а иногда не успевает подключиться ко второй сети
(kind) — в его логах будут постоянные ошибки вида getaddrinfo EAI_AGAIN app-postgres.
Лечится одной командой:
docker compose -f docker-compose.workflow.yml restart backend
Повторно эта проблема не возникает, пока обе сети не пересоздаются заново.
Публичный и внутренний порты
Заголовок раздела «Публичный и внутренний порты»Backend слушает два порта. Публичный (PORT, в compose-файлах 4000) отдаёт API приложения — только его
нужно публиковать через reverse proxy или ingress. Внутренний (INTERNAL_PORT, по умолчанию 3001) отдаёт
только /internal/* — API для runner-подов, сервиса ActivePieces и медиа-сервиса; на публичном порту эти
пути отвечают 404. Не открывайте внутренний порт в интернет: укажите его в BACKEND_INTERNAL_URL этих
сервисов (внутри кластера — через ClusterIP Service).
Логи и метрики
Заголовок раздела «Логи и метрики»LOG_FORMAT=json включает вывод логов backend по одному JSON-объекту на строку (уровень, время, контекст,
сообщение и — внутри HTTP-запроса — requestId, то же значение приходит в ответе в заголовке
x-request-id; входящий x-request-id до 128 безопасных символов используется как есть). По
умолчанию — читаемый текст. LOG_LEVEL необязательно фильтрует: один уровень (warn — это warn
и выше) или список через запятую. GET /internal/metrics на внутреннем порту отдаёт метрики Prometheus
(процесс, Node.js, HTTP-запросы по шаблону маршрута, сборки, runner-поды, вызовы AI-агента); аутентификации нет, так
что держите этот порт закрытым.
