docs: LiveKit 셀프호스팅(AWS EC2+도커 nginx) 구성 완성
- livekit/nginx.livekit.conf.example: 도커 nginx용 wss 리버스 프록시 참조 설정 추가 (docker DNS resolver+변수 proxy_pass로 부팅 순서 의존 제거, TLS 종단·WS 업그레이드) - DEPLOY.md: AWS EC2+도커 nginx 4-1장 추가, 참조 설정 파일로 연결 - .env.production.example: LiveKit 섹션을 셀프호스팅 우선으로 정리, 키 생성 원라이너 추가 애플리케이션 코드 변경 없음(백엔드 토큰 오프라인 서명·프론트 env 주입뿐). 운영은 selfhost-livekit 프로파일 + EC2 보안그룹/EIP 구성으로 가동. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
+12
-6
@@ -54,13 +54,19 @@ CORS_ORIGIN=http://localhost:5173
|
||||
AI_TIMEOUT_MS=60000
|
||||
|
||||
# ==========================================
|
||||
# 화상회의 (LiveKit) — 셀프호스팅 또는 Cloud
|
||||
# 화상회의 (LiveKit) — 셀프호스팅(기본) / Cloud(대안)
|
||||
# ==========================================
|
||||
# [셀프호스팅] livekit/DEPLOY.md 참고. selfhost-livekit 프로파일로 컨테이너 기동.
|
||||
# LIVEKIT_WS_URL : 브라우저 접속(리버스 프록시 TLS) → wss://livekit.<운영도메인>
|
||||
# LIVEKIT_URL : 백엔드 → LiveKit 내부 API(도커 네트워크) → http://livekit:7880
|
||||
# [Cloud] LIVEKIT_WS_URL/URL 을 wss://<project>.livekit.cloud 로, 키를 Cloud 값으로.
|
||||
# LIVEKIT_API_SECRET 은 충분히 강력하게(최소 32자).
|
||||
# [셀프호스팅] 운영 권장. 절차: livekit/DEPLOY.md (단일노드 3장, AWS EC2+도커 nginx 4-1장).
|
||||
# - selfhost-livekit 프로파일로 컨테이너 기동:
|
||||
# docker compose --profile selfhost-livekit --env-file .env.production up -d --build
|
||||
# - 리버스 프록시(wss 종단)는 livekit/nginx.livekit.conf.example 참고.
|
||||
# - 키 생성(절대 Cloud 키 재사용 금지):
|
||||
# LIVEKIT_API_KEY=$(openssl rand -hex 8)
|
||||
# LIVEKIT_API_SECRET=$(openssl rand -hex 32) # 최소 32자
|
||||
# - LIVEKIT_WS_URL : 브라우저 접속(리버스 프록시 TLS) → wss://livekit.<운영도메인>
|
||||
# - LIVEKIT_URL : 백엔드 → LiveKit 내부 API(도커 네트워크) → http://livekit:7880
|
||||
# [Cloud] 대안. LIVEKIT_WS_URL/URL 을 wss://<project>.livekit.cloud 로, 키를 Cloud 값으로
|
||||
# 채우고 selfhost-livekit 프로파일은 켜지 않는다.
|
||||
LIVEKIT_API_KEY=change-me-livekit-key
|
||||
LIVEKIT_API_SECRET=change-me-livekit-secret-min-32-characters
|
||||
LIVEKIT_WS_URL=wss://livekit.example.com
|
||||
|
||||
@@ -89,6 +89,86 @@ server {
|
||||
|
||||
---
|
||||
|
||||
## 4-1. AWS EC2 + 도커 nginx 구성 (실전 시나리오)
|
||||
|
||||
리버스 프록시가 **도커 컨테이너 nginx**이고 서버가 **AWS EC2** 인 경우의 구체 절차.
|
||||
compose 구조 변경 없이 nginx 설정과 EC2 네트워크만 맞추면 그대로 동작한다.
|
||||
|
||||
### (1) nginx 컨테이너를 `proxy` 네트워크에 연결
|
||||
|
||||
`livekit` 서비스는 이미 `app-network` + `proxy`(external)에 물려 있다(`docker-compose.yml`).
|
||||
따라서 **nginx 컨테이너를 같은 `proxy` 네트워크에 join** 시키기만 하면
|
||||
`proxy_pass http://livekit:7880` 이 도커 내장 DNS 로 해석된다.
|
||||
|
||||
> 실무 주의: nginx 부팅 시 `livekit` 업스트림을 즉시 해석하려다, livekit 컨테이너가
|
||||
> 늦게 뜨면 **nginx 가 부팅 실패**한다. 도커 내장 DNS resolver + 변수 방식으로
|
||||
> 해석을 런타임까지 지연시켜 부팅 순서 의존을 제거한다.
|
||||
|
||||
> 바로 쓸 수 있는 전체 설정 파일: **`livekit/nginx.livekit.conf.example`** (복사 후 도메인·인증서
|
||||
> 경로만 치환). 아래는 핵심 발췌.
|
||||
|
||||
```nginx
|
||||
server {
|
||||
listen 443 ssl;
|
||||
server_name livekit.<도메인>;
|
||||
# ssl_certificate / ssl_certificate_key ... (이 서브도메인용 인증서)
|
||||
|
||||
resolver 127.0.0.11 valid=30s; # 도커 내장 DNS (부팅 순서 의존 제거)
|
||||
location / {
|
||||
set $lk http://livekit:7880; # 변수로 받아 런타임 해석
|
||||
proxy_pass $lk;
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection "upgrade";
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Forwarded-Proto $scheme; # TLS 종단이므로 추가 권장
|
||||
proxy_read_timeout 600s;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
- `livekit.<도메인>` 서브도메인용 **인증서 별도 발급**(certbot 등).
|
||||
- 호스트 설치형 nginx(도커 밖)라면 컨테이너명 `livekit` 이 안 풀리므로, 7880 을
|
||||
`expose`→`ports` 로 publish 한 뒤 `127.0.0.1:7880` 으로 프록시해야 한다.
|
||||
|
||||
### (2) EC2 공인 IP 감지 — `use_external_ip: true` 가 정답
|
||||
|
||||
EC2 는 퍼블릭 IP 가 NIC 에 직접 붙지 않고 1:1 NAT 지만, LiveKit 의
|
||||
`use_external_ip: true` 는 **STUN 으로 외부 IP 를 탐지**하므로 EC2 에서 퍼블릭 IP 를
|
||||
정상적으로 잡는다. → `livekit.prod.yaml` 설정을 **그대로 둔다**(바꾸지 말 것).
|
||||
|
||||
확인 사항:
|
||||
|
||||
- **Elastic IP 부착** — 일반 퍼블릭 IP 는 재시작 시 바뀌므로 EIP 로 고정(DNS·ICE 후보 안정).
|
||||
- **보안 그룹 인바운드** (`0.0.0.0/0`):
|
||||
- `443/tcp` — 시그널링(nginx 경유)
|
||||
- `7881/tcp` — ICE/TCP 폴백
|
||||
- `7882/udp` — ICE/UDP 미디어
|
||||
- **아웃바운드 STUN 허용** — `use_external_ip` 가 STUN(UDP 3478)으로 조회한다.
|
||||
EC2 기본 SG 는 아웃바운드 전체 허용이라 보통 무관하나, egress 를 잠갔다면 UDP 3478 개방.
|
||||
- **STUN 실패 시 폴백** — 탐지가 실패할 때만 `rtc` 에 EIP 를 직접 박는다(평시 불필요):
|
||||
```yaml
|
||||
rtc:
|
||||
tcp_port: 7881
|
||||
udp_port: 7882
|
||||
use_external_ip: true
|
||||
# node_ip: <Elastic IP> # STUN 탐지 실패 시에만 백업으로 명시
|
||||
```
|
||||
|
||||
### (3) 가동 순서 요약
|
||||
|
||||
1. EIP 부착 + DNS `livekit.<도메인>` A 레코드 → EIP
|
||||
2. 보안 그룹: 443/tcp, 7881/tcp, 7882/udp 인바운드 개방
|
||||
3. nginx 컨테이너를 `proxy` 네트워크에 join + 위 server 블록 + 서브도메인 인증서
|
||||
4. `.env.production`: 키 신규 생성·교체(2장 참고), `LIVEKIT_WS_URL=wss://livekit.<도메인>`,
|
||||
`LIVEKIT_URL=http://livekit:7880`
|
||||
5. `docker compose --profile selfhost-livekit --env-file .env.production up -d --build`
|
||||
(프론트는 `VITE_LIVEKIT_URL` 이 빌드타임 주입이라 **재빌드 필수** — `--build` 로 충족)
|
||||
6. (권장) 호스트 `sysctl` 로 UDP 버퍼 상향(7장 참고)
|
||||
7. 서로 다른 두 네트워크/기기에서 입장 검증 → `./livekit/load-test.sh full`
|
||||
|
||||
---
|
||||
|
||||
## 5. 검증
|
||||
|
||||
1. 브라우저에서 회의 입장(두 기기/네트워크에서 영상 확인)
|
||||
|
||||
@@ -0,0 +1,74 @@
|
||||
# LiveKit 시그널링(wss) 리버스 프록시 — 도커 컨테이너 nginx 용 참조 설정
|
||||
#
|
||||
# 사용법:
|
||||
# 1) 이 파일을 운영 nginx 의 conf.d 에 복사: nginx.livekit.conf (.example 제거)
|
||||
# 2) <도메인> / 인증서 경로를 실제 값으로 치환
|
||||
# 3) nginx 컨테이너를 base compose 의 외부 `proxy` 네트워크에 join (아래 주석 참고)
|
||||
# 4) docker compose ... exec nginx nginx -s reload (또는 컨테이너 재기동)
|
||||
#
|
||||
# 전제(자세한 내용: livekit/DEPLOY.md 4·4-1장):
|
||||
# - LiveKit 컨테이너는 `selfhost-livekit` 프로파일로 기동되어 `proxy` 네트워크에 있다.
|
||||
# - nginx 컨테이너도 같은 `proxy` 네트워크에 있어야 `livekit` 이름이 해석된다.
|
||||
# (호스트 설치형 nginx 라면 docker-compose 의 livekit 7880 을 ports 로 publish 한 뒤
|
||||
# proxy_pass 를 http://127.0.0.1:7880 으로 바꾼다.)
|
||||
# - 미디어(RTC: 7881/tcp, 7882/udp)는 이 프록시를 거치지 않고 서버 공인 IP 로 직접 통신한다.
|
||||
|
||||
# HTTP → HTTPS 강제 리다이렉트 (Let's Encrypt 갱신 경로는 통과)
|
||||
server {
|
||||
listen 80;
|
||||
server_name livekit.<도메인>;
|
||||
|
||||
location /.well-known/acme-challenge/ {
|
||||
root /var/www/certbot;
|
||||
}
|
||||
location / {
|
||||
return 301 https://$host$request_uri;
|
||||
}
|
||||
}
|
||||
|
||||
# 시그널링(wss) 종단 → LiveKit 7880
|
||||
server {
|
||||
listen 443 ssl;
|
||||
http2 on;
|
||||
server_name livekit.<도메인>;
|
||||
|
||||
# 이 서브도메인용 인증서 (certbot 등으로 발급)
|
||||
ssl_certificate /etc/letsencrypt/live/livekit.<도메인>/fullchain.pem;
|
||||
ssl_certificate_key /etc/letsencrypt/live/livekit.<도메인>/privkey.pem;
|
||||
|
||||
# 도커 내장 DNS — `livekit` 업스트림을 런타임에 해석해 부팅 순서 의존을 제거한다.
|
||||
# (이 줄과 변수 proxy_pass 가 없으면 livekit 컨테이너가 늦게 뜰 때 nginx 가 부팅 실패한다.)
|
||||
resolver 127.0.0.11 valid=30s;
|
||||
|
||||
location / {
|
||||
set $lk http://livekit:7880; # 변수로 받아 런타임 해석
|
||||
proxy_pass $lk;
|
||||
|
||||
# WebSocket 업그레이드 (시그널링)
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection "upgrade";
|
||||
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme; # TLS 종단이므로 백엔드에 https 알림
|
||||
|
||||
# 장수명 WebSocket 유지
|
||||
proxy_read_timeout 600s;
|
||||
proxy_send_timeout 600s;
|
||||
}
|
||||
}
|
||||
|
||||
# ─────────────────────────────────────────────────────────────────────────────
|
||||
# nginx 컨테이너를 `proxy` 네트워크에 연결하는 예 (운영 nginx 의 compose 발췌):
|
||||
#
|
||||
# services:
|
||||
# nginx:
|
||||
# # ...
|
||||
# networks:
|
||||
# - proxy # base compose 의 external proxy 네트워크
|
||||
# networks:
|
||||
# proxy:
|
||||
# external: true
|
||||
# ─────────────────────────────────────────────────────────────────────────────
|
||||
Reference in New Issue
Block a user