285 lines
10 KiB
YAML
285 lines
10 KiB
YAML
# Konfiguracja produkcyjna instancji klienta.
|
|
#
|
|
# Roznice wobec docker-compose.yml:
|
|
# - baza i MinIO nie maja wystawionych portow na zewnatrz, sa dostepne wylacznie
|
|
# w sieci Dockera. Dostep do bazy z zewnatrz przez tunel SSH, nie przez otwarty port.
|
|
# - frontend nasluchuje na 127.0.0.1, wiec ruch z internetu musi przejsc przez
|
|
# reverse proxy klienta, ktore konczy SSL.
|
|
# - restart: unless-stopped na kazdej usludze.
|
|
#
|
|
# - obrazy uslug zewnetrznych sa przypiete do wersji, zeby ponowne wdrozenie
|
|
# dalo ten sam stan co poprzednie.
|
|
#
|
|
# Uruchomienie:
|
|
# docker compose -f docker-compose.prod.yml --env-file .env.production up -d
|
|
#
|
|
# Z wlasnym serwerem wideo i nagrywaniem (cztery dodatkowe uslugi):
|
|
# docker compose -f docker-compose.prod.yml --env-file .env.production \
|
|
# --profile livekit up -d
|
|
#
|
|
# PRZED PIERWSZYM URUCHOMIENIEM: ./scripts/check-config.sh
|
|
|
|
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
|
|
# brak sekcji ports: baza nie jest wystawiona poza siec Dockera
|
|
healthcheck:
|
|
test: ["CMD-SHELL", "pg_isready -U ${DB_USER} -d ${DB_NAME}"]
|
|
interval: 10s
|
|
timeout: 5s
|
|
retries: 10
|
|
logging:
|
|
driver: json-file
|
|
options:
|
|
max-size: "10m"
|
|
max-file: "3"
|
|
|
|
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
|
|
# brak sekcji ports: konsola MinIO takze nie jest wystawiona.
|
|
# Dostep do konsoli przez tunel: ssh -L 9001:localhost:9001 user@serwer
|
|
# a nastepnie docker compose port minio 9001, albo tymczasowe przekierowanie.
|
|
healthcheck:
|
|
test: ["CMD", "mc", "ready", "local"]
|
|
interval: 10s
|
|
timeout: 5s
|
|
retries: 10
|
|
logging:
|
|
driver: json-file
|
|
options:
|
|
max-size: "10m"
|
|
max-file: "3"
|
|
|
|
backend:
|
|
build:
|
|
context: ./backend
|
|
image: webinar-backend:${APP_VERSION:-latest}
|
|
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:-120}
|
|
ADMIN_EMAIL: ${ADMIN_EMAIL}
|
|
ADMIN_PASSWORD: ${ADMIN_PASSWORD}
|
|
SWAGGER_ENABLED: ${SWAGGER_ENABLED:-false}
|
|
MINIO_ENDPOINT: http://minio:9000
|
|
MINIO_ACCESS_KEY: ${MINIO_ACCESS_KEY}
|
|
MINIO_SECRET_KEY: ${MINIO_SECRET_KEY}
|
|
MINIO_BUCKET: ${MINIO_BUCKET}
|
|
# Konfiguracji SMTP i bramki SMS nie przekazujemy przez srodowisko.
|
|
# Ustawia sie ja w panelu administracyjnym, w sekcji Ustawienia, a wartosci
|
|
# trafiaja do tabeli app_settings. Zmienne srodowiskowe mialyby nizszy
|
|
# priorytet, ale ich obecnosc tutaj myliloby co jest zrodlem prawdy.
|
|
VIDEO_PROVIDER: ${VIDEO_PROVIDER:-none}
|
|
CORS_ALLOWED_ORIGINS: ${CORS_ALLOWED_ORIGINS}
|
|
LOG_LEVEL_ROOT: ${LOG_LEVEL_ROOT:-INFO}
|
|
LOG_LEVEL_APP: ${LOG_LEVEL_APP:-INFO}
|
|
# brak sekcji ports: backend jest dostepny tylko przez frontend w sieci Dockera
|
|
#
|
|
# Healthcheck na wlasnym endpointcie /api/health. Bez niego `docker compose ps`
|
|
# pokazuje kontener jako dzialajacy takze wtedy, gdy aplikacja wstala, ale
|
|
# nie polaczyla sie z baza: proces zyje, a zadne zadanie nie przechodzi.
|
|
# `start_period` jest dlugi, bo pierwszy start wykonuje migracje Flyway.
|
|
healthcheck:
|
|
test: ["CMD-SHELL", "wget -qO- http://localhost:8080/api/health || exit 1"]
|
|
interval: 30s
|
|
timeout: 5s
|
|
retries: 3
|
|
start_period: 90s
|
|
logging:
|
|
driver: json-file
|
|
options:
|
|
max-size: "10m"
|
|
max-file: "5"
|
|
|
|
frontend:
|
|
build:
|
|
context: ./frontend
|
|
image: webinar-frontend:${APP_VERSION:-latest}
|
|
container_name: webinar-frontend
|
|
restart: unless-stopped
|
|
depends_on:
|
|
backend:
|
|
# czekamy na zdrowy backend, nie na sam uruchomiony proces: inaczej
|
|
# pierwsze wejscie na strone po wdrozeniu konczy sie bledem 502
|
|
condition: service_healthy
|
|
ports:
|
|
# Nasluch tylko na petli lokalnej. Reverse proxy klienta (nginx, Caddy,
|
|
# Traefik) konczy SSL i przekazuje ruch na ten port po HTTP.
|
|
- "127.0.0.1:${FRONTEND_PORT_EXPOSED:-8081}:80"
|
|
logging:
|
|
driver: json-file
|
|
options:
|
|
max-size: "10m"
|
|
max-file: "3"
|
|
|
|
# ==========================================================================
|
|
# PROFIL LIVEKIT: wlasny serwer wideo z nagrywaniem
|
|
# ==========================================================================
|
|
#
|
|
# Cztery uslugi, wylaczone domyslnie. Instancja w trybie NONE albo na Jitsi
|
|
# ich nie uruchamia i nie musi otwierac zadnych dodatkowych portow.
|
|
#
|
|
# docker compose -f docker-compose.prod.yml --env-file .env.production \
|
|
# --profile livekit up -d
|
|
#
|
|
# SAM PROFIL NIE WYSTARCZY: w panelu trzeba jeszcze ustawic VIDEO_PROVIDER
|
|
# na LIVEKIT oraz klucze API, inaczej aplikacja nie wie, ze serwer istnieje.
|
|
|
|
livekit:
|
|
# Wersja serwera musi nadazac za `livekit-client` we froncie. Klient 2.22
|
|
# laczy sie sciezka `/rtc/v1`, ktorej serwer 1.8 nie zna: sala wyglada na
|
|
# dzialajaca, ale nie plynie ani obraz, ani dzwiek.
|
|
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}"
|
|
UDP_PORT: ${LIVEKIT_UDP_RANGE:-50000-50100}
|
|
# NA PRODUKCJI: publiczny adres IP serwera. Bez niego uczestnicy wchodza
|
|
# do pokoju i sie nie slysza, bo serwer rozglasza adres nieosiagalny
|
|
# z internetu. Ustawienie `use_external_ip: true` w livekit.yaml
|
|
# NADPISUJE ta wartosc, wiec nie uzywac obu naraz.
|
|
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 audio i wideo. Bez otwarcia tego
|
|
# zakresu na firewallu serwera uczestnicy widza liste obecnych, ale
|
|
# sie nie slysza. Najczestsza przyczyna zgloszenia "wideo nie dziala".
|
|
- "${LIVEKIT_UDP_RANGE:-50000-50100}:${LIVEKIT_UDP_RANGE:-50000-50100}/udp"
|
|
logging:
|
|
driver: json-file
|
|
options:
|
|
max-size: "10m"
|
|
max-file: "3"
|
|
|
|
# Redis: kanal miedzy serwerem LiveKit a Egress.
|
|
#
|
|
# Serwer wrzuca tu zadanie nagrania, Egress je odbiera. BEZ REDISA serwer
|
|
# przyjmuje zadanie i nie ma go komu przekazac: nagrywanie konczy sie
|
|
# timeoutem przy poprawnie dzialajacym serwerze i dzialajacych spotkaniach.
|
|
redis:
|
|
image: redis:7-alpine
|
|
container_name: webinar-redis
|
|
profiles: ["livekit"]
|
|
restart: unless-stopped
|
|
command: redis-server --save "" --appendonly no
|
|
# Brak sekcji ports, inaczej niz w konfiguracji lokalnej: na produkcji
|
|
# Egress zostaje w sieci Dockera i laczy sie po nazwie uslugi, wiec
|
|
# wystawianie Redisa na host byloby otwarciem kolejki zadan bez potrzeby.
|
|
# Bez wolumenu: to kolejka na chwile, a nie magazyn.
|
|
logging:
|
|
driver: json-file
|
|
options:
|
|
max-size: "10m"
|
|
max-file: "3"
|
|
|
|
# Egress: nagrywa pokoj i zapisuje plik prosto do MinIO.
|
|
#
|
|
# Uruchamia PRAWDZIWA przegladarke (Chrome), wchodzi nia do pokoju i nagrywa
|
|
# to, co widzi. Stad wymagania nietypowe dla zwyklej uslugi: SYS_ADMIN dla
|
|
# piaskownicy Chrome i powiekszony /dev/shm, bo domyslne 64 MB konczy sie
|
|
# wysypaniem przegladarki w polowie nagrania.
|
|
livekit-egress:
|
|
# Wersja przypieta, nie `latest`: obraz Egress zmienia sie czesto,
|
|
# a niezgodnosc z serwerem 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:
|
|
# Egress czyta cala konfiguracje z tej zmiennej, wiec nie ma czego montowac.
|
|
# `ws_url` i `redis` wskazuja 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
|
|
shm_size: 1gb
|
|
logging:
|
|
driver: json-file
|
|
options:
|
|
max-size: "10m"
|
|
max-file: "3"
|
|
|
|
# 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.
|
|
coturn:
|
|
image: coturn/coturn:4.6
|
|
container_name: webinar-coturn
|
|
profiles: ["livekit"]
|
|
restart: unless-stopped
|
|
# 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.
|
|
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}
|
|
logging:
|
|
driver: json-file
|
|
options:
|
|
max-size: "10m"
|
|
max-file: "3"
|
|
|
|
volumes:
|
|
postgres-data:
|
|
minio-data:
|