Перейти к основному содержимому

Jitsi: k8s-топология и взаимодействие с бэкендом

Как саморазмещённый Jitsi разворачивается для PIN и как stateless .NET-сервис messenger с ним взаимодействует. Right-sized под целевую нагрузку (ADR-0056: 1–10k активных, конференции редки). Контракт домена/API остаётся нейтральным — здесь конкретика инфраструктуры Jitsi.

Набор компонентов

КомпонентРольТипРеплики (target)Заметки
prosodyXMPP-сервер: MUC-комнаты, presence, JWT-auth, reservationsstateful (in-memory rooms)1 (HA — 2, sticky)несёт кастомные PIN-модули (token/reservations/presence)
jicofofocus: создание конференций, выбор моста, оркестрация jibristateless1одна на кластер; управляет brewery JVB/jibri
JVB (SFU)jitsi-videobridge: маршрутизация медиаstateless1–2 (HPA по нагрузке)публичный UDP 10000; без Octo (octo.enabled=false)
jibriзапись сессии (file mode) → финализацияstateful (диск /mnt/recordings)0–2 (по числу параллельных записей)1 сессия на инстанс; SYS_ADMIN, headless Chrome
coturnTURN/STUN — обход NATstateless1–2use-auth-secret, TLS на 443
jigasiSIP-шлюз0 (будущее)не разворачиваем в MVP (телефонный вход/транскрипция — роадмап)

Веб-SPA jitsi-meet не разворачивается: фронт PIN — нативный клиент на lib-jitsi-meet (jitsi-client.md), не iframe. Разворачиваются только серверные компоненты + библиотека.

Порты и ingress

