Skip to content

Стенд под своим брендом

Сценарий: из той же кодовой базы поднимается второй продукт под другим именем, логотипом и палитрой — со своим доменом и своими данными. Референс — стенд TurboBPM (docker-compose-turbobpm.yml).

Бренд и изоляция данных от машины не зависят: стенд может стоять на своём сервере или рядом с другими. TurboBPM развёрнут по второму варианту — на том же сервере, что orbita.bamboo2panda.ru и app.orbitaitsm.ru, поэтому общий у них только Traefik (см. второй стенд на одном сервере). Свои у стенда БД, Redis, Meilisearch, SeaweedFS, тома, compose-проект и env-файл.

Что определяет бренд

Один и тот же образ backend/frontend обслуживает все бренды. Различает их переменная BRAND_KEY в env-файле стенда: по ней бэкенд отдаёт в /api/app-config название, ссылки, палитру и пути к ассетам, а фронтенд применяет их в рантайме. Пересборка образа при смене бренда не нужна.

  • Палитры и наборы ассетов перечислены в src/config/brand.php.
  • Файлы логотипов лежат в frontend/public/brand/<key>/ и собираются скриптом build-tools/brand/build.py (см. README рядом с ним).
  • Отдельные цвета можно переопределить, не заводя новый бренд: BRAND_PRIMARY, BRAND_ACCENT_ON_DARK и остальные из секции overrides.

Инсталляция, где BRAND_* не заданы, работает как раньше: дефолты в конфиге равны значениям Orbita.

Порядок развёртывания

Автоматический путь — workflow Bootstrap TurboBPM (.github/workflows/bootstrap-turbobpm.yml, запуск вручную через workflow_dispatch, параметр — домен). Он делает всё, что описано ниже: клонирует репозиторий в /opt/hosting/turbobpm/prod, генерирует turbobpm.env с новыми секретами, отдаёт маршруты общему Traefik, собирает и поднимает стек, прогоняет миграции и сидеры, проверяет health и доступность домена снаружи. Повторный запуск существующий turbobpm.env не трогает.

Ручные шаги ниже — то же самое, если разворачиваете руками или разбираете поломку.

1. DNS

A-запись домена на публичный IP сервера. Сертификат стенду выпускает общий Traefik резолвером le-http (HTTP-01), поэтому Cloudflare-токен не нужен, но порт 80 должен быть открыт снаружи, а домен не должен проксироваться в оранжевом режиме. Домен за Cloudflare-прокси обслуживается дефолтным резолвером letsencrypt (DNS-01) — тогда в маршрутах стенда уберите certResolver.

2. Checkout и конфигурация

bash
git clone <repo_url> /opt/hosting/turbobpm/prod
cd /opt/hosting/turbobpm/prod

cp turbobpm.env.example turbobpm.env
chmod 600 turbobpm.env

Заполните в turbobpm.env домен (APP_DOMAIN, APP_URL, AWS_URL, SANCTUM_STATEFUL_DOMAINS, SESSION_DOMAIN) и все пустые секреты. Команды генерации указаны в комментариях рядом с каждым полем.

Не переиспользуйте секреты другого стенда

APP_KEY шифрует конфигурации каналов и LDAP-серверов в БД. Общий ключ между стендами означает, что компрометация одного раскрывает данные второго.

Свои сервисы — только по container_name

DB_HOST, REDIS_HOST, MEILISEARCH_HOST, AWS_ENDPOINT указывают на turbobpm_prod_db / _redis / _meili / _s3, а не на database / redis / meilisearch / seaweedfs. backend и reverb подключены к общей сети Traefik, где у соседних стендов есть контейнеры с теми же алиасами, и Docker DNS отдаёт по ним чужой контейнер. На этом сервере проверено: с DB_HOST=database backend стенда резолвил боевую БД соседа (спас только несовпавший пароль), а чужой Redis слушает без пароля и принял бы сессии и очереди молча. Оба workflow проверяют это перед запуском.

3. Маршруты в общем Traefik

Своего Traefik у стенда нет: порты 80/443 держит orbita_prod_traefik. Его file-provider читает каталог целиком, поэтому маршруты стенда кладутся туда отдельным файлом — конфиг соседних стендов при этом не трогается. Домен вживляется в шаблон (Traefik не подставляет переменные окружения):

bash
DOMAIN=$(grep '^APP_DOMAIN=' turbobpm.env | cut -d= -f2)

# каталог, который смонтирован в Traefik как /etc/traefik/dynamic
DYN=$(docker inspect orbita_prod_traefik \
  --format '{{range .Mounts}}{{if eq .Destination "/etc/traefik/dynamic"}}{{.Source}}{{end}}{{end}}')

sed "s|{{APP_DOMAIN}}|$DOMAIN|g" \
  traefik/turbobpm/dynamic/routers.yml.tpl > traefik/turbobpm/dynamic/routers.yml
install -m 644 traefik/turbobpm/dynamic/routers.yml "$DYN/turbobpm.yml"

Traefik подхватывает новый файл сам (watch: true), рестарт не нужен. Оба отрендеренных файла в .gitignore — они пересоздаются при каждом деплое.

4. Запуск

Обязательно со своим -p: он изолирует тома и имена от соседних стендов.

bash
COMPOSE="docker compose --env-file turbobpm.env -p turbobpm_prod -f docker-compose-turbobpm.yml"
$COMPOSE build
$COMPOSE up -d

docker exec turbobpm_prod_backend php artisan migrate --force
docker exec turbobpm_prod_backend php artisan db:seed --class=RoleSeeder --force
docker exec turbobpm_prod_backend php artisan db:seed --class=FrontendTranslationSeeder --force
docker exec turbobpm_prod_backend php artisan search:rebuild

docker exec -it turbobpm_prod_backend php artisan superadmin:create \
  --name="Platform Admin" --email=<почта>

tenant:init и admin:create здесь не работают — это команды on-premise режима, а стенд запущен с APP_MODE=saas: тенанты заводятся регистрацией на /signup или суперадмином. AdminSeeder не запускайте — он создаёт admin@test.com с фиксированным паролем.

5. Проверка

bash
curl -I https://<домен>                                   # → 200
docker exec turbobpm_prod_backend curl -fsS http://localhost/api/health/deep
curl -s https://<домен>/api/app-config | jq .brand        # имя, палитра, ассеты
docker logs orbita_prod_traefik 2>&1 | grep -i <домен>    # сертификат выпущен
docker ps --filter name=orbita_prod --filter name=orbitaitsm_prod

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

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

Обновление

Workflow deploy-turbobpm.yml (запуск вручную через workflow_dispatch), либо теми же шагами вручную из своего checkout со своими -p, --env-file и -f. Workflow перерендеривает маршруты, снимает дамп БД перед миграциями и откатывает код на предыдущий коммит, если миграции или health-check не прошли.

Секреты репозитория: SERVER_HOST, SERVER_USER, SERVER_SSH_KEY — те же, что у соседних стендов, стенд живёт на той же машине. Если стенд переедет на свой сервер, задайте TURBOBPM_SERVER_HOST/TURBOBPM_SERVER_USER/ TURBOBPM_SERVER_SSH_KEY — оба workflow предпочитают их, когда они есть.

Orbita ITSM — документация для системных администраторов