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

Jitsi: k8s/Helm-манифесты (SKETCH)

YAML-наброски развёртывания медиа-компонентов для PIN. Вынесено из jitsi-deployment.md (там — топология, env-матрица, prosody/jicofo-конфиги, сеть/секреты), чтобы держать оба документа обозримыми. Это иллюстративные наброски, не production-чарт: показывают форму (реплики, порты, тома, права), а не полный values.yaml. Right-sized под ADR-0056 (1–10k активных, конференции редки). Секреты — из vault (External Secrets/CSI), в манифесте — только ссылки на secretKeyRef.

Обзор

КомпонентKindРепликиПубличная точкаОсобенности
prosodyDeployment + ConfigMap + Secret1 (HA 2, sticky)— (за ingress)кастомные модули, JWT-секрет
jicofoDeployment + ConfigMap1override jicofo.conf
JVBDeployment + Service(UDP) + HPA1–2UDP 10000 hostPort/LBDOCKER_HOST_ADDRESS = публичный IP
jibriDeployment0–2emptyDir /mnt/recordings, SYS_ADMIN, shm 2Gi
coturnDeployment + Service(LB)1–2443 tcp+udpstatic-auth-secret, TLS
ingressIngress (TLS 443)wss://<domain>/xmpp-websocket→ prosody 5280

prosody

Несёт кастомные PIN-модули (mod_pin_muc_presence.lua + vhost-конфиг) через ConfigMap; JWT/webhook-секреты — из Secret. Комнаты — in-memory, поэтому sticky-балансировка при HA.

apiVersion: apps/v1
kind: Deployment
metadata: { name: jitsi-prosody, namespace: media }
spec:
replicas: 1 # HA: 2 + sticky (по XMPP-сессии)
selector: { matchLabels: { app: jitsi-prosody } }
template:
metadata: { labels: { app: jitsi-prosody } }
spec:
containers:
- name: prosody
image: jitsi/prosody:stable # + слой с mod_pin_muc_presence.lua
ports: [ { containerPort: 5222 }, { containerPort: 5280 } ]
envFrom: [ { configMapRef: { name: jitsi-prosody-env } } ]
env:
- name: JWT_APP_SECRET
valueFrom: { secretKeyRef: { name: jitsi-secrets, key: jwt-app-secret } }
- name: JITSI_WEBHOOK_TOKEN
valueFrom: { secretKeyRef: { name: jitsi-secrets, key: webhook-token } }
volumeMounts:
- { name: pin-modules, mountPath: /prosody-plugins-custom } # mod_pin_muc_presence.lua
- { name: vhost-cfg, mountPath: /config/conf.d } # main-vhost .cfg.lua
readinessProbe: { tcpSocket: { port: 5222 }, initialDelaySeconds: 10 }
livenessProbe: { tcpSocket: { port: 5222 }, periodSeconds: 15 }
volumes:
- { name: pin-modules, configMap: { name: jitsi-prosody-modules } }
- { name: vhost-cfg, configMap: { name: jitsi-prosody-vhost } }
---
apiVersion: v1
kind: ConfigMap
metadata: { name: jitsi-prosody-env, namespace: media }
data: # см. env-матрицу в jitsi-deployment.md
ENABLE_AUTH: "1"
AUTH_TYPE: "jwt"
JWT_APP_ID: "pin_messenger"
XMPP_DOMAIN: "media.pin.ru"
XMPP_AUTH_DOMAIN: "auth.media.pin.ru"
XMPP_MUC_DOMAIN: "conference.media.pin.ru"
XMPP_INTERNAL_MUC_DOMAIN: "internal.auth.media.pin.ru"
XMPP_RECORDER_DOMAIN: "recorder.media.pin.ru"
---
apiVersion: v1
kind: Secret # смонтирован из vault; здесь — форма
metadata: { name: jitsi-secrets, namespace: media }
type: Opaque
stringData:
jwt-app-secret: "<vault>"
webhook-token: "<vault>"

