Files
x-server-mobile/docs/guides/deployment.md
T

599 lines
15 KiB
Markdown
Raw Normal View History

# 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)
---
## 联系方式
如有问题,请联系开发团队。