Skip to main content

Развёртывание on-premise

Актуально для: SimpleTwo 0.9.x · Проверено: 25.08.2026

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

Что в статье не разбирается: облачная поставка, изолированный контур без доступа в интернет, телефония и запись конференций — они опциональны и вынесены в отдельные статьи.

Что ставится руками, а что само

Руками ставится один сервис — координатор. Всё остальное (базу, чат, календарь, SFU, TURN, VPN-узлы) координатор ставит сам: вы даёте ему SSH-доступ к хосту и выбираете роль, он копирует туда бинарник, пишет конфиг и systemd-юнит и запускает сервис. Бинарники всех ролей и Xray уже лежат внутри образа координатора — отдельно ничего скачивать не нужно.

1. Что вы разворачиваете

Одна приватная сеть, и все правила доступа в ней записаны явно. Хосты стоят в общей приватной сети, а между ними — только правила, которые пишет сама плоскость управления:

  • база принимает только два хостаmessaging и calendar, каждый как /32 в pg_hba;
  • шина принимает только тех, кто на неё ходит — узлы sfu, mediation и recorder, — правилом фаервола на своём хосте плюс пароль.

Оба правила применяются заново на каждом опросе состояния, поэтому ни одно из них не приходится помнить и поддерживать руками.

                             интернет

┌──────────────┬─────────────┼──────────────┬───────────────┐
│ │ │ │ │
координатор messaging sfu ×N turn VPN-узлы
(80/443) chat.s2.<домен> calls.s2.<домен> 3478 + (gateway)
│ │ │ 49152–65535 по IP, 443/tcp
└──────────────┴─────────────┴──────────────┴───────────────┘
одна приватная сеть 10.x.x.x

┌───────────────┼────────────────┐
postgres calendar redis
только /32 хостов своя база на только адреса
messaging и calendar том же postgres тех, кто ходит на шину
РольПубличноВ приватной сетиЗачем
координаторда, 80 и 443даплоскость управления: каталог и вход, политики, реестр узлов, админка, выпуск сертификатов, установка остальных ролей
postgresнетдабаза чата и календаря; снаружи не доступна, а внутри принимает только хосты messaging и calendar — каждый как /32 в pg_hba
messagingдадачат, треды, файлы, сигналинг звонков; продуктовый сервер
calendarдадакалендари, ICS-подписки, CalDAV. Публичен потому, что читатели — не наш клиент: Outlook обновляет подписку, CalDAV-клиент опрашивает. Держит свою базу на том же postgres
redisнетдашина координации. Нужна со второго узла sfu и обязательна для телефонии. Единственная граница — правило фаервола, поэтому на хосте шины обязателен ufw или firewalld
sfuдадамедиасервер: медиа и сигналинг звонков. Пересылает потоки, не микширует
turnдарелей для клиентов за строгим NAT
gatewayда, 443/tcpDMZ для корпоративного выходаVPN-узлы (VLESS+Reality), по одному на GEO
recorderнетдазапись конференций (опционально)
mediationдадателефония SIP/PSTN (опционально)
supportнетDMZ рядом с координаторомдемон бота поддержки (опционально)

2. Что нужно заранее

Хосты и размеры на старт

ХостvCPU / RAM / дискЧем ограничен
координатор2 / 4 / 20 ГБничем; держит SQLite и сертификаты в томе
postgres2 / 4 / 40 ГБ SSDдиском
messaging2 / 4 ГБдиском: вложения лежат на нём, пока не подключено S3
redis1 / 1 ГБничем; держит маршрутизацию комнат, не данные
sfu, каждый4 / 8 ГБканалом: SFU пересылает каждый поток каждому участнику, аплинк упирается раньше процессора
turn2 / 2 ГБканалом, по той же причине
calendar2 / 4 ГБопросом, а не людьми: подписанный Outlook и CalDAV-клиент приходят по своему расписанию
gateway, каждый1 / 1 ГБканалом

