OmniFlow — версии платформы

Реестр образов registry.omniflow.ru

Текущая версия

версиядатасостояниечто в ней
0.76.02026-10-25текущаяValkey — обязательная часть платформы: при его недоступности ядро больше не ведёт звонки по копии в памяти, а… подробнее

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

Обновляясь, прочитайте в истории разделы «Важно при обновлении» у всех версий новее вашей: ручной шаг перешагнутой версии не отменяется тем, что вы её не ставили. Разделы своей и более старых версий не нужны.

Перед установкой

Нужен сервер с Linux, публичным статическим IP и установленным Docker (Engine 24+, плагин compose v2.20+; верхней границы нет — свежие Engine 29 / compose v5 подходят). Ставьте Docker из официального репозитория docker.com (пошагово — docs.docker.com/engine/install): в дистрибутивных пакетах часто нет плагина compose, без которого установка не пройдёт.

минимумрекомендуется
CPU2 ядра8 ядер
RAM4 ГБ без сценариев, 6 ГБ со сценариями16 ГБ
Диск30 ГБ SSD200 ГБ SSD — записи разговоров растут

Минимум — для небольшой организации. Первый запуск скачивает образы — меньше 2 ГБ по сети. Место под записи считайте так: минута разговора двух участников — около 2 МБ (по дорожке WAV на участника), то есть тысяча минут в месяц — около 2 ГБ.

Виртуальная машина: модель процессора и миниатюры в чате. Готовые бинарники обработки изображений внутри образа corenode требуют уровня инструкций x86-64-v2 (SSE4.2, POPCNT и соседи) — это нужно для миниатюр вложений в чате, но не для работы платформы. Любой серверный процессор с 2009 года его имеет, но гипервизоры по умолчанию отдают гостю урезанную модель (kvm64, qemu64, Common KVM processor), где эти флаги замаскированы.

Симптом до версии 0.65.5 был коварным — установка проходила, а узел молча крутился в перезапусках. Сейчас отказ локален: платформа работает, но у вложений в чате не будет миниатюр. Телефония, запись и маршрутизация не затронуты. В логах (./omniflow-ctl logs core-node) видно Unsupported CPU: Prebuilt binaries for Linux x64 require v2 microarchitecture.

Проверка на сервере — вывод должен быть непустым:
lscpu | grep -o -E 'sse4_2|popcnt' | sort -u

Пусто — смените модель CPU виртуальной машины на host (проброс реального процессора) и перезапустите её. В proxmox это Processors → Type → host, в libvirt/virt-manager — Copy host CPU configuration. Менять тип на работающей машине нельзя — нужен полный останов и старт, не reboot.

Таблица — не жёсткий вентиль: установщик по RAM/CPU не откажет, а doctor из ресурсов проверяет место на диске и сумму потолков памяти против памяти машины. Но ниже 4 ГБ RAM набор узлов (~15 контейнеров) упирается в OOM при запуске — установщик подгоняет потолки памяти под сервер и скажет прямо, если набор не помещается; на 4 ГБ помещается только набор без сценариев. На диске меньше 30 ГБ места под записи разговоров почти не остаётся. Проверка ресурсов сервера:

nproc          # ядра
free -g        # RAM, столбец available
df -h /        # свободное место
docker version          # Engine 24+
docker compose version  # v2.20+

# Разрешаем своему пользователю работать с Docker без sudo.
# Все команды — и установщик, и docker compose — обращаются к сокету
# /var/run/docker.sock, а он принадлежит группе docker. Не состоя в ней,
# вы получите "permission denied" на первой же команде.
sudo usermod -aG docker $USER

# Теперь выйдите из системы и зайдите заново (для ssh — переподключитесь).
# Состав групп берётся в момент входа, текущая сессия о новой группе не знает:
# без повторного входа команды продолжат падать, хотя настроено уже верно.
id                      # в списке групп должна появиться docker
Дальше — либо всё под своим пользователем, либо всё под sudo, но не вперемешку. У root свой домашний каталог, поэтому логин в реестр, сделанный под вашим пользователем, для sudo docker просто не существует — оттуда берётся no basic auth credentials. Вдобавок файлы инсталляции окажутся с разными владельцами, и обычные команды перестанут в них писать. Рекомендуемый вариант — обычный пользователь в группе docker.

