Files
x-server-mobile/docs/DEPLOYMENT.md
T
root 4ceb5eac7e chore: 重组项目文件结构,文档和部署文件归档
文件结构优化:
- docs/ 归档所有项目文档(PROJECT.md, PROJECT_ISSUES.md 等)
- deploy/ 归档所有部署相关文件(docker-compose.prod.yml, nginx 配置等)
- scripts/ 归档脚本文件(deploy.sh, rollback.sh)
- tests/ 归档测试文件(test_data.php)

更新引用路径:
- CLAUDE.md 更新文档路径引用
- 部署文档更新配置文件路径引用

清理文件:
- 移除非必要的测试文件(TestController, 测试脚本等)
- 移除重复/错误的目录

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-27 18:01:17 +08:00

599 lines
15 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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 <previous-commit>
# 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)
---
## 联系方式
如有问题,请联系开发团队。