ОС на хостах ролей: Ubuntu 22.04 / 24.04, amd64 или arm64. На хосте координатора нужны Docker Engine и плагин Compose.

Имена DNS

Соглашение: база тенанта — s2.<домен-организации>, сервисы — поддомены от неё.

ИмяНа чтоОбязательно
s2.<домен>координаторда
chat.s2.<домен>хост messagingда, для чата
calls.s2.<домен>хост sfuда, для звонков
turn.s2.<домен>хост turnрекомендуется

Приложение находит ваш контур по домену почты: ivan@<домен>s2.<домен>. Если поддомен s2. занять нельзя, есть альтернатива — файл https://<домен>/.well-known/simpletwo.json в корне основного домена.

VPN-узлам DNS не нужен: клиент ходит на IP:443, а SNI в их конфигурации — домен камуфляжа, а не ваша запись.

Порты

ГдеНаружуЗачем
координатор80/tcp, 443/tcpадминка и API; порт 80 нужен для выпуска сертификатов (HTTP-01), 443 — для TLS-ALPN-01
messaging443/tcpREST и WebSocket клиентов, выпуск своего сертификата
sfu443/tcpwss://calls.s2.<домен> и медиа
turn3478 + 49152–65535/udpрелей
gateway443/tcpVLESS+Reality
все хосты ролей22/tcp с адреса координатораустановка и обновление по SSH

Доступы

  • SSH к каждому хосту роли: пользователь root либо пользователь с беспарольным sudo, и приватный ключ, который вы дадите координатору. На хостах нужен curl.
  • Доступ к образу координатора: GitLab Container Registry или переданный вам архив образа.
  • Домен организации и возможность править его DNS-зону.

3. Шаг 1. Координатор

mkdir -p /opt/simpletwo && cd /opt/simpletwo
# положите рядом deploy/docker-compose.coordinator.yml как docker-compose.yml
cp <путь>/docker-compose.coordinator.yml ./docker-compose.yml
echo "COORDINATOR_IMAGE=<registry>/coordinator:<версия>" > .env
docker login <registry>
docker compose pull && docker compose up -d

В compose-файле нет ни одного секрета, и это сознательно: координатор генерирует свои секреты сам (секрет подписи токенов, токен узлов, учётную запись администратора) при первом запуске и хранит их в томе coordinator-data. Порты: 80:8080 и 443:8443 — TLS терминируется внутри координатора, внешний обратный прокси не нужен.

Проверка:

docker compose ps                       # healthy
curl -fsS http://127.0.0.1/healthz # 200

Заберите одноразовый bootstrap-токен — он печатается в лог при первом запуске и лежит в томе:

docker compose logs coordinator | grep -i bootstrap
docker compose exec coordinator cat /data/bootstrap-token

Откройте http://<адрес>/admin, вставьте токен, задайте пароль администратора и публичный URL (https://s2.<домен>). Дальше вход только по паролю; токен больше не действует.

Том — это весь ваш контур

В coordinator-data лежат секреты, база плоскости управления и сертификаты. Потеря тома означает переустановку контура и перерегистрацию всех узлов. Он — первый пункт в бэкапе (§9).

4. Шаг 2. Имя и сертификат координатора

  1. Создайте A-запись s2.<домен> на публичный адрес хоста координатора.
  2. В админке: Settings → TLS → Tenant domains → добавьте s2.<домен>. Сертификат выпустится автоматически при первом обращении по этому имени.
  3. Проверьте снаружи, а не с самого хоста:
curl -fsS https://s2.<домен>/healthz
curl -fsS https://s2.<домен>/.well-known/simpletwo.json
note

Выпуск сертификата требует, чтобы хост был доступен из интернета на 80 и 443 в момент выпуска. Если запись ещё не разошлась по DNS, выпуск не удастся — это не ошибка настройки, просто повторите позже.

5. Шаг 3. Роли, строго по порядку