Порты, которые должны быть свободны на сервере и открыты извне:

5060/udp,tcpSIP — телефоны и софтфоны
5080/udp,tcpSIP — транки провайдеров
5061/tcpSIP через TLS
8443/tcpSIP через WebSocket (звонки из браузера)
30000-30500/udpголосовой трафик
80/tcp, 443/tcpвеб-интерфейс
# Ничего не должно слушать эти порты до запуска платформы:
ss -lntup | grep -E ':(5060|5080|5061|8443|80|443) '

«Открыты извне» обеспечивает firewall провайдера: у облачных VM это security group в консоли облака, а не локальный ufw (на облачных образах он обычно выключен). Команда выше проверяет лишь локальную занятость портов; внешнюю доступность — проверкой снаружи.

Доступ к реестру образов

Реестр закрытый: логин и пароль выдаются вместе с поставкой. Без них не скачается ни установщик, ни сама платформа. Шаг делается один раз — Docker запоминает учётные данные в домашнем каталоге пользователя.

docker login registry.omniflow.ru
# успех выглядит как: Login Succeeded
# без терминала (ssh host '…', CI): echo "$PASS" | docker login registry.omniflow.ru -u "$USER" --password-stdin

# Проверяем, что доступ рабочий (маленьким образом (~8 МБ)):
docker pull registry.omniflow.ru/omniflow/alpine:3.20

Если проверка не прошла: unauthorized — неверный пароль или логин; no basic auth credentials — команда идёт под другим пользователем, чем docker login (обычно из-за sudo); denied — учётная запись есть, но прав на этот образ у неё нет; no such host или таймаут — сервер не резолвит имя реестра либо закрыт исходящий доступ на 443.

Установка

# Каталог инсталляции: сюда лягут конфиги, база, записи разговоров.
# Он и есть вся система — бэкап делается с него.
# В корне файловой системы папку создаёт только root, поэтому mkdir под sudo,
# а сразу за ним chown: каталог должен принадлежать ВАМ ещё до установщика.
# Он определяет владельца по каталогу и передаёт ему всё, что создал; каталог
# root'а означает .env и конфиги, которые вы не сможете править без sudo.
sudo mkdir -p /app/omniflow
sudo chown "$USER":"$USER" /app/omniflow
cd /app/omniflow

# CTL_HOST_DIR — чтобы установщик показывал ваши пути, а не свои внутренние:
# внутрь контейнера этот каталог виден как /install.
# ════════════════════ команда установки ════════════════════
docker run --rm -it --pull always -v "$PWD:/install" -e CTL_HOST_DIR="$PWD" \
  registry.omniflow.ru/omniflow/ctl:0.76.0 install
# ═══════════════════════════════════════════════════════════

# Установщик раскладывает установку папками-юнитами (infra/ core/ sbc-1/ cu-1/
# rtpe-1/ web/ …) и кладёт рядом скрипт ./omniflow-ctl — дальше все команды через него.
# В конце вывода он печатает пароль администратора панели — сохраните его.
# Он же лежит в .env строкой ADMIN_PASSWORD.

# Три строки, которые нужно проверить руками. По EXTERNAL_IP телефоны находят
# сервер, по PUBLIC_DOMAIN — браузер, SIP_DOMAIN — SIP-домен (тот же). Остальное установщик заполнил сам.
nano .env
#   EXTERNAL_IP=203.0.113.10          # публичный IP сервера
#   PUBLIC_DOMAIN=pbx.example.com     # домен панели; нет домена — пишите тот же IP
#   SIP_DOMAIN=pbx.example.com        # SIP-домен; тот же, что PUBLIC_DOMAIN (нет домена — тот же IP)
# За 1:1 NAT (облачная VM: внутренний адрес 10.x, публичный — снаружи) в EXTERNAL_IP
# пишите ПУБЛИЧНЫЙ адрес — этого достаточно: rtpengine анонсирует его в SDP, Kamailio —
# в Via/Contact, внутренний адрес платформе указывать не нужно. Публичный IP с сервера:
#   curl -s https://api.ipify.org