jitsi-prosody-modules несёт mod_pin_muc_presence.lua, jitsi-prosody-vhost — main-vhost .cfg.lua (jitsi-deployment.md, sketch prosody). Ingress ниже терминирует TLS и проксирует websocket на 5280.

jicofo

Override-jicofo.conf (brewery-jid + moderator-checks) кладётся ConfigMap-ом; focus-пароль — из Secret.

apiVersion: apps/v1
kind: Deployment
metadata: { name: jitsi-jicofo, namespace: media }
spec:
replicas: 1 # одна на кластер
selector: { matchLabels: { app: jitsi-jicofo } }
template:
metadata: { labels: { app: jitsi-jicofo } }
spec:
containers:
- name: jicofo
image: jitsi/jicofo:stable
env:
- { name: XMPP_AUTH_DOMAIN, value: "auth.media.pin.ru" }
- { name: JICOFO_AUTH_USER, value: "focus" }
- name: JICOFO_AUTH_PASSWORD
valueFrom: { secretKeyRef: { name: jitsi-secrets, key: jicofo-auth-password } }
volumeMounts:
- { name: jicofo-override, mountPath: /config/jicofo.conf, subPath: jicofo.conf }
readinessProbe: { httpGet: { path: /about/health, port: 8888 } }
volumes:
- { name: jicofo-override, configMap: { name: jitsi-jicofo-conf } } # brewery-jid, enable-moderator-checks

JVB (SFU)

Публичный UDP 10000 — hostPort (или Service type=LoadBalancer c UDP). DOCKER_HOST_ADDRESS = публичный IP ноды (кандидат ICE). HPA по CPU — под редкие пики конференций.

apiVersion: apps/v1
kind: Deployment
metadata: { name: jitsi-jvb, namespace: media }
spec:
replicas: 1
selector: { matchLabels: { app: jitsi-jvb } }
template:
metadata: { labels: { app: jitsi-jvb } }
spec:
containers:
- name: jvb
image: jitsi/jvb:stable
ports:
- { name: media, containerPort: 10000, protocol: UDP, hostPort: 10000 }
env:
- { name: JVB_AUTH_USER, value: "jvb" }
- name: JVB_AUTH_PASSWORD
valueFrom: { secretKeyRef: { name: jitsi-secrets, key: jvb-auth-password } }
- name: DOCKER_HOST_ADDRESS # публичный IP ноды (ICE)
valueFrom: { fieldRef: { fieldPath: status.hostIP } } # либо явный external-IP
readinessProbe: { httpGet: { path: /about/health, port: 8080 } }
---
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata: { name: jitsi-jvb, namespace: media }
spec:
scaleTargetRef: { apiVersion: apps/v1, kind: Deployment, name: jitsi-jvb }
minReplicas: 1
maxReplicas: 2 # ADR-0056: конференции редки
metrics:
- type: Resource
resource: { name: cpu, target: { type: Utilization, averageUtilization: 70 } }

При hostPort UDP-порт per-node уникален — на одной ноде один JVB (или разные внешние порты). Альтернатива — Service type=LoadBalancer c protocol: UDP и externalTrafficPolicy: Local (сохранение source-IP для ICE).

jibri

Записывающий под: emptyDir под /mnt/recordings (запись эфемерна — после ingest удаляется), SYS_ADMIN для headless-Chrome, 2Gi shm. Реплики 0–2 по числу параллельных записей. Образ несёт pin_finalize.sh (jitsi-modifications.md).