Все роли ставятся из админки: Installed servers → Add a host. Для каждого хоста нужны адрес, SSH-пользователь, приватный ключ, роль и приватный адрес — тот, по которому этот хост видят остальные. Перед установкой нажмите Test SSH: он проверяет доступность до того, как что-то начнёт устанавливаться. Ход установки виден в Provisioning log, статус проходит pending → deploying → waiting → active.

На хосте шины нужен фаервол

redis защищён паролем и правилом, разрешающим 6379 только тем, кто на шину ходит — узлам sfu, mediation и recorder. В одной приватной сети это правило и есть вся граница шины, поэтому на хосте без ufw и без firewalld установка откажется выполняться, а не поднимет базу, доступную всей сети. Список адресов плоскость управления ведёт сама: добавили узел SFU, телефонию или запись — правило дополнилось на следующем опросе состояния.

Порядок:

1) postgres2) messaging3) calendar (опционально)переустановить postgres один раз4) redis5) sfu узел A6) sfu узел B7) turn

Почему postgres переустанавливается: правила доступа к базе сужаются до /32 адресов хостов messaging и calendar, а записать их нельзя, пока этих хостов не существует. Одна переустановка закрывает оба.

Одно правило, которое молча ломает звонки

Приватный адрес нельзя отдавать клиентам. Приватный адрес хоста идёт в поле private address; адрес, по которому клиенты обращаются к SFU, — в node IP. Перепутаете — звонок установится, медиа не пойдёт, соединение отвалится по таймауту, и ни в одном журнале ничего не будет.

Ещё две вещи, которые продукт не даст сделать неправильно:

  • Второй узел sfu без redis установить нельзя — установка откажется. Два узла без шины ведут каждый свою таблицу комнат: двое «в одной встрече» на разных узлах не видят друг друга, и никто об этом не сообщает.
  • calendar можно не ставить. Пока хоста календаря нет, клиенты прячут экраны календаря, а не показывают кнопки, которые не работают.

Чат: имя и сертификат

После установки messaging откройте карточку Messaging TLS: укажите hostname chat.s2.<домен>Save configRedeployIssue. Сертификат выпустится по TLS-ALPN-01 на :443, а адрес https://chat.s2.<домен> попадёт в дискавери автоматически. Для проверки схемы есть флаг staging — тестовый сертификат Let's Encrypt, который не расходует лимиты.

Звонки: адрес для клиентов

Для sfu нужен TLS-фронт с сертификатом на calls.s2.<домен> — этот сертификат выпускается вручную, координатор его не выдаёт. Адрес wss://calls.s2.<домен> указывается в Settings → Calls → Client URL.

Проверка после каждой роли

curl -fsS https://chat.s2.<домен>/healthz     # messaging
curl -fsS https://s2.<домен>/healthz # координатор видит состояние ролей

В админке у каждой установленной роли есть строка состояния: координатор сам опрашивает /healthz ролей. Роль в статусе active, но с красным состоянием — это установленный сервис, который не работает; смотрите Host logs.

6. Шаг 4. Каталог и вход

  1. Settings → Single sign-on (OIDC) — подключите корпоративный провайдер (OIDC/SAML/ADFS).
  2. Groups / roles → profile — сопоставьте группы каталога с ролями и атрибутами профиля.
  3. Roles — проверьте, кто администратор, кто может вести встречи, кто видит телефонию.

Отдельная статья про ADFS понадобится в двух случаях: если пользователи входят по адресу, отличному от UPN, и если руководитель в AD задан как DN, а не как почта.

7. Шаг 5. Клиенты и приёмка контура

Клиент называется SimpleTwo Connect и один на все платформы: iOS, Android, macOS, Windows и веб. Пользователь вводит рабочую почту — приложение само находит контур по домену.

