# X-Server Mobile 线上部署方案 ## 线上环境 - **面板**: 1Panel (Docker 管理) - **Web 服务器**: OpenResty - **数据库**: MySQL (容器) - **缓存**: Redis (容器) - **域名**: `xsm.hnxinyouhe.com` (待配置) --- ## 架构设计 ``` ┌─────────────────────────────────────┐ │ OpenResty (80/443) │ │ ┌─────────────────────────────┐ │ │ │ 静态资源托管 │ │ │ │ /frontend/dist/* │ │ │ │ SPA Router fallback │ │ │ └─────────────────────────────┘ │ │ ┌─────────────────────────────┐ │ │ │ API 反向代理 │ │ │ │ /api/* → localhost:8787 │ │ │ └─────────────────────────────┘ │ │ ┌─────────────────────────────┐ │ │ │ 飞书回调代理 │ │ │ │ /login/* → localhost:8787 │ │ │ └─────────────────────────────┘ │ └─────────────────────────────────────┘ │ ┌───────────────┼───────────────┐ ▼ ▼ ▼ ┌───────────┐ ┌───────────┐ ┌───────────┐ │ 后端 │ │ MySQL │ │ Redis │ │ :8787 │ │ :3306 │ │ :6379 │ │ Docker │ │ 1Panel │ │ 1Panel │ └───────────┘ └───────────┘ └───────────┘ ``` --- ## 部署步骤 ### 第一步:准备线上目录结构 ```bash # 在服务器上创建部署目录 mkdir -p /opt/x-server-mobile mkdir -p /opt/x-server-mobile/frontend mkdir -p /opt/x-server-mobile/backend mkdir -p /opt/x-server-mobile/logs mkdir -p /opt/x-server-mobile/backup ``` --- ### 第二步:前端部署 #### 2.1 本地构建 ```bash # 在开发环境 cd /www/x-server-mobile/frontend # 安装依赖(如果需要) npm install # 构建生产版本 npm run build # 生成的文件在 frontend/dist/ 目录 ``` #### 2.2 上传静态文件 ```bash # 方式1:使用 scp 上传 scp -r frontend/dist/* root@your-server:/opt/x-server-mobile/frontend/ # 方式2:使用 rsync 同步 rsync -avz --delete frontend/dist/ root@your-server:/opt/x-server-mobile/frontend/ ``` #### 2.3 构建环境配置 创建 `frontend/.env.production`: ```env # API 地址(使用相对路径,由 OpenResty 代理) VITE_API_BASE=/api/v1 # 或使用绝对地址(如果前后端分离部署) # VITE_API_BASE=https://api.xsm.hnxinyouhe.com ``` --- ### 第三步:后端部署 #### 3.1 创建生产环境 Docker Compose **文件**: `/opt/x-server-mobile/deploy/docker-compose.prod.yml` ```yaml services: backend: build: context: ./backend dockerfile: Dockerfile container_name: x-server-backend-prod restart: unless-stopped ports: - "8787:8787" environment: # 生产环境配置 - APP_ENV=production - APP_DEBUG=false # 数据库配置(使用 1Panel 的 MySQL) - DB_HOST=1Panel-mysql-xxxx # 替换为实际容器名 - DB_PORT=3306 - DB_DATABASE=xserver - DB_USERNAME=xserver # 建议创建专用数据库用户 - DB_PASSWORD=your_password # 强密码 # Redis 配置(使用 1Panel 的 Redis) - REDIS_HOST=1Panel-redis-xxxx # 替换为实际容器名 - REDIS_PORT=6379 - REDIS_PASSWORD=your_redis_password # 飞书配置 - FEISHU_APP_ID=cli_xxxxxxxxxxxxx - FEISHU_APP_SECRET=xxxxxxxxxxxxxxxxxxxx - FEISHU_ENCRYPT_KEY=xxxxxxxxxxxxxxxxxxxx - FEISHU_VERIFICATION_TOKEN=xxxxxxxxxxxxxxxxxxxx - FEISHU_TENANT_KEY=xxxxxxxxxxxxxxxxxxxx volumes: - ./backend:/app - ./logs/app:/app/runtime/logs networks: - 1panel-network healthcheck: test: ["CMD", "php", "-r", "exit(0);"] interval: 30s timeout: 10s retries: 3 start_period: 40s networks: 1panel-network: external: true ``` #### 3.2 创建生产环境后端配置 **后端 OPcache 配置** (`deploy/php-prod.ini`): ```ini # 生产环境 OPcache 配置 opcache.enable=1 opcache.memory_consumption=128 opcache.interned_strings_buffer=8 opcache.max_accelerated_files=10000 opcache.revalidate_freq=60 opcache.fast_shutdown=1 opcache.enable_cli=1 # 禁用错误显示 display_errors=0 display_startup_errors=0 log_errors=1 error_log=/app/runtime/logs/php_errors.log ``` #### 3.3 更新 Dockerfile ```dockerfile FROM php:8.5-cli # Install system dependencies RUN apt-get update && apt-get install -y \ unzip \ git \ libzip-dev \ libpng-dev \ curl \ && rm -rf /var/lib/apt/lists/* # Install PHP extensions RUN docker-php-ext-install zip pdo pdo_mysql bcmath pcntl opcache # Install Composer COPY --from=composer:latest /usr/bin/composer /usr/local/bin/composer # Set working directory WORKDIR /app # Copy composer files COPY composer.json composer.lock* ./ # Install dependencies RUN composer install --no-dev --optimize-autoloader --no-interaction # Copy application files COPY . . # Copy PHP configuration COPY php-prod.ini /usr/local/etc/php/conf.d/99-production.ini # Create runtime directories RUN mkdir -p /app/runtime/logs # Expose port EXPOSE 8787 # Start webman in foreground mode CMD ["php", "start.php", "start"] ``` --- ### 第四步:OpenResty 配置 #### 4.1 站点配置 **文件**: `/usr/local/openresty/nginx/conf/conf.d/x-server-mobile.conf` ```nginx # X-Server Mobile 线上环境配置 server { listen 80; server_name xsm.hnxinyouhe.com; # 强制 HTTPS return 301 https://$server_name$request_uri; } server { listen 443 ssl http2; server_name xsm.hnxinyouhe.com; # SSL 证书配置(使用 Let's Encrypt 或自签名) ssl_certificate /etc/nginx/ssl/xsm.hnxinyouhe.com.crt; ssl_certificate_key /etc/nginx/ssl/xsm.hnxinyouhe.com.key; # SSL 安全配置 ssl_protocols TLSv1.2 TLSv1.3; ssl_ciphers HIGH:!aNULL:!MD5; ssl_prefer_server_ciphers on; ssl_session_cache shared:SSL:10m; ssl_session_timeout 10m; # 日志 access_log /opt/x-server-mobile/logs/nginx_access.log; error_log /opt/x-server-mobile/logs/nginx_error.log; # Gzip 压缩 gzip on; gzip_vary on; gzip_min_length 1024; gzip_types text/plain text/css text/xml text/javascript application/json application/javascript application/xml+rss application/rss+xml font/truetype font/opentype application/vnd.ms-fontobject image/svg+xml; # 静态资源缓存 location ~* \.(js|css|png|jpg|jpeg|gif|webp|svg|ico|woff|woff2|ttf|eot)$ { root /opt/x-server-mobile/frontend; expires 7d; add_header Cache-Control "public, immutable"; } # 飞书回调(必须在 API 之前配置) location /login { proxy_pass http://127.0.0.1:8787; proxy_http_version 1.1; 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; } location /login/callback { proxy_pass http://127.0.0.1:8787; proxy_http_version 1.1; 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; } location /login/qrcode { proxy_pass http://127.0.0.1:8787; proxy_http_version 1.1; 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; } # 飞书卡片回调 location /feishu/card/callback { proxy_pass http://127.0.0.1:8787; proxy_http_version 1.1; 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; } # API 代理 location /api/ { proxy_pass http://127.0.0.1:8787; proxy_http_version 1.1; 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; proxy_set_header Host $host; # 超时配置 proxy_connect_timeout 60s; proxy_send_timeout 60s; proxy_read_timeout 60s; } # 前端 SPA(所有其他请求返回 index.html) location / { root /opt/x-server-mobile/frontend; try_files $uri $uri/ /index.html; add_header Cache-Control "no-cache, no-store, must-revalidate"; } } ``` #### 4.2 配置说明 1. **静态资源**: 直接由 OpenResty 托管,7天缓存 2. **API 请求**: 代理到后端 8787 端口 3. **飞书回调**: 特殊路径直接代理到后端 4. **SPA 路由**: 所有其他请求返回 index.html --- ### 第五步:数据库准备 #### 5.1 创建数据库和用户 ```sql -- 创建专用数据库用户 CREATE USER 'xserver'@'%' IDENTIFIED BY 'your_strong_password'; -- 授予权限 GRANT ALL PRIVILEGES ON xserver.* TO 'xserver'@'%'; FLUSH PRIVILEGES; -- 如果数据库不存在,创建并导入结构 CREATE DATABASE IF NOT EXISTS xserver CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; ``` #### 5.2 数据库迁移 ```bash # 从开发环境导出数据 docker exec 1Panel-mysql-409s mysqldump -uroot -pmysql_xEsdPa xserver > xserver_backup.sql # 导入到线上数据库 docker exec -i 1Panel-mysql-xxxx mysql -uxserver -pyour_password xserver < xserver_backup.sql ``` --- ### 第六步:SSL 证书配置 #### 6.1 使用 Let's Encrypt(推荐) ```bash # 安装 certbot apt-get install certbot # 获取证书 certbot certonly --standalone -d xsm.hnxinyouhe.com # 证书路径 # /etc/letsencrypt/live/xsm.hnxinyouhe.com/fullchain.pem # /etc/letsencrypt/live/xsm.hnxinyouhe.com/privkey.pem ``` #### 6.2 自动续期 ```bash # 添加 crontab 0 3 * * * certbot renew --quiet && nginx -s reload ``` --- ### 第七步:部署流程 #### 开发到生产部署 ```bash #!/bin/bash # deploy.sh - 部署脚本 set -e echo "=========================================" echo " X-Server Mobile 生产部署脚本" echo "=========================================" # 1. 备份当前版本 echo "📦 备份当前版本..." ssh root@your-server "mkdir -p /opt/x-server-mobile/backup/\$(date +%Y%m%d_%H%M%S)" ssh root@your-server "cp -r /opt/x-server-mobile/frontend /opt/x-server-mobile/backup/\$(date +%Y%m%d_%H%M%S)/" # 2. 构建前端 echo "🔨 构建前端..." cd frontend npm install npm run build # 3. 上传前端 echo "📤 上传前端..." rsync -avz --delete dist/ root@your-server:/opt/x-server-mobile/frontend/ # 4. 上传后端 echo "📤 上传后端..." rsync -avz --exclude='vendor' --exclude='runtime' \ ../backend/ root@your-server:/opt/x-server-mobile/backend/ # 5. 重启后端容器 echo "🔄 重启后端..." ssh root@your-server "cd /opt/x-server-mobile && docker-compose -f docker-compose.prod.yml restart backend" # 6. 重载 OpenResty echo "🔄 重载 OpenResty..." ssh root@your-server "openresty -s reload" echo "✅ 部署完成!" ``` --- ### 第八步:监控和日志 #### 8.1 日志位置 ```bash # Nginx 访问日志 /opt/x-server-mobile/logs/nginx_access.log # Nginx 错误日志 /opt/x-server-mobile/logs/nginx_error.log # 后端应用日志 docker logs x-server-backend-prod -f # 后端运行时日志 /opt/x-server-mobile/logs/app/ ``` #### 8.2 监控命令 ```bash # 检查后端容器状态 docker ps | grep x-server-backend-prod # 检查后端健康 curl http://127.0.0.1:8787/ # 检查数据库连接 docker exec x-server-backend-prod php -r "var_dump(getenv('DB_HOST'));" # 查看 PHP OPcache 状态 # 需要在后端添加 OPcache 状态页面 ``` --- ### 第九步:安全加固 #### 9.1 防火墙配置 ```bash # 只开放必要端口 ufw allow 22 # SSH ufw allow 80 # HTTP ufw allow 443 # HTTPS ufw enable ``` #### 9.2 PHP 安全配置 ```ini # php-prod.ini expose_php = Off allow_url_fopen = Off allow_url_include = Off disable_functions = exec,passthru,shell_exec,system,proc_open,popen ``` #### 9.3 环境变量保护 - 不要将敏感信息提交到代码仓库 - 使用 `.env` 文件或 Docker secrets 管理敏感信息 - 定期轮换密码和密钥 --- ### 第十步:回滚方案 #### 10.1 快速回滚 ```bash #!/bin/bash # rollback.sh - 回滚脚本 BACKUP_DATE=$1 # 格式: 20240227_150000 if [ -z "$BACKUP_DATE" ]; then echo "请指定备份日期,格式: 20240227_150000" exit 1 fi echo "🔄 回滚到 $BACKUP_DATE ..." # 恢复前端 cp -r /opt/x-server-mobile/backup/$BACKUP_DATE/frontend/* /opt/x-server-mobile/frontend/ # 恢复后端(可选) # cd /opt/x-server-mobile/backend # git checkout # docker-compose -f docker-compose.prod.yml restart backend # 重载 OpenResty openresty -s reload echo "✅ 回滚完成" ``` --- ## 检查清单 部署前检查: - [ ] 域名 DNS 已配置指向服务器 IP - [ ] SSL 证书已配置 - [ ] MySQL 数据库已创建 - [ ] Redis 可正常连接 - [ ] 飞书应用回调地址已更新 - [ ] 环境变量已正确配置 - [ ] 防火墙规则已设置 - [ ] 日志目录已创建 部署后验证: - [ ] 前端页面可正常访问 - [ ] API 接口返回正常 - [ ] 飞书登录功能正常 - [ ] 审批流程功能正常 - [ ] 日志输出正常 - [ ] 容器健康检查通过 --- ## 常见问题 ### Q1: 前端刷新 404 **原因**: OpenResty 配置没有正确处理 SPA 路由 **解决**: 确保 `try_files $uri $uri/ /index.html;` 配置正确 ### Q2: API 请求跨域 **原因**: 前端和后端域名不同 **解决**: 在后端添加 CORS 头,或使用 OpenResty 代理统一域名 ### Q3: 飞书回调失败 **原因**: 回调地址配置错误或 SSL 证书问题 **解决**: - 确认飞书后台配置的回调地址为 `https://xsm.hnxinyouhe.com/login/callback` - 确保服务器可公网访问 - 检查 SSL 证书是否有效 ### Q4: 容器无法连接数据库 **原因**: 容器网络不在同一个网络 **解决**: 确保后端容器和 MySQL/Redis 容器在同一个 Docker 网络(如 1panel-network) --- ## 联系方式 如有问题,请联系开发团队。