Files
comrelay/livekit/DEPLOY.md
T
ttipo 3ec7e76f7c 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>
2026-06-25 11:30:32 +09:00

8.9 KiB

LiveKit 셀프호스팅 배포 가이드 (화상회의 단계 6)

운영에서 LiveKit(SFU)을 직접 호스팅하는 방법. 단일 노드 → 멀티 노드 클러스터 순으로 설명한다. (간단히 가려면 운영도 LiveKit Cloud 를 그대로 쓸 수 있다 — 그 경우 .env.productionLIVEKIT_* 만 Cloud 값으로 두고 아래 셀프호스팅 절차는 생략.)

로컬 dev 에서 셀프호스팅이 막혔던 건 WSL2/Docker Desktop 네트워킹 제약 때문이며, 공인 IP 가 있는 실서버에서는 발생하지 않는다.


1. 사전 준비

  • 공인 IP 를 가진 리눅스 서버(도커 설치)
  • 도메인 + DNS: livekit.<도메인> A 레코드 → 서버 공인 IP
  • 리버스 프록시(TLS 종단): 기존 proxy 외부 네트워크의 프록시(Traefik/nginx 등)
  • 방화벽 개방:
    • 443/tcp — 시그널링(wss, 프록시 경유)
    • 7881/tcp — ICE/TCP 폴백
    • 7882/udp — ICE/UDP(미디어)

2. 키 생성

API Key 는 임의 식별자, Secret 은 32자 이상 강력한 난수.

echo "LIVEKIT_API_KEY=$(openssl rand -hex 8)"
echo "LIVEKIT_API_SECRET=$(openssl rand -hex 32)"

.env.production 에 채운다:

LIVEKIT_API_KEY=<생성한 key>
LIVEKIT_API_SECRET=<생성한 secret(32자+)>
LIVEKIT_WS_URL=wss://livekit.<도메인>     # 브라우저 접속(프론트 빌드에 주입)
LIVEKIT_URL=http://livekit:7880            # 백엔드 → LiveKit 내부 API

LIVEKIT_WS_URL 은 프론트 빌드 시 VITE_LIVEKIT_URL굽혀진다. 값 변경 시 프론트 재빌드 필요.


3. 기동 (단일 노드)

docker compose --profile selfhost-livekit --env-file .env.production up -d --build
  • selfhost-livekit 프로파일을 켜야 LiveKit 컨테이너가 생성된다(미지정 시 미생성).
  • 설정: livekit/livekit.prod.yaml(키는 LIVEKIT_KEYS env 로 주입), use_external_ip: true 로 공인 IP 자동 광고.

4. 리버스 프록시 라우팅 (시그널링 wss)

wss://livekit.<도메인> → 컨테이너 livekit:7880 으로 라우팅(WebSocket 업그레이드 + TLS 종단). 미디어(7881/7882)는 프록시를 거치지 않는다 — 직접 공인 IP 로 통신.

Traefik(라벨) 예:

# docker-compose 의 livekit 서비스에 labels 추가(운영 프록시 구성에 맞게):
labels:
  - "traefik.enable=true"
  - "traefik.http.routers.livekit.rule=Host(`livekit.<도메인>`)"
  - "traefik.http.routers.livekit.entrypoints=websecure"
  - "traefik.http.routers.livekit.tls.certresolver=<resolver>"
  - "traefik.http.services.livekit.loadbalancer.server.port=7880"

nginx 예:

server {
  listen 443 ssl;
  server_name livekit.<도메인>;
  # ssl_certificate ... ;
  location / {
    proxy_pass http://livekit:7880;
    proxy_http_version 1.1;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection "upgrade";
    proxy_set_header Host $host;
    proxy_read_timeout 600s;
  }
}

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 (복사 후 도메인·인증서 경로만 치환). 아래는 핵심 발췌.

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 을 exposeports 로 publish 한 뒤 127.0.0.1:7880 으로 프록시해야 한다.

(2) EC2 공인 IP 감지 — use_external_ip: true 가 정답

EC2 는 퍼블릭 IP 가 NIC 에 직접 붙지 않고 1:1 NAT 지만, LiveKit 의 use_external_ip: trueSTUN 으로 외부 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 를 직접 박는다(평시 불필요):
    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. 브라우저에서 회의 입장(두 기기/네트워크에서 영상 확인)
  2. 부하 테스트:
    LIVEKIT_URL=wss://livekit.<도메인> \
    LIVEKIT_API_KEY=<key> LIVEKIT_API_SECRET=<secret> \
    ./livekit/load-test.sh full
    

6. 멀티 노드 클러스터링 (수평 확장)

대규모(예: 160명 다수 방)에서 노드 간 트래픽을 분산하려면 Redis 로 LiveKit 노드들을 묶는다.

  1. 설정에 redis 블록 추가 — 비밀번호가 들어가므로 커밋 파일이 아닌 사본에 둔다.

    cp livekit/livekit.prod.yaml livekit/livekit.prod.local.yaml   # gitignore 대상
    

    livekit.prod.local.yaml 에 추가:

    redis:
      address: redis:6379
      db: 1                       # 앱 캐시(db 0)와 분리
      password: <REDIS_PASSWORD>  # 루트 .env 의 REDIS_PASSWORD
    

    compose 의 livekit 볼륨을 이 사본으로 교체(override 파일 권장).

  2. 노드 확장

    • 단일 호스트에서 replica 만 늘리는 것은 진짜 분산이 아니다(같은 머신·NIC).
    • 실제 분산 = 여러 호스트 각각 LiveKit 실행 + 공유 Redis + 시그널링은 프록시가 여러 노드로 LB. RTC 는 각 노드의 공인 IP 로 직접.
    • LiveKit 이 방을 노드에 분배(room→node)하고, 한 방이 여러 노드에 걸치면 캐스케이딩으로 노드 간 미디어를 릴레이한다.
    • Simulcast/SVC 로 수신측 상황에 맞는 화질 레이어만 전달(기본 활성).
  3. 부하 분산 검증: ./livekit/load-test.sh full 로 다수 참여자를 투입하고, 노드별 참여자/대역폭 분포를 모니터링.


7. 운영 팁

  • UDP 수신 버퍼 상향(권장): sysctl -w net.core.rmem_max=16777216 net.core.wmem_max=16777216 (영구화는 /etc/sysctl.conf). 미설정 시 LiveKit 이 경고 로그를 남긴다.
  • 모니터링: LiveKit Prometheus 메트릭(/metrics) + Grafana — 노드별 참여자/트랙/CPU/대역폭.
  • 리소스: compose 의 deploy.resources.limits 를 트래픽에 맞게 조정(기본 2 CPU / 2GB).
  • .gitignorelivekit/*.local.yaml 추가(비밀번호 포함 사본 커밋 방지).