c53ad95104
Deploy PROD / deploy (push) Failing after 0s
Zakres port_range 50000-50100 rezerwowal osobny port na kazde polaczenie. Test z 200 botami wszedl dokladnie w 49, a log serwera pokazal kandydata UDP tylko przy 52 polaczeniach i TCP przy wszystkich 204: kolejni uczestnicy dostawali juz tylko TCP. Zmienna UDP_PORT z zakresem nie wlaczala muxa, bo przegrywala z zakresem z pliku. Teraz udp_port: 7882 (sprawdzone lokalnie: rtc.portUDP Start 7882, End 0), nowa zmienna LIVEKIT_UDP_PORT, zeby stara LIVEKIT_UDP_RANGE w .env nie mogla przywrocic zakresu. Automatyczne wdrozenie nie dotyka profilu livekit: kontener trzeba przeladowac recznie. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
232 lines
8.6 KiB
YAML
232 lines
8.6 KiB
YAML
services:
|
|
postgres:
|
|
image: postgres:16
|
|
container_name: webinar-postgres
|
|
restart: unless-stopped
|
|
environment:
|
|
POSTGRES_DB: ${DB_NAME}
|
|
POSTGRES_USER: ${DB_USER}
|
|
POSTGRES_PASSWORD: ${DB_PASSWORD}
|
|
volumes:
|
|
- postgres-data:/var/lib/postgresql/data
|
|
ports:
|
|
- "${DB_PORT_EXPOSED:-5432}:5432"
|
|
healthcheck:
|
|
test: ["CMD-SHELL", "pg_isready -U ${DB_USER} -d ${DB_NAME}"]
|
|
interval: 5s
|
|
timeout: 5s
|
|
retries: 10
|
|
|
|
minio:
|
|
image: minio/minio:RELEASE.2024-12-18T13-15-44Z
|
|
container_name: webinar-minio
|
|
restart: unless-stopped
|
|
command: server /data --console-address ":9001"
|
|
environment:
|
|
MINIO_ROOT_USER: ${MINIO_ACCESS_KEY}
|
|
MINIO_ROOT_PASSWORD: ${MINIO_SECRET_KEY}
|
|
volumes:
|
|
- minio-data:/data
|
|
ports:
|
|
- "${MINIO_PORT_EXPOSED:-9000}:9000"
|
|
- "${MINIO_CONSOLE_PORT_EXPOSED:-9001}:9001"
|
|
healthcheck:
|
|
test: ["CMD", "mc", "ready", "local"]
|
|
interval: 5s
|
|
timeout: 5s
|
|
retries: 10
|
|
|
|
backend:
|
|
build:
|
|
context: ./backend
|
|
container_name: webinar-backend
|
|
restart: unless-stopped
|
|
depends_on:
|
|
postgres:
|
|
condition: service_healthy
|
|
minio:
|
|
condition: service_healthy
|
|
environment:
|
|
SERVER_PORT: 8080
|
|
DB_HOST: postgres
|
|
DB_PORT: 5432
|
|
DB_NAME: ${DB_NAME}
|
|
DB_USER: ${DB_USER}
|
|
DB_PASSWORD: ${DB_PASSWORD}
|
|
JWT_SECRET: ${JWT_SECRET}
|
|
JWT_EXPIRATION_MINUTES: ${JWT_EXPIRATION_MINUTES}
|
|
ADMIN_EMAIL: ${ADMIN_EMAIL}
|
|
ADMIN_PASSWORD: ${ADMIN_PASSWORD}
|
|
SWAGGER_ENABLED: ${SWAGGER_ENABLED}
|
|
MINIO_ENDPOINT: http://minio:9000
|
|
MINIO_ACCESS_KEY: ${MINIO_ACCESS_KEY}
|
|
MINIO_SECRET_KEY: ${MINIO_SECRET_KEY}
|
|
MINIO_BUCKET: ${MINIO_BUCKET}
|
|
MAIL_HOST: ${MAIL_HOST}
|
|
MAIL_PORT: ${MAIL_PORT}
|
|
MAIL_USER: ${MAIL_USER}
|
|
MAIL_PASSWORD: ${MAIL_PASSWORD}
|
|
MAIL_FROM: ${MAIL_FROM}
|
|
MAIL_SMTP_AUTH: ${MAIL_SMTP_AUTH}
|
|
MAIL_SMTP_STARTTLS: ${MAIL_SMTP_STARTTLS}
|
|
VIDEO_PROVIDER: ${VIDEO_PROVIDER}
|
|
# Adres serwera LiveKit widziany przez przegladarke uczestnika.
|
|
# Konfiguracja kluczy idzie przez panel, tak samo jak SMTP.
|
|
LIVEKIT_URL: ${LIVEKIT_URL:-}
|
|
CORS_ALLOWED_ORIGINS: ${CORS_ALLOWED_ORIGINS}
|
|
LOG_LEVEL_ROOT: ${LOG_LEVEL_ROOT}
|
|
LOG_LEVEL_APP: ${LOG_LEVEL_APP}
|
|
ports:
|
|
- "${BACKEND_PORT_EXPOSED:-8080}:8080"
|
|
|
|
frontend:
|
|
build:
|
|
context: ./frontend
|
|
container_name: webinar-frontend
|
|
restart: unless-stopped
|
|
depends_on:
|
|
- backend
|
|
ports:
|
|
- "${FRONTEND_PORT_EXPOSED:-8081}:80"
|
|
|
|
# --- profil livekit: wlasny serwer wideo ---
|
|
#
|
|
# DLACZEGO PROFIL, A NIE ZWYKLE USLUGI
|
|
# -------------------------------------------------------------------------
|
|
# Instancja w trybie NONE albo na Jitsi nie potrzebuje serwera wideo, a te
|
|
# dwa kontenery zajmuja pamiec i otwieraja porty. Profil sprawia, ze
|
|
# `docker compose up` ich nie dotyka: uruchamiaja sie wylacznie na zadanie,
|
|
# poleceniem `docker compose --profile livekit up -d`.
|
|
livekit:
|
|
# Wersja serwera musi nadazac za `livekit-client` we froncie.
|
|
#
|
|
# Klient 2.22 laczy sie sciezka `/rtc/v1`, ktorej serwer 1.8 nie zna:
|
|
# przegladarka dostaje 404 i komunikat "v1 RTC path not found. Consider
|
|
# upgrading your LiveKit server version". Sala wyglada wtedy na dzialajaca
|
|
# (uczestnik wchodzi, widzi liste obecnych), ale nie plynie ani obraz,
|
|
# ani dzwiek. Podnoszac `livekit-client`, sprawdz wersje serwera.
|
|
image: livekit/livekit-server:v1.13
|
|
container_name: webinar-livekit
|
|
profiles: ["livekit"]
|
|
restart: unless-stopped
|
|
command: --config /etc/livekit.yaml
|
|
volumes:
|
|
- ./livekit/livekit.yaml:/etc/livekit.yaml:ro
|
|
environment:
|
|
LIVEKIT_KEYS: "${LIVEKIT_API_KEY}: ${LIVEKIT_API_SECRET}"
|
|
# Jeden port UDP dla wszystkich uczestnikow (tryb mux), opis
|
|
# w livekit.yaml. Ma pierwszenstwo przed wartoscia z pliku, bo serwer nie
|
|
# rozwija zmiennych w pliku konfiguracyjnym. Instancja z zajetym portem
|
|
# zmienia wylacznie LIVEKIT_UDP_PORT w .env.
|
|
UDP_PORT: ${LIVEKIT_UDP_PORT:-7882}
|
|
# Adres rozglaszany uczestnikom. Przy uruchomieniu lokalnym ustaw
|
|
# NODE_IP=127.0.0.1 w .env: bez tego serwer rozglasza publiczny adres
|
|
# lacza, a przegladarka na tej samej maszynie nie zestawi polaczenia.
|
|
NODE_IP: ${LIVEKIT_NODE_IP:-}
|
|
ports:
|
|
# sygnalizacja WebSocket i API serwera
|
|
- "${LIVEKIT_PORT:-7880}:7880"
|
|
# RTC po TCP: droga zapasowa, gdy UDP jest zablokowane w sieci klienta
|
|
- "${LIVEKIT_TCP_PORT:-7881}:7881"
|
|
# media po UDP. To jest **glowna** droga ruchu audio i wideo:
|
|
# bez tego portu uczestnicy widza sie, ale sie nie slysza.
|
|
# Ten sam numer po obu stronach: UDP_PORT ustawia go wewnatrz kontenera,
|
|
# a serwer rozglasza uczestnikom wlasnie ten numer.
|
|
- "${LIVEKIT_UDP_PORT:-7882}:${LIVEKIT_UDP_PORT:-7882}/udp"
|
|
|
|
# TURN: przekazuje ruch, gdy polaczenie bezposrednie jest niemozliwe.
|
|
#
|
|
# Potrzebny uczestnikom za restrykcyjnym NAT-em albo firewallem firmowym,
|
|
# czyli w praktyce **wiekszosci uczestnikow szkolen korporacyjnych**.
|
|
# Bez TURN czesc osob dolaczy do pokoju i nie zobaczy ani nie uslyszy nikogo.
|
|
# Redis: kanal komunikacji miedzy serwerem LiveKit a Egress.
|
|
#
|
|
# Egress nie rozmawia z serwerem bezposrednio, tylko przez Redisa: serwer
|
|
# wrzuca tam zadanie nagrania, Egress je odbiera. **Bez Redisa serwer
|
|
# przyjmuje zadanie i nie ma go komu przekazac**, a nagrywanie konczy sie
|
|
# timeoutem przy poprawnie dzialajacym serwerze.
|
|
#
|
|
# W tym samym profilu co reszta wideo: instancja bez nagrywania nie ma
|
|
# powodu trzymac Redisa.
|
|
redis:
|
|
image: redis:7-alpine
|
|
container_name: webinar-redis
|
|
profiles: ["livekit"]
|
|
restart: unless-stopped
|
|
command: redis-server --save "" --appendonly no
|
|
ports:
|
|
# Wystawiony **wylacznie na petli zwrotnej** i tylko po to, zeby Egress
|
|
# w sieci hosta (uruchomienie lokalne) mial jak sie polaczyc. Na produkcji,
|
|
# gdzie Egress zostaje w sieci Dockera, te linie mozna usunac.
|
|
- "127.0.0.1:${REDIS_PORT_EXPOSED:-6379}:6379"
|
|
# Bez wolumenu i bez zapisu na dysk: to kolejka zadan na chwile, a nie
|
|
# magazyn. Utrata jej zawartosci przy restarcie nic nie kosztuje.
|
|
|
|
# Egress: nagrywa pokoj i zapisuje plik prosto do MinIO.
|
|
#
|
|
# DLACZEGO OSOBNY KONTENER
|
|
# -------------------------------------------------------------------------
|
|
# Egress uruchamia **prawdziwa przegladarke** (Chrome), wchodzi nia do pokoju
|
|
# i nagrywa to, co widzi. Stad wymagania nietypowe dla zwyklej uslugi:
|
|
# uprawnienie SYS_ADMIN dla piaskownicy Chrome i powiekszony /dev/shm, bo
|
|
# domyslne 64 MB nie wystarcza przegladarce i konczy sie jej wysypaniem
|
|
# w polowie nagrania.
|
|
livekit-egress:
|
|
# Przypiete do tej samej wersji co w produkcji. `latest` pobieralo
|
|
# nieprzewidywalny obraz, a niezgodnosc z serwerem LiveKit ujawnia sie
|
|
# dopiero przy probie nagrania.
|
|
image: livekit/egress:v1.9
|
|
container_name: webinar-livekit-egress
|
|
profiles: ["livekit"]
|
|
restart: unless-stopped
|
|
depends_on:
|
|
- livekit
|
|
- redis
|
|
environment:
|
|
# Konfiguracja idzie zmienna, a nie plikiem: Egress czyta ja w calosci
|
|
# z EGRESS_CONFIG_BODY, wiec nie ma czego montowac.
|
|
#
|
|
# `ws_url` i `redis` musza wskazywac adresy **w sieci Dockera**, a nie
|
|
# te widziane z przegladarki: to ruch miedzy uslugami.
|
|
EGRESS_CONFIG_BODY: |
|
|
redis:
|
|
address: ${EGRESS_REDIS_ADDR:-redis:6379}
|
|
api_key: ${LIVEKIT_API_KEY}
|
|
api_secret: ${LIVEKIT_API_SECRET}
|
|
ws_url: ${EGRESS_WS_URL:-ws://livekit:7880}
|
|
insecure: true
|
|
log_level: info
|
|
template_base: ${EGRESS_TEMPLATE_BASE:-http://frontend/}
|
|
cap_add:
|
|
# Piaskownica Chrome. Bez tego przegladarka nie wstaje i nagranie
|
|
# konczy sie bledem jeszcze przed wejsciem do pokoju.
|
|
- SYS_ADMIN
|
|
# Pamiec wspoldzielona przegladarki. Domyslne 64 MB wystarcza na kilka
|
|
# sekund nagrania, potem Chrome sie wysypuje.
|
|
shm_size: 1gb
|
|
|
|
coturn:
|
|
image: coturn/coturn:4.6
|
|
container_name: webinar-coturn
|
|
profiles: ["livekit"]
|
|
restart: unless-stopped
|
|
network_mode: host
|
|
command: >
|
|
-n
|
|
--realm=${TURN_REALM:-webinar.local}
|
|
--user=${TURN_USER:-webinar}:${TURN_PASSWORD}
|
|
--lt-cred-mech
|
|
--fingerprint
|
|
--no-cli
|
|
--no-tlsv1
|
|
--no-tlsv1_1
|
|
--min-port=${TURN_MIN_PORT:-49160}
|
|
--max-port=${TURN_MAX_PORT:-49200}
|
|
# network_mode: host, bo TURN musi widziec prawdziwe adresy IP:
|
|
# za NAT-em Dockera rozdawalby uczestnikom adresy wewnetrzne kontenera,
|
|
# pod ktore nikt z zewnatrz sie nie polaczy
|
|
|
|
volumes:
|
|
postgres-data:
|
|
minio-data:
|