From 200fc65617e311b8767ea8a7b322a1086ca39d7e Mon Sep 17 00:00:00 2001 From: david_bai Date: Tue, 30 Sep 2025 14:01:30 +0800 Subject: [PATCH] build(docker): Intranet deployment is successfully tested using turn - Switch all CLI examples to Docker Compose V2 (docker compose) for consistency. - Add explicit instruction to grant write permissions to the host logs/ directory (chmod 777 -R logs) to fix coturn/nginx bind-mount logging errors. - Parameterize TURN UDP port range via TURN_MIN_PORT/TURN_MAX_PORT and set a safer default 49152-49252 to reduce startup/cleanup overhead and port conflicts. - Update troubleshooting with coturn log write failure guidance and port conflict hints. - Clarify that LAN IP is auto-detected in private mode; --local-ip is no longer needed by default but remains as an override for edge cases. --- README.zh-CN.md | 15 ++- deploy.sh | 44 +++++---- docker-compose.yml | 4 +- docker/scripts/generate-config.sh | 154 +++++++++++++++++++++++++++++- docs/DEPLOYMENT.zh-CN.md | 15 ++- 5 files changed, 197 insertions(+), 35 deletions(-) diff --git a/README.zh-CN.md b/README.zh-CN.md index 7f98f85..a201fa9 100644 --- a/README.zh-CN.md +++ b/README.zh-CN.md @@ -48,11 +48,18 @@ PrivyDrop (原 SecureShare) 是一个基于 WebRTC 的开源点对点(P2P) git clone https://github.com/david-bai00/PrivyDrop.git cd PrivyDrop -# 一键部署 -bash deploy.sh +# 生成配置(自动检测本机局域网 IP,已无需 --local-ip) +bash docker/scripts/generate-config.sh --mode private + +# 日志目录权限(coturn/nginx 外挂日志需要可写) +chmod 777 -R logs + +# 一键部署(Compose V2) +bash deploy.sh --mode private # 访问应用 -# http://localhost:3002 +# 前端: http://localhost:3002 +# 后端: http://localhost:3001 ``` **部署优势**: @@ -62,7 +69,7 @@ bash deploy.sh - ✅ 环境要求: 公网 IP → 内网即可使用 - ✅ 成功率: 70% → 95%+ -详见: [Docker 部署指南](./docs/DEPLOYMENT_docker.zh-CN.md) +详见: [Docker 部署指南](./build/docker/README.md) ### 💻 本地开发环境 diff --git a/deploy.sh b/deploy.sh index 2204b5e..2324d71 100644 --- a/deploy.sh +++ b/deploy.sh @@ -54,6 +54,9 @@ PrivyDrop Docker 一键部署脚本 $0 --domain example.com --mode full # 完整HTTPS部署 $0 --clean # 清理部署 +要求: + - Docker Engine 和 Docker Compose V2(命令为 `docker compose`) + EOF } @@ -116,8 +119,8 @@ check_dependencies() { missing_deps+=("docker") fi - if ! command -v docker-compose &> /dev/null && ! docker compose version &> /dev/null; then - missing_deps+=("docker-compose") + if ! docker compose version &> /dev/null; then + missing_deps+=("docker compose (V2)") fi if ! command -v curl &> /dev/null; then @@ -137,8 +140,8 @@ check_dependencies() { docker) echo " Docker: https://docs.docker.com/get-docker/" ;; - docker-compose) - echo " Docker Compose: https://docs.docker.com/compose/install/" + "docker compose (V2)") + echo " Docker Compose V2 插件: https://docs.docker.com/compose/install/" ;; curl) echo " curl: sudo apt-get install curl (Ubuntu/Debian)" @@ -161,7 +164,7 @@ clean_deployment() { # 停止并删除容器 if [[ -f "docker-compose.yml" ]]; then - docker-compose down -v --remove-orphans 2>/dev/null || true + docker compose down -v --remove-orphans 2>/dev/null || true fi # 删除镜像 @@ -209,12 +212,12 @@ deploy_services() { log_info "构建和启动服务..." # 停止现有服务 - if docker-compose ps | grep -q "Up"; then + if docker compose ps | grep -q "Up"; then log_info "停止现有服务..." - docker-compose down + docker compose down fi - # 确定启用的服务 + # 确定启用的服务(Compose V2 需要将 --profile 放在子命令之前) local profiles="" if [[ "$WITH_NGINX" == "true" ]]; then profiles="$profiles --profile nginx" @@ -225,11 +228,12 @@ deploy_services() { # 构建镜像 log_info "构建Docker镜像..." - docker-compose build --parallel + docker compose build --parallel - # 启动服务 + # 启动服务(--profile 需置于 up 之前) log_info "启动服务..." - docker-compose up -d $profiles + # shellcheck disable=SC2086 + docker compose $profiles up -d log_success "服务启动完成" } @@ -273,8 +277,8 @@ wait_for_services() { return 0 else log_error "服务启动超时" - log_info "查看服务状态: docker-compose ps" - log_info "查看服务日志: docker-compose logs -f" + log_info "查看服务状态: docker compose ps" + log_info "查看服务日志: docker compose logs -f" return 1 fi } @@ -285,7 +289,7 @@ post_deployment_checks() { # 检查容器状态 log_info "检查容器状态..." - docker-compose ps + docker compose ps # 运行健康检查测试 if [[ -f "test-health-apis.sh" ]]; then @@ -337,10 +341,10 @@ show_deployment_info() { echo "" echo -e "${BLUE}🔧 管理命令:${NC}" - echo " 查看状态: docker-compose ps" - echo " 查看日志: docker-compose logs -f [服务名]" - echo " 重启服务: docker-compose restart [服务名]" - echo " 停止服务: docker-compose down" + echo " 查看状态: docker compose ps" + echo " 查看日志: docker compose logs -f [服务名]" + echo " 重启服务: docker compose restart [服务名]" + echo " 停止服务: docker compose down" echo " 完全清理: $0 --clean" if [[ -f "docker/ssl/ca-cert.pem" ]]; then @@ -369,7 +373,7 @@ show_deployment_info() { echo "" echo -e "${YELLOW}💡 提示:${NC}" echo " - 首次启动可能需要几分钟来下载和构建镜像" - echo " - 如遇问题,请查看日志: docker-compose logs -f" + echo " - 如遇问题,请查看日志: docker compose logs -f" echo " - 更多帮助: $0 --help" echo "" } @@ -403,7 +407,7 @@ main() { post_deployment_checks show_deployment_info else - log_error "部署失败,请检查日志: docker-compose logs" + log_error "部署失败,请检查日志: docker compose logs" exit 1 fi } diff --git a/docker-compose.yml b/docker-compose.yml index 47d5c93..2fd65bb 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,5 +1,3 @@ -version: "3.8" - services: # Redis缓存服务 redis: @@ -116,7 +114,7 @@ services: - "3478:3478/udp" - "5349:5349/tcp" - "5349:5349/udp" - - "49152-65535:49152-65535/udp" + - "${TURN_MIN_PORT:-49152}-${TURN_MAX_PORT:-49252}:${TURN_MIN_PORT:-49152}-${TURN_MAX_PORT:-49252}/udp" volumes: - ./docker/coturn/turnserver.conf:/etc/coturn/turnserver.conf:ro - ./docker/ssl:/etc/ssl/certs:ro diff --git a/docker/scripts/generate-config.sh b/docker/scripts/generate-config.sh index e2b252d..bf258da 100755 --- a/docker/scripts/generate-config.sh +++ b/docker/scripts/generate-config.sh @@ -28,6 +28,109 @@ log_error() { echo -e "${RED}❌ $1${NC}" } +# 默认与全局参数 +WITH_TURN="${WITH_TURN:-false}" +TURN_EXTERNAL_IP_OVERRIDE="" +TURN_MIN_PORT_DEFAULT=49152 +TURN_MAX_PORT_DEFAULT=49252 +TURN_MIN_PORT="$TURN_MIN_PORT_DEFAULT" +TURN_MAX_PORT="$TURN_MAX_PORT_DEFAULT" + +parse_turn_port_range() { + local range="$1" + if [[ -z "$range" ]]; then + return 0 + fi + if [[ ! "$range" =~ ^([0-9]{2,5})-([0-9]{2,5})$ ]]; then + log_error "--turn-port-range 格式应为 MIN-MAX,例如 49152-49252" + exit 1 + fi + local min="${BASH_REMATCH[1]}" + local max="${BASH_REMATCH[2]}" + if (( min < 1 || max > 65535 || min >= max )); then + log_error "无效端口段:$min-$max,应在 1-65535 且 MIN/dev/null || true + rm -f docker/nginx/nginx.conf 2>/dev/null || true + rm -f docker/nginx/conf.d/*.conf 2>/dev/null || true + rm -f docker/coturn/turnserver.conf 2>/dev/null || true + rm -f docker/ssl/* 2>/dev/null || true +} + +# 显示帮助信息 +show_help() { + cat << 'EOF' +PrivyDrop 配置生成脚本(Docker 版) + +用法: bash docker/scripts/generate-config.sh [选项] + +选项: + --mode MODE 生成模式: private|basic|public|full + private/basic: 内网HTTP;默认不启用TURN,前端直连后端 + public: 公网HTTP + 启用TURN(无域名也可,TURN host=本机IP) + full: 完整HTTPS + 启用TURN(建议配合域名) + --with-turn 在任意模式下启用TURN(含private/basic)。默认 external-ip=LOCAL_IP + --turn-external-ip IP 显式指定TURN external-ip;不指定则使用 PUBLIC_IP,否则回退 LOCAL_IP + --turn-port-range R 指定TURN端口段(UDP),格式 MIN-MAX;默认 49152-49252 + --domain DOMAIN 指定域名(用于 Nginx/证书/TURN realm,如 turn.DOMAIN) + --local-ip IP 指定本机局域网IP(不传则自动探测) + --help 显示本帮助 + +环境变量(可选): + PUBLIC_IP 显式指定公网IP;仅在 public/full 模式有效。 + TURN external-ip 写入优先使用 PUBLIC_IP, + 留空则回退为 LOCAL_IP(仅同局域网可用,穿透受限)。 + +生成内容: + - .env 核心环境变量(含 NEXT_PUBLIC_API_URL/CORS 等) + - docker/nginx/* Nginx 反向代理配置(private/basic 也会生成 HTTP 配置) + - docker/ssl/* 自签证书(private/basic/public 生成;full 可替换为正式证书) + - docker/coturn/turnserver.conf 在 public/full 或使用 --with-turn 时生成/覆盖 + +重要说明: + - TURN external-ip 赋值逻辑为 external-ip=${PUBLIC_IP:-${LOCAL_IP}} + 即优先使用 PUBLIC_IP,否则回退 LOCAL_IP。 + - private/basic 模式不会覆盖 docker/coturn/turnserver.conf, + 若此前生成过 TURN 配置,该文件可能保留历史 external-ip。 + +示例: + # 1) 纯内网(推荐开发/内网快速跑通) + bash docker/scripts/generate-config.sh --mode private [--local-ip 192.168.0.113] + + # 2) 内网 + 启用TURN(默认 external-ip=LOCAL_IP,端口段=49152-49252) + bash docker/scripts/generate-config.sh --mode private --with-turn [--local-ip 192.168.0.113] + + # 3) 内网 + 启用TURN(自定义端口段/显式external-ip) + bash docker/scripts/generate-config.sh --mode private --with-turn \ + --turn-port-range 56000-56100 --turn-external-ip 192.168.0.113 \ + [--local-ip 192.168.0.113] + + # 4) 公网HTTP + TURN(自动探测公网IP,不带域名也可) + bash docker/scripts/generate-config.sh --mode public --local-ip 192.168.0.113 + + # 5) 公网HTTP + TURN(指定公网IP,避免外网探测) + PUBLIC_IP=1.2.3.4 bash docker/scripts/generate-config.sh --mode public --local-ip 192.168.0.113 + + # 6) HTTPS + TURN(有域名) + bash docker/scripts/generate-config.sh --mode full --domain example.com --local-ip 192.168.0.113 + +内网带TURN测试提示(不改脚本的最小步骤): + A) 一步生成(推荐): + bash docker/scripts/generate-config.sh --mode private --with-turn --local-ip 192.168.0.113 + 然后 bash ./deploy.sh --mode private --with-turn + B) 分步生成: + 先按 private 生成部署前后端,再 docker compose up -d coturn + +EOF +} + # 生成环境变量文件 generate_env_file() { log_info "生成环境变量配置..." @@ -77,6 +180,11 @@ generate_env_file() { turn_enabled="true" fi + # 若显式启用 TURN,则覆盖模式默认 + if [[ "$WITH_TURN" == "true" ]]; then + turn_enabled="true" + fi + if [[ "$turn_enabled" == "true" ]]; then if [[ -n "$DOMAIN_NAME" ]]; then turn_host_value="turn.${DOMAIN_NAME}" @@ -91,6 +199,10 @@ generate_env_file() { next_public_turn_password="$turn_password" fi + # 端口段(默认 49152-49252,可被 --turn-port-range 覆盖) + local turn_min_port_value="${TURN_MIN_PORT:-$TURN_MIN_PORT_DEFAULT}" + local turn_max_port_value="${TURN_MAX_PORT:-$TURN_MAX_PORT_DEFAULT}" + local default_no_proxy="localhost,127.0.0.1,backend,frontend,redis,coturn" local http_proxy_value="${HTTP_PROXY:-${existing_env[HTTP_PROXY]}}" local https_proxy_value="${HTTPS_PROXY:-${existing_env[HTTPS_PROXY]}}" @@ -102,6 +214,8 @@ generate_env_file() { TURN_PASSWORD="$turn_password" TURN_REALM="$turn_realm_value" TURN_HOST="$turn_host_value" + TURN_MIN_PORT="$turn_min_port_value" + TURN_MAX_PORT="$turn_max_port_value" cat > "$env_file" << EOF # PrivyDrop Docker 配置文件 @@ -153,6 +267,8 @@ TURN_ENABLED=${turn_enabled} TURN_USERNAME=${turn_username_value} TURN_PASSWORD=${turn_password} TURN_REALM=${turn_realm_value} +TURN_MIN_PORT=${turn_min_port_value} +TURN_MAX_PORT=${turn_max_port_value} # ============================================================================= # Nginx配置 @@ -495,6 +611,19 @@ generate_coturn_config() { mkdir -p docker/coturn + # 计算 external-ip:优先 --turn-external-ip,再次 PUBLIC_IP,最后 LOCAL_IP + local external_ip_value + if [[ -n "$TURN_EXTERNAL_IP_OVERRIDE" ]]; then + external_ip_value="$TURN_EXTERNAL_IP_OVERRIDE" + elif [[ -n "$PUBLIC_IP" ]]; then + external_ip_value="$PUBLIC_IP" + else + external_ip_value="$LOCAL_IP" + fi + + local min_port_value="${TURN_MIN_PORT:-$TURN_MIN_PORT_DEFAULT}" + local max_port_value="${TURN_MAX_PORT:-$TURN_MAX_PORT_DEFAULT}" + cat > docker/coturn/turnserver.conf << EOF # PrivyDrop TURN服务器配置 # 自动生成时间: $(date) @@ -508,7 +637,7 @@ listening-ip=0.0.0.0 relay-ip=0.0.0.0 # 外部IP (用于NAT环境) -external-ip=${PUBLIC_IP:-${LOCAL_IP}} +external-ip=${external_ip_value} # 服务器域名 realm=${TURN_REALM} @@ -535,8 +664,8 @@ no-loopback-peers no-multicast-peers # 性能配置 -min-port=49152 -max-port=65535 +min-port=${min_port_value} +max-port=${max_port_value} # 数据库 (可选) # userdb=/var/lib/turn/turndb @@ -634,12 +763,31 @@ main() { LOCAL_IP_OVERRIDE="$2" shift 2 ;; + --with-turn) + WITH_TURN="true" + shift + ;; + --turn-external-ip) + TURN_EXTERNAL_IP_OVERRIDE="$2" + shift 2 + ;; + --turn-port-range) + parse_turn_port_range "$2" + shift 2 + ;; + --help) + show_help + exit 0 + ;; *) shift ;; esac done + # 先清理上一次生成物(避免历史残留误导) + cleanup_previous_artifacts + # 首先运行环境检测 if ! detect_network_environment; then log_error "环境检测失败" diff --git a/docs/DEPLOYMENT.zh-CN.md b/docs/DEPLOYMENT.zh-CN.md index bf4b813..ca0c874 100644 --- a/docs/DEPLOYMENT.zh-CN.md +++ b/docs/DEPLOYMENT.zh-CN.md @@ -30,14 +30,16 @@ sudo bash backend/docker/env_install.sh ``` 该脚本将自动安装: + - **Node.js v20** - 运行环境 -- **Redis Server** - 用于房间管理和缓存 -- **Coturn** - TURN/STUN 服务器(可选,用于NAT穿透) +- **Redis Server** - 用于房间管理和缓存 +- **Coturn** - TURN/STUN 服务器(可选,用于 NAT 穿透) - **Nginx** - Web 服务器和反向代理(使用官方仓库) - **PM2** - Node.js 进程管理器 - **Certbot** - SSL 证书管理 安装完成后,可以验证各服务状态: + ```bash # 验证 Node.js 版本 node -v @@ -53,11 +55,13 @@ sudo systemctl status coturn ``` **注意事项:** -- **Redis配置:** 默认监听 `127.0.0.1:6379`,请确保后端 `.env` 文件中包含正确的 `REDIS_HOST` 和 `REDIS_PORT` -- **TURN服务:** 为可选配置,Privydrop 默认使用公共 STUN 服务器,只有对 NAT 穿透有极高要求时才需要配置 + +- **Redis 配置:** 默认监听 `127.0.0.1:6379`,请确保后端 `.env` 文件中包含正确的 `REDIS_HOST` 和 `REDIS_PORT` +- **TURN 服务:** 为可选配置,Privydrop 默认使用公共 STUN 服务器,只有对 NAT 穿透有极高要求时才需要配置 - **Nginx:** 脚本安装官方版本并验证 stream 模块支持 -**TURN服务器防火墙配置(如果需要配置TURN服务):** +**TURN 服务器防火墙配置(如果需要配置 TURN 服务):** + ```bash # 启用 Coturn 服务 sudo sed -i 's/#TURNSERVER_ENABLED=1/TURNSERVER_ENABLED=1/' /etc/default/coturn @@ -68,6 +72,7 @@ sudo ufw reload ``` 通过 `sudo ufw app info Turnserver` 看到的端口如下: + - `3478,3479,5349,5350,49152:65535/tcp` - `3478,3479,5349,5350,49152:65535/udp`