# Запуск. Юниты поднимаются по порядку с ожиданием готовности каждого.
# Первый раз скачиваются образы и создаётся схема базы — несколько минут.
# Оборвался на скачивании (502, timeout) — повторите ту же команду:
# докачает недостающее, уже скачанное не тронет.
./omniflow-ctl up

В терминале установщик задаёт четыре вопроса. Первый — состав компонентов: меню с номерами, где цифра переключает пункт, а Enter идёт дальше (буквы вроде y он не понимает — переспросит). Второй — показывает будущую структуру каталогов и спрашивает «Создать эту структуру? [Y/n]»: Enter означает «да», и только после этого на диске что-то появляется. Третий — внешний IP сервера: он его определяет сам и предлагает в скобках, Enter соглашается. Четвёртый — домен панели, и там же по умолчанию предложен этот самый адрес: домен нужен не всякой установке, по IP одинаково работают и панель, и телефония, и запись. Домен обязателен только для настоящего сертификата (letsencrypt/custom) и для чатов через внешних провайдеров — им нужен достижимый адрес вебхука. Повторный запуск ничего не затирает, а недостающее дополняет.

Если сессия оборвалась на вопросе, контейнер установщика остаётся ждать ввода: docker ps покажет его с образом omniflow/ctl, снимите его docker stop <имя> и запустите команду заново.

Без терминала (скрипт, ssh host 'команда', CI) — добавьте флаг --yes после install и уберите -t из docker run, иначе Docker откажет с the input device is not a TTY. Вопросов установщик задавать не будет: из компонентов поставит панель, внешний IP определит сам и его же подставит в PUBLIC_DOMAIN — установка без домена получается сразу рабочей. Домен, если он есть, вписывается в .env потом.

Сценарии обработки обращений (стратегии) установщик не ставит: они включаются на организацию, отдельной командой и позже — ./omniflow-ctl unit add strategies --tenant <тенант>, затем ./omniflow-ctl up strategies-<тенант>.

Сертификат по умолчанию самоподписанный (TLS_MODE=self-signed в .env): браузер предупредит о нём при любом значении PUBLIC_DOMAIN — и с доменом, и с IP. Настоящий сертификат — режимы letsencrypt / custom, для них нужен домен; описаны в INSTALL.md.

Управление установкой

Установка — набор папок-юнитов, каждая свой compose-проект. Управляет всеми сразу ./omniflow-ctl, а не docker compose в корне: корневого compose-файла нет, и docker compose up -d в корне ответит, что его не нашёл. С одним юнитом можно работать и обычным docker compose из его папки: cd cu-1 && docker compose ps.

./omniflow-ctl up                  # поднять всё по порядку, дождаться готовности
./omniflow-ctl up cu-1             # поднять один юнит (или несколько через пробел)
./omniflow-ctl ps                  # одна таблица по всем юнитам: состояние, health, аптайм
./omniflow-ctl logs core-node      # логи сервиса
./omniflow-ctl logs infra migrator # логи сервиса юнита — например, накат миграций
./omniflow-ctl restart sbc-1       # перезапустить юнит
./omniflow-ctl down                # остановить всё (спросит подтверждение; в скрипте — --yes)

Проверка результата

# Одна команда проверяет всё и печатает OK/FAIL по каждому пункту:
# контейнеры, образы из реестра, схема базы, конфиг SIP-фронта, панель,
# вход администратора, здоровье сервисов.
./omniflow-ctl verify

После первого ./omniflow-ctl up дайте платформе 2–3 минуты подняться, прежде чем запускать verify — узлы стартуют не мгновенно. Пункт «здоровье сервисов» может показать 503 в первые минуты после первого старта: платформа сама восстанавливает браузерный путь шины, и пункт зеленеет. Ускорить — разовым docker compose -f infra/docker-compose.yml restart nats, затем повторить verify.