ПотокEndpointТранспорт
Клиент → prosody (сигналинг)/xmpp-websocket (secure WebSocket) за TLS-ingresswss://<MediaDomain>
Клиент → JVB (медиа)UDP 10000 (публичный, per-node)SRTP/ICE
Клиент → coturn (TURN relay)TCP/UDP 443 (use-auth-secret)TURN over TLS
messenger → prosodyне подключается (см. ниже)
prosody → messenger (reservation/presence)HTTP на api/internal/*HTTP + HMAC/mTLS
jibri → messenger (ingest записи)HTTP на api/internal/call-recordings/{sessionId}/ingestmultipart HTTP

SignalingUrl/MediaDomain из MediaJoinModel указывают на prosody WebSocket-endpoint и домен развёртывания; IceServers — на coturn.

Как взаимодействует stateless-бэкенд

messenger не участник XMPP-плоскости и не держит медиа-состояния. Его роль:

  1. Минтит JWT входа (jitsi-jwt.md) — HS256 на общем app_secret; клиент предъявляет его prosody при подключении к комнате. Токен короткоживущий, в БД не хранится (INV-CALL-6/INV-CONF-7).
  2. Принимает admission-вебхук mod_reservations (POST /internal/jitsi/reservations) — авторизует комнату по имени и возвращает конфиг (jitsi-rooms.md); DELETE …/{roomName} — финализация.
  3. Принимает presence-вебхук кастомного prosody-модуля (POST /internal/jitsi/events) — join/leave — авторитет ростера (call-participant.md INV-CPART-5).
  4. Принимает ingest записи от jibri finalize-скрипта (recording-pipeline.md).

Обратно к Jitsi бэкенд не ходит императивно (не создаёт комнаты по XMPP): комната поднимается, когда клиент с валидным JWT входит по имени, а admission решается reservation-вебхуком. Единственный императивный жест — старт/стоп записи инициируется клиентом-модератором через lib-jitsi-meet (jitsi-client.md), не бэкендом.

Env-матрица (перенос из Nomad-референса в Helm/k8s)

Мета-репозиторий Jitsi поставляет Nomad-IaC (thirdparty/jitsi/infra-provisioning/nomad/*) как референс конфигурации; PIN авторит собственные Helm/k8s-манифесты, перенося оттуда ключи. Ниже — полная env-матрица по компонентам (значения <domain> = MediaRoom.Domain = домен развёртывания медиа, напр. media.pin.ru). Секреты (*_SECRET, *_PASSWORD, static-auth-secret) — только из vault, не в манифесте.

КомпонентПеременнаяЗначение / источник
prosodyENABLE_AUTH1 — включить аутентификацию главного vhost
prosodyAUTH_TYPEjwtauthentication = "token" (см. mod_auth_token.lua)
prosodyJWT_APP_IDpin_messenger (== aud/iss токена, jitsi-jwt.md)
prosodyJWT_APP_SECRETобщий HS256-секрет (vault) — prosody проверяет подпись JWT входа
prosodyXMPP_DOMAIN<domain> — публичный домен, muc_mapper_domain_base
prosodyXMPP_AUTH_DOMAINauth.<domain> — vhost аутентифицированных сервисов (focus/jvb)
prosodyXMPP_MUC_DOMAINconference.<domain> — MUC-компонент комнат
prosodyXMPP_INTERNAL_MUC_DOMAINinternal.auth.<domain> — brewery-MUC для JVB/jibri
prosodyXMPP_RECORDER_DOMAINrecorder.<domain> — hidden-домен рекордера (jibri)
prosody(config) reservations_api_prefix<messenger>/api/internal/jitsi (jitsi-rooms.md)
prosodyports5222 (c2s, internal), 5280 (http/websocket, за ingress 443)
jicofoXMPP_AUTH_DOMAIN / JICOFO_AUTH_USER / JICOFO_AUTH_PASSWORDfocus-пользователь на auth.<domain> (vault пароль)
jicofo(config) jibri.brewery-jidjibribrewery@internal.auth.<domain> (reference.conf:305)
jicofo(config) conference.enable-moderator-checkstrue — запись/привилегии только модератору (reference.conf:219)
JVBJVB_AUTH_USER / JVB_AUTH_PASSWORDjvb-пользователь на auth.<domain> (vault пароль)
JVBJVB_PORT10000/udp — публичный медиапорт (per-node)
JVBDOCKER_HOST_ADDRESSпубличный IP ноды (кандидат ICE для клиента)
JVBJVB_OCTO_BIND_ADDRESSне задаём — octo.enabled=false (один регион, ADR-0056)
jibriENABLE_RECORDINGtrue при RECORDINGS_ENABLED (nomad/jibri.hcl:213)
jibriJIBRI_FINALIZE_RECORDING_SCRIPT_PATH/usr/bin/pin_finalize.sh (замена jitsi_uploader.sh, recording-pipeline.md)
jibriJIBRI_RECORDING_DIR/mnt/recordings (emptyDir/PV, nomad/jibri.hcl:214)
jibriJIBRI_USAGE_TIMEOUT61 minutes — авто-shutdown простаивающего (nomad/jibri.hcl:219)
jibriJIBRI_RECORDER_USER / JIBRI_RECORDER_PASSWORDрекордер на recorder.<domain> (vault пароль)
jibriJIBRI_XMPP_USER / JIBRI_XMPP_PASSWORDcontrol-пользователь на auth.<domain> (vault пароль)
jibriXMPP_RECORDER_DOMAIN / XMPP_INTERNAL_MUC_DOMAINrecorder.<domain> / internal.auth.<domain>
coturn(config) static-auth-secretобщий TURN-секрет (vault) — тем же секретом messenger минтит TURN-креды
coturn(config) realm<domain>
coturnports443/tcp+udp (TURN over TLS), 9641 (/metrics)

Полный инвентарь изменённых файлов — jitsi-modifications.md. YAML-манифесты развёртывания — jitsi-manifests.md.

prosody: main-vhost .cfg.lua (SKETCH)

Главный vhost авторится PIN (не берётся из образа as-is): включаем только нужные модули, точка входа в контракт messengerreservations_api_prefix + presence-модуль. Сокращённый набросок:

VirtualHost "media.pin.ru" -- = XMPP_DOMAIN = MediaRoom.Domain
authentication = "token" -- ENABLE_AUTH=1 / AUTH_TYPE=jwt
app_id = "pin_messenger" -- JWT_APP_ID (== aud/iss)
app_secret = os.getenv("JWT_APP_SECRET") -- из vault-смонтированного секрета
modules_enabled = {
"token_verification"; -- room-claim обязан совпадать с именем комнаты
"token_affiliation"; -- context.user.moderator/affiliation=owner → moderator
"reservations"; -- authoritative admission через messenger
"jibri_session"; -- обогащение app_data записи (session_id/initiator)
"pin_muc_presence"; -- НОВЫЙ: presence join/leave → messenger
}
muc_mapper_domain_base = "media.pin.ru"

-- authoritative admission (mod_reservations)
reservations_api_prefix = "https://messenger.pin.internal/api/internal/jitsi"
reservations_api_timeout = 20 -- секунд
reservations_api_retry_count = 3
reservations_api_headers = { -- аутентификация вебхука (HMAC/Bearer)
["Authorization"] = "Bearer "..os.getenv("JITSI_WEBHOOK_TOKEN");
}

-- presence-модуль (свой) читает тот же токен из env
pin_muc_presence_api_url = "https://messenger.pin.internal/api/internal/jitsi/events"

Отключаются неиспользуемые модули открытой поставки (breakout-rooms, lobby, av-moderation, polls). Формат reservations_api_* — штатные опции mod_reservations.lua; заголовок Authorization кладётся в каждый вызов (http_headers, строки 130–138 модуля).

jicofo: override-конфиг (SKETCH)

Значения задаются override-jicofo.conf/env поверх reference.conf (сам reference не правим):

jicofo {
jibri {
brewery-jid = "jibribrewery@internal.auth.media.pin.ru" # reference.conf:305
pending-timeout = 90 seconds
}
conference {
enable-moderator-checks = true # reference.conf:219
}
# transcription { url-template = ... } # НЕ задаём в MVP (transcription-roadmap.md)
}

Хранение и эксплуатация

  • jibri пишет на persistent volume /mnt/recordings; после успешного ingest finalize-скрипт удаляет локальную директорию (2xx от messenger). Диск — на время захвата+выгрузки, не долговременно.
  • prosody держит комнаты в памяти — при рестарте активные комнаты рвутся; клиент реконнектит по REST (persist-then-push не источник истины для медиа). HA — 2 реплики со sticky-сессиями.
  • Здоровье: jibri /jibri/api/v1.0/health; JVB/jicofo/coturn — /metrics (Prometheus). Алерты — нет свободного jibri при запросе записи (MSG_RECORDING_PROVISION_FAILED).

Секреты, сеть, health/readiness

  • Секреты — только vault. JWT_APP_SECRET, focus/JVB/jibri XMPP-пароли, static-auth-secret coturn, JITSI_WEBHOOK_TOKEN — монтируются из vault (External Secrets / CSI), не в ConfigMap и не в git. Тот же JWT_APP_SECRET держит и messenger (минт JWT входа), тот же static-auth-secret — для TURN-кред (jitsi-jwt.md).
  • Публичный UDP на JVB. UDP 10000 должен быть достижим клиентом снаружи: hostPort/hostNetwork или Service type=LoadBalancer c UDP; DOCKER_HOST_ADDRESS = публичный IP ноды (иначе ICE не соберёт кандидатов). coturn слушает публичный 443 для обхода строгого NAT (jitsi-modifications.md).
  • NetworkPolicy. Внутрь пускаем: клиент → prosody (ingress 443/wss), клиент → JVB (UDP 10000), клиент → coturn (443). Внутрикластерно: jicofo ↔ prosody (5222), JVB/jibri ↔ prosody (5222 internal-MUC). Egress prosody/jibri → messenger (reservation/presence/ingest) — единственные исходящие к бэкенду; всё прочее egress закрыто (self-host, disableThirdPartyRequests).
  • Health/readiness. prosody — c2s-порт + /http-bind liveness; jicofo/JVB — /about/health; jibri — /jibri/api/v1.0/health (readiness=idle перед приёмом записи); coturn — /metrics. Реадинес jibri влияет на доступность записи (нет idle-инстанса → MSG_RECORDING_PROVISION_FAILED).

Обратные ссылки

Автоссылки: где используется этот документ.

Связанные документы