Приёмка — не «сервисы запущены», а сценарий целиком:

  1. Вход сотрудника по корпоративной учётной записи.
  2. Сообщение в чате доходит до второго устройства и до второго сотрудника.
  3. Звонок один на один: слышно в обе стороны.
  4. Встреча: внешний участник заходит по ссылке из браузера, ждёт в лобби, его впускают.
  5. Туннель: клиент подключается к VPN-узлу и открывает внутренний ресурс.
  6. Все /healthz отвечают.
warning

Зелёная джоба выката и запущенный контейнер — не признак работающего сервиса. Выкат не закончен, пока каждый сервис не ответил на /healthz, а сценарий выше не прошёл целиком.

8. Опциональные роли

РольКогда нужнаЧто важно знать
gatewayнужен доступ в корпоративную сеть или выход в нужном GEOсначала создайте GEO, затем установите узел. Ключи Reality генерируются на узле, наружу уходит только публичный. DNS не требуется
recorderнужна запись конференцийединственная роль, которая работает контейнером: она пишет комнату, управляя headless Chrome. Ограничена процессором: 4 vCPU / 4 ГБ на одну одновременную запись плюс диск. Наружу не слушает ничего — берёт задания из шины и пишет в объектное хранилище. Требует redis и настроенное хранилище
mediationнужна телефония и дозвонredis — жёсткая зависимость, причём тот же, что у пула SFU: без него сервис не стартует. Каждый звонок транскодируется, поэтому хост считается по одновременным звонкам (≈0,05 ядра на звонок), а не по участникам. Входящие вызовы не балансируются: второй хост — это решение про прокси, а не про масштаб, и установка второго будет отклонена
supportнужен бот поддержкиставится в DMZ рядом с координатором. Входящие — только подписанные вебхуки с хоста messaging на :8087; наружу и к клиентам не доступен. Держит свой ключ провайдера модели и свою базу знаний; токен GitLab нужен с правом read_api на проект продукта

9. Обновление и бэкап

Обновление координатора — это новый тег образа:

cd /opt/simpletwo
sed -i 's|^COORDINATOR_IMAGE=.*|COORDINATOR_IMAGE=<registry>/coordinator:<новая версия>|' .env
docker compose pull && docker compose up -d
curl -fsS http://127.0.0.1/healthz

Откат — тот же файл с предыдущим тегом: прошлые образы остаются в реестре. Если на хосте координатора установлен GitLab Runner, эти же шаги выполняет джоба deploy на теге v*.

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

Бэкап — три вещи, и первая важнее остальных:

  1. том coordinator-data (секреты, база плоскости управления, сертификаты);
  2. базы postgres (чат и календарь) — обычным pg_dump по расписанию;
  3. вложения на хосте messaging, пока они лежат на его диске.

10. Если что-то не работает

Где смотреть: в админке — Coordinator logs, Host logs, Provisioning log, Audit log; на хосте координатора — docker compose logs coordinator; на хостах ролей — journalctl -u <юнит роли> -f.

СимптомВероятная причина
Установка роли останавливается сразуSSH: не тот пользователь, ключ без доступа, нет curl на хосте. Проверьте Test SSH
Роль active, состояние красноесервис установлен, но не поднялся: смотрите Host logs и journalctl на хосте
Сертификат не выпускаетсяхост недоступен из интернета на 80/443 или DNS-запись ещё не разошлась
Клиент не находит контур по почтенет s2.<домен> или нет файла /.well-known/simpletwo.json
Чат работает, звонки — нетне задан Client URL в Settings → Calls, либо у SFU нет своего TLS-фронта
Звонок соединяется, звука нетв node IP попал приватный адрес (см. §5) или заблокированы порты turn
Двое в одной встрече не видят друг другадва узла sfu без redis
Клиент не подключается к VPN-узлуне совпали shortId, публичный ключ или SNI; либо узел не отправляет heartbeat и признан устаревшим
Экраны календаря отсутствуют в клиентероль calendar не установлена — это ожидаемое поведение, а не ошибка