Синтеза и распознавания речи в поставке нет. Речевой шлюз — отдельный компонент, его образа в реестре нет вовсе, поэтому озвучка, ASR и голосовые сценарии в стандартной установке не работают — ни до, ни после обновления. Не берите «сценарный звонок с озвучкой» за проверку установки: тишина в этом месте ожидаема и ничего не говорит о том, правильно ли всё поставлено. Проверяйте звонком между двумя SIP-точками — маршрутизация, медиа и запись от речевого шлюза не зависят.

Ручные команды — если нужно разобраться, что именно не сошлось:

# Все сервисы Up (где есть healthcheck — healthy). Одноразовые migrator
# и shared-tls завершаются сами: для них норма — Exited (0).
./omniflow-ctl ps

# Подробная проверка: порты, место на диске, память, контейнеры, схема базы,
# блокировки источников (и не ваш ли адрес среди них).
./omniflow-ctl doctor

# Панель отвечает (-k из-за самоподписанного сертификата): ожидаем 200.
curl -k -o /dev/null -w '%{http_code}\n' https://<PUBLIC_DOMAIN>/

# Вход администратора без браузера: ожидаем 200 и {"token":"..."};
# 401 — пароль не тот (берите из вывода установщика или ADMIN_PASSWORD в .env).
curl -k -s -w '\n%{http_code}\n' -H 'content-type: application/json' \
  -d '{"username":"admin","password":"<пароль>"}' \
  https://<PUBLIC_DOMAIN>/api/v1/auth/login

Если всё это сошлось, платформа установлена: дальше — вход в панель браузером под тем же admin и телефония по следующему разделу.

После установки

Работающий стек ещё никому не звонит: в системе нет ни одного телефона. В панели заведите SIP-точки — по одной на каждый аппарат или софтфон, подключите их к внешнему IP сервера (порт 5060) и позвоните с одной точки на другую: это разом проверяет весь тракт — SIP-фронт, медиа-движок, оркестрацию и запись. Пошагово — от установки до первого звонка; всё остальное — в INSTALL.md, он лежит рядом с инсталляцией.

Без браузера — то же самое из консоли. Сначала организация (тенант), её код потом станет SIP-доменом телефонов: ./omniflow-ctl tenant create romashka --name "Ромашка". Затем точки: ./omniflow-ctl sipuser add 101 --tenant romashka и ./omniflow-ctl sipuser add 102 --tenant romashka — команда печатает всё, что нужно вбить в софтфон; входящий номер на точку — ./omniflow-ctl route add --did <номер> --to sipuser:101; транк провайдера — ./omniflow-ctl trunk add …. Полный список команд и флагов — ./omniflow-ctl help, машинный каталог для скриптов — ./omniflow-ctl help --json.

Обновление

Версия платформы — одна строка в .env. Какие версии опубликованы — таблица на этой странице или ./omniflow-ctl versions.

# 0. дамп базы — миграции необратимы, откат версии образа схему не откатит
docker compose -f infra/docker-compose.yml exec -T postgres pg_dump -U postgres omniflow > backup-$(date +%F).sql

# 1. сменить версию в .env. Без номера команда покажет опубликованные версии
#    и предложит самую свежую; в скрипте без терминала нужен --yes, иначе
#    она откажет, ничего не изменив
./omniflow-ctl upgrade 0.76.0

# 2. обновить файлы установки до новой версии и проверить инсталляцию.
#    Установщик запустится уже новым образом и принесёт compose юнитов по
#    шаблонам версии, INSTALL.md, .env.example и самого себя
./omniflow-ctl doctor

# 3. применить: юниты пересоздаются по порядку, образы новой версии скачиваются сами
./omniflow-ctl up

# 4. проверка — в том числе что все образы пришли из реестра
./omniflow-ctl verify
Подсказка в конце upgrade в 0.67.0 устарела. Команда советует docker compose pull и docker compose up -d — это шаги прежней раскладки. В раскладке юнитов выполняйте шаги 2–3 из блока выше: ./omniflow-ctl doctor, затем ./omniflow-ctl up.

