# LiveKit Docker 网络配置与 WebRTC ICE 连接验证

## 问题背景

Docker 容器与宿主机之间存在网络隔离，WebRTC 媒体流（UDP）无法穿透。

## 解决方案

### 正确的启动命令

```bash
docker run -d --name livekit-server \
  -p 7880:7880 \
  -p 7881:7881/tcp \
  -p 7882-7892:7882-7892/udp \
  -v ~/workspace/livekit-agents/config/livekit.yaml:/livekit.yaml:ro \
  livekit/livekit-server --config=/livekit.yaml --node-ip=192.168.31.229
```

**关键参数**：
- `--node-ip=192.168.31.229`：强制 LiveKit 向客户端广播 Mac 真实 IP
- `-p 7882-7892:7882-7892/udp`：UDP 端口映射（WebRTC 媒体流必需）

### 错误方案

| 方案 | 问题 | 原因 |
|------|------|------|
| `--network host` | 绑定失败 | Docker VM 内部没有 Mac 物理网卡 |
| `--node-ip=host.docker.internal` | ICE 候选者错误 | 解析为 `192.168.65.254`（Docker 网关），非 Mac IP |
| YAML 配置 `node_ip` | 被忽略 | LiveKit 优先使用 CLI 参数 |

## 验证方法

### 1. 检查服务端日志

```bash
docker logs livekit-server 2>&1 | grep -E "nodeIP|ICE|UDP"
```

期望输出：
```
nodeIP: 192.168.31.229
rtc.portUDP: {"Start":7882,"End":0}
```

### 2. 检查端口监听

```bash
lsof -i -P | grep -E "788[0-9]" | grep LISTEN
netstat -an | grep -E "788[0-9]"
```

期望：TCP 7880/7881 和 UDP 7882-7892 都在监听。

### 3. 运行客户端测试

```python
# test_client_v2.py
from livekit import api

token = api.AccessToken("devkey", "secret") \
    .with_identity("test-user") \
    .with_room_preset("publish-only") \
    .with_grants(api.VideoGrants(
        room="test-room",
        room_join=True,
        can_publish=True,
        can_subscribe=True,
    )) \
    .to_jwt()

livekit_api = api.LiveKitAPI(url, api_key="devkey", api_secret="secret")
room = await livekit_api.room.create_room(api.CreateRoomRequest(name="test-room"))
```

### 4. 验证 WebRTC ICE 连接

```python
# test_webrtc_ice.py
from livekit import api, rtc
from livekit.rtc import Room

room = Room()
await room.connect(url, token)

@room.on("connected")
def on_connected():
    print(f"✅ 成功连接: {room.name}")

@room.on("track_published")
def on_track_published(publication, participant):
    print(f"🎬 轨道已发布: {publication.track_sid}")
```

**服务器日志确认**：
```
added ICE candidate: 192.168.31.229:61663 (host)
ice connection state change: state: "connected"
selected ICE candidate pair: local=192.168.31.229:7882, remote=185.199.111...:20339
peer connection state: connected
data channel open: true
```

## API 注意事项

### LiveKitAPI 构造参数

```python
# ✅ 正确
api.LiveKitAPI(url, api_key="devkey", api_secret="secret")

# ❌ 错误
api.LiveKitAPI(url, api_key="devkey", secret_key="secret")  # 参数名错误
```

### Token 生成

```python
from livekit.api import AccessToken, VideoGrants

# ✅ 正确：使用 VideoGrants 配置权限
token = AccessToken("devkey", "secret") \
    .with_identity("user") \
    .with_grants(VideoGrants(room_join=True, room="room-name", can_publish=True)) \
    .to_jwt()

# ❌ 错误：with_room_preset 权限不足
token = AccessToken("devkey", "secret") \
    .with_room_preset("publish-only")  # 可能 401
```

## 性能指标

- **ICE 连接建立耗时**: ~121ms
- **UDP 端口范围**: 7882-7892（11个端口）
- **TCP 信号端口**: 7880（WebSocket）
- **选择协议**: UDP（优先于 TCP）

## 相关配置

```yaml
# config/livekit.yaml
log_level: info
development: true
keys:
  devkey: secret
rtc:
  udp_port: 7882-7892
  tcp_port: 7881
  use_external_ip: true
  stun_servers:
    - stun:stun.l.google.com:19302
```

**注意**: YAML 中的 `node_ip` 和 `ips.includes` 配置被忽略，必须通过 CLI 参数 `--node-ip` 设置。