apiVersion: apps/v1
kind: Deployment
metadata: { name: jitsi-jibri, namespace: media }
spec:
replicas: 1 # 0..2 (1 запись на инстанс)
selector: { matchLabels: { app: jitsi-jibri } }
template:
metadata: { labels: { app: jitsi-jibri } }
spec:
containers:
- name: jibri
image: pin/jibri:stable # jitsi/jibri + /usr/bin/pin_finalize.sh
securityContext: { capabilities: { add: [ "SYS_ADMIN" ] } }
env:
- { name: ENABLE_RECORDING, value: "true" }
- { name: JIBRI_FINALIZE_RECORDING_SCRIPT_PATH, value: "/usr/bin/pin_finalize.sh" }
- { name: JIBRI_RECORDING_DIR, value: "/mnt/recordings" }
- { name: JIBRI_USAGE_TIMEOUT, value: "61 minutes" }
- { name: XMPP_RECORDER_DOMAIN, value: "recorder.media.pin.ru" }
- { name: XMPP_INTERNAL_MUC_DOMAIN, value: "internal.auth.media.pin.ru" }
- name: JIBRI_RECORDER_PASSWORD
valueFrom: { secretKeyRef: { name: jitsi-secrets, key: jibri-recorder-password } }
- name: JIBRI_XMPP_PASSWORD
valueFrom: { secretKeyRef: { name: jitsi-secrets, key: jibri-xmpp-password } }
- name: JITSI_WEBHOOK_TOKEN # для pin_finalize.sh (ingest)
valueFrom: { secretKeyRef: { name: jitsi-secrets, key: webhook-token } }
volumeMounts:
- { name: recordings, mountPath: /mnt/recordings }
- { name: shm, mountPath: /dev/shm }
readinessProbe: { httpGet: { path: /jibri/api/v1.0/health, port: 2222 } }
volumes:
- { name: recordings, emptyDir: {} } # эфемерно (после ingest rm -rf)
- { name: shm, emptyDir: { medium: Memory, sizeLimit: 2Gi } }

emptyDir достаточно: pin_finalize.sh при 2xx от messenger удаляет директорию, долговременное хранилище — file storage (recording-pipeline.md). PV нужен лишь если требуется переживать рестарт пода при незавершённой выгрузке — вне MVP.

coturn

Публичный 443 (tcp+udp) через LoadBalancer; static-auth-secret и TLS-серт — из vault.

apiVersion: apps/v1
kind: Deployment
metadata: { name: jitsi-coturn, namespace: media }
spec:
replicas: 1
selector: { matchLabels: { app: jitsi-coturn } }
template:
metadata: { labels: { app: jitsi-coturn } }
spec:
containers:
- name: coturn
image: coturn/coturn:4.6.3
args: [ "-c", "/local/coturn.conf" ]
ports: [ { containerPort: 443, protocol: TCP }, { containerPort: 443, protocol: UDP } ]
volumeMounts:
- { name: coturn-conf, mountPath: /local/coturn.conf, subPath: coturn.conf }
- { name: tls, mountPath: /secrets, readOnly: true }
volumes:
- { name: coturn-conf, configMap: { name: jitsi-coturn-conf } } # realm, use-auth-secret
- { name: tls, secret: { secretName: jitsi-coturn-tls } }
---
apiVersion: v1
kind: Service
metadata: { name: jitsi-coturn, namespace: media }
spec:
type: LoadBalancer
externalTrafficPolicy: Local # сохранить source-IP
selector: { app: jitsi-coturn }
ports:
- { name: turns-tcp, port: 443, protocol: TCP }
- { name: turns-udp, port: 443, protocol: UDP }

coturn.conf — из nomad/coturn.hcl (denied-peer-ip, no-tlsv1*, static-auth-secret, realm); секрет и серты в манифесте лишь ссылками.

ingress

TLS-терминация 443 → prosody websocket (5280, /xmpp-websocket). Медиа (UDP 10000) и TURN (443) минуют ingress — идут напрямую на JVB/coturn.

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: jitsi-signaling
namespace: media
annotations:
nginx.ingress.kubernetes.io/proxy-read-timeout: "3600" # долгие wss
spec:
tls: [ { hosts: [ "media.pin.ru" ], secretName: jitsi-signaling-tls } ]
rules:
- host: media.pin.ru
http:
paths:
- path: /xmpp-websocket
pathType: Prefix
backend: { service: { name: jitsi-prosody, port: { number: 5280 } } }

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

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

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