Первые секунд десять после up в логах нормальны строки уровня error — узлы находят друг друга не мгновенно: медиа-узел сообщает о разрыве с медиа-движком, SBC-узел — о пустом наборе медиа-прокси. Они снимаются сами за несколько секунд и остаются в логе как след. Тревогу поднимать стоит, если такая строка повторяется спустя минуту после подъёма.

Установка прежней раскладки

Если в корне установки лежит docker-compose.yml, а папки infra/ нет, — это прежняя раскладка (установки до 0.67.0). Она продолжает работать, ./omniflow-ctl понимает обе. Обновление в ней — прежними шагами: дамп docker compose exec -T postgres pg_dump …, ./omniflow-ctl upgrade, ./omniflow-ctl doctor, docker compose pull && docker compose up -d; остальные команды — Приложение Г в INSTALL.md. Перевести её в папки-юниты — две команды, переезд останавливает всё, поэтому дождитесь нуля разговоров:

./omniflow-ctl layout plan       # только показ: что куда уедет, размер, конфликты
./omniflow-ctl layout migrate    # исполнение: с подтверждением, журналом и откатом (--rollback)
Сторонние образы — из реестра. Базу, шину, хранилище и медиа-прокси установка берёт из registry.omniflow.ru с закреплённым digest. Если у вас прежняя раскладка с compose старых версий (дефолты на Docker Hub, который из России отдаёт образы нестабильно), добавьте в .env те же копии из реестра:
IMAGE_ACME=registry.omniflow.ru/omniflow/acme.sh:3.1.3
IMAGE_ALPINE=registry.omniflow.ru/omniflow/alpine:3.20
IMAGE_NATS=registry.omniflow.ru/omniflow/nats:2.11-alpine
IMAGE_NGINX=registry.omniflow.ru/omniflow/nginx:1.27-alpine
IMAGE_POSTGRES=registry.omniflow.ru/omniflow/postgres:18-alpine
IMAGE_S3STORE=registry.omniflow.ru/omniflow/seaweedfs:4.47
IMAGE_VALKEY=registry.omniflow.ru/omniflow/valkey:8-alpine

Резервное копирование

Бэкап установки — это .env, дамп базы и папки юнитов, кроме infra/data/postgres (его заменяет дамп) и */var/ (пересоздаётся само). Без .env бэкап бесполезен: пароли базы и ключи подписи записаны только в нём.

# дамп базы и отдельно её роли — без ролей дамп не восстановится на чистом сервере
docker compose -f infra/docker-compose.yml exec -T postgres pg_dump -U postgres omniflow > backup-$(date +%F).sql
docker compose -f infra/docker-compose.yml exec -T postgres pg_dumpall -U postgres --roles-only > roles-$(date +%F).sql

# файлы — на остановленной установке (записи разговоров, конфиги, стратегии)
./omniflow-ctl down --yes
sudo tar -czf omniflow-files-$(date +%F).tar.gz \
  --exclude='infra/data/postgres' --exclude='*/var' .env */
./omniflow-ctl up

Храните копию не на этом сервере. Восстановление идёт в строгом порядке — пустая база, роли, дамп, затем ./omniflow-ctl up; порядок и что будет, если его нарушить, — раздел «Резервное копирование и восстановление» в INSTALL.md.

Диагностика

./omniflow-ctl verify              # коротко: установлено или нет
./omniflow-ctl doctor              # подробно, с причинами и командами исправления
./omniflow-ctl logs <сервис> --tail 100
./omniflow-ctl ban list            # не заблокирован ли ваш адрес защитой от подбора паролей

Частые проблемы и как их решать — отдельная страница.

Состав поставки

acme.sh, alpine, asterisk, corenode, ctl, fail2ban, kamailio, nats, nginx, postgres, rtpe-agent, rtpengine, seaweedfs, skopeo, strategy-ai-reception, strategy-chat-queue, strategy-dial-by-code, strategy-echo, strategy-mibs-main, strategy-not-work, strategy-operator-status, strategy-pay-select, strategy-queue-operator, strategy-reminder-dialer, strategy-simple-main, strategy-speech-demo, strategy-tcardio-main, strategy-transcribe-batch, strategy-vq, syft, ui, valkey