# Linux 服务器部署方案

> **文档状态**: 初稿  
> **适用版本**: v1.0.0  
> **编写日期**: 2026-07-27  
> **目标系统**: CentOS 7+ / Ubuntu 20.04+ / 任何 Linux 发行版  

---

## 目录

1. [方案对比](#1-方案对比)
2. [方案一：Docker 容器化部署（推荐）](#2-方案一docker-容器化部署推荐)
3. [方案二：裸机部署（传统方式）](#3-方案二裸机部署传统方式)
4. [部署前改造清单](#4-部署前改造清单)
5. [CI/CD 集成](#5-cicd-集成)
6. [运维与监控](#6-运维与监控)
7. [常见问题](#7-常见问题)
8. [附录：脚本模板](#8-附录脚本模板)

---

## 1. 方案对比

| 维度 | Docker 部署（推荐） | 裸机部署 |
|------|-------------------|---------|
| **部署速度** | ⭐⭐⭐ 快（一键启动） | ⭐⭐ 中等（需手动安装依赖） |
| **环境隔离** | ⭐⭐⭐ 完全隔离 | ⭐ 依赖系统 Python/Node 版本 |
| **可移植性** | ⭐⭐⭐ 任意 Linux 一致运行 | ⭐⭐ 依赖发行版 |
| **Playwright 兼容性** | ⭐⭐⭐ 容器内安装 Chromium | ⭐⭐ 需系统级依赖库 |
| **维护成本** | ⭐⭐⭐ 低（镜像升级） | ⭐⭐ 需手动维护 |
| **资源占用** | ⭐⭐ 略高（容器层） | ⭐⭐⭐ 原生性能 |
| **适合场景** | 生产环境、团队协作 | 开发测试、临时验证 |

---

## 2. 方案一：Docker 容器化部署（推荐）

### 2.1 整体架构

```
┌─────────────────────────────────────────────────┐
│                    Linux 服务器                    │
│                                                   │
│  ┌──────────────┐   ┌──────────────────────────┐ │
│  │   Nginx       │   │    Docker Compose         │ │
│  │  (反向代理)    │──▶│                          │ │
│  │  :80 / :443   │   │  ┌────────┐ ┌────────┐  │ │
│  │  SSL 终止     │   │  │ Backend│ │Frontend│  │ │
│  │  WebSocket    │   │  │ :8001  │ │ :3000  │  │ │
│  │  代理         │   │  └───┬────┘ └────────┘  │ │
│  └──────────────┘   │      │                    │ │
│                     │  ┌───▼────┐               │ │
│                     │  │ SQLite │               │ │
│                     │  │(volume)│               │ │
│                     │  └────────┘               │ │
│                     └──────────────────────────┘ │
└─────────────────────────────────────────────────┘
```

### 2.2 目录结构

```
platform-auto-test/
├── deploy/
│   ├── docker-compose.yml         # 主编排文件
│   ├── Dockerfile.backend         # 后端镜像构建
│   ├── Dockerfile.frontend        # 前端镜像构建（Nginx 静态托管）
│   ├── nginx/
│   │   └── default.conf           # Nginx 站点配置
│   └── .env                       # 环境变量文件
├── backend/
│   └── ...                        # 后端源码（不变）
├── frontend/
│   └── ...                        # 前端源码（不变）
└── data/                          # 数据持久化（gitignored）
    ├── test_platform.db
    ├── screenshots/
    └── reports/
```

### 2.3 后端 Dockerfile

```dockerfile
# deploy/Dockerfile.backend
FROM python:3.10-slim

WORKDIR /app

# 安装系统依赖（Playwright 需要）
RUN apt-get update && apt-get install -y --no-install-recommends \
    wget \
    gnupg \
    libnss3 \
    libnspr4 \
    libatk1.0-0 \
    libatk-bridge2.0-0 \
    libcups2 \
    libdrm2 \
    libdbus-1-3 \
    libxkbcommon0 \
    libxcomposite1 \
    libxdamage1 \
    libxfixes3 \
    libxrandr2 \
    libgbm1 \
    libpango-1.0-0 \
    libcairo2 \
    libasound2 \
    && rm -rf /var/lib/apt/lists/*

# 安装 Python 依赖
COPY backend/requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt

# 安装 Playwright Chromium（Linux 版本）
RUN playwright install chromium

# 复制后端代码
COPY backend/ .

# 创建数据目录
RUN mkdir -p /app/data/screenshots /app/data/reports

# 暴露端口
EXPOSE 8001

# 启动命令
CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "8001"]
```

### 2.4 前端 Dockerfile（Nginx 静态托管）

```dockerfile
# deploy/Dockerfile.frontend
# ---- 构建阶段 ----
FROM node:20-alpine AS build

WORKDIR /app

COPY frontend/package.json frontend/package-lock.json ./
RUN npm ci

COPY frontend/ .
RUN npm run build

# ---- 运行阶段 ----
FROM nginx:alpine

# 复制构建产物
COPY --from=build /app/dist /usr/share/nginx/html

# 复制 Nginx 配置
COPY deploy/nginx/default.conf /etc/nginx/conf.d/default.conf

EXPOSE 80

CMD ["nginx", "-g", "daemon off;"]
```

### 2.5 Nginx 配置

```nginx
# deploy/nginx/default.conf
server {
    listen 80;
    server_name _;

    # 前端静态文件
    root /usr/share/nginx/html;
    index index.html;

    # 前端路由（SPA 支持）
    location / {
        try_files $uri $uri/ /index.html;
    }

    # API 反向代理到后端
    location /api/ {
        proxy_pass http://backend:8001;
        proxy_set_header Host $host;
        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_read_timeout 300s;
        proxy_send_timeout 300s;
    }

    # WebSocket 反向代理
    location /api/executions/ws/ {
        proxy_pass http://backend:8001;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
        proxy_set_header Host $host;
        proxy_read_timeout 86400s;
    }

    # 文件服务
    location /api/files/ {
        proxy_pass http://backend:8001;
        proxy_set_header Host $host;
    }

    # 静态资源缓存
    location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2)$ {
        expires 7d;
        add_header Cache-Control "public, immutable";
    }
}
```

### 2.6 Docker Compose 编排

```yaml
# deploy/docker-compose.yml
version: "3.8"

services:
  backend:
    build:
      context: ..
      dockerfile: deploy/Dockerfile.backend
    container_name: plat-auto-test-backend
    ports:
      - "8001:8001"
    volumes:
      - ../data:/app/data
    environment:
      - DATABASE_URL=sqlite+aiosqlite:///./data/test_platform.db
      - PLAYWRIGHT_HEADLESS=true
      - PLAYWRIGHT_TIMEOUT=30000
      - LOG_LEVEL=INFO
    restart: unless-stopped
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost:8001/health"]
      interval: 30s
      timeout: 10s
      retries: 3

  frontend:
    build:
      context: ..
      dockerfile: deploy/Dockerfile.frontend
    container_name: plat-auto-test-frontend
    ports:
      - "80:80"
    depends_on:
      backend:
        condition: service_healthy
    restart: unless-stopped
```

### 2.7 环境变量文件

```bash
# deploy/.env
# 数据库
DATABASE_URL=sqlite+aiosqlite:///./data/test_platform.db

# Playwright
PLAYWRIGHT_HEADLESS=true
PLAYWRIGHT_TIMEOUT=30000

# 日志
LOG_LEVEL=INFO

# 安全（生产环境建议修改）
SECRET_KEY=change-this-to-a-random-string
```

### 2.8 一键部署脚本

```bash
# deploy/deploy.sh
#!/bin/bash
set -e

echo "=== 平台自动化测试系统 - Docker 部署脚本 ==="

# 检查 Docker 是否安装
if ! command -v docker &> /dev/null; then
    echo "错误: Docker 未安装"
    echo "请先安装 Docker: https://docs.docker.com/engine/install/"
    exit 1
fi

# 检查 Docker Compose 是否安装
if ! command -v docker-compose &> /dev/null; then
    echo "错误: Docker Compose 未安装"
    echo "请先安装 Docker Compose: https://docs.docker.com/compose/install/"
    exit 1
fi

# 创建数据目录
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
PROJECT_DIR="$(dirname "$SCRIPT_DIR")"
mkdir -p "$PROJECT_DIR/data/screenshots" "$PROJECT_DIR/data/reports"

echo "📦 构建并启动服务..."
cd "$SCRIPT_DIR"
docker-compose up -d --build

echo "⏳ 等待服务就绪..."
sleep 10

# 健康检查
echo "🔍 检查后端健康状态..."
curl -s http://localhost:8001/health || echo "⚠️  后端未响应，请检查日志"

echo ""
echo "✅ 部署完成！"
echo "   前端: http://$(hostname -I | awk '{print $1}')"
echo "   API:  http://$(hostname -I | awk '{print $1}')/api"
echo "   Docs: http://$(hostname -I | awk '{print $1}')/docs"
echo ""
echo "📋 查看日志: docker-compose logs -f"
echo "🛑 停止服务: docker-compose down"
```

---

## 3. 方案二：裸机部署（传统方式）

### 3.1 基础环境准备

```bash
# ===== Ubuntu/Debian =====
sudo apt update && sudo apt upgrade -y
sudo apt install -y python3.10 python3.10-venv python3-pip \
    nodejs npm nginx curl

# ===== CentOS/RHEL =====
sudo yum install -y epel-release
sudo yum install -y python3.10 python3-pip nodejs nginx curl
```

### 3.2 安装 Playwright 系统依赖

```bash
# Playwright 在 Linux 上需要系统级库
# Ubuntu/Debian
sudo apt install -y \
    libnss3 libnspr4 libatk1.0-0 libatk-bridge2.0-0 \
    libcups2 libdrm2 libdbus-1-3 libxkbcommon0 \
    libxcomposite1 libxdamage1 libxfixes3 libxrandr2 \
    libgbm1 libpango-1.0-0 libcairo2 libasound2

# CentOS/RHEL
sudo yum install -y \
    nss nspr atk at-spi2-atk cups-libs libdrm \
    dbus-libs libxkbcommon libXcomposite libXdamage \
    libXfixes libXrandr mesa-libgbm pango cairo alsa-lib
```

### 3.3 后端部署

```bash
# 1. 克隆代码
git clone http://git.ubainsyun.com/bing/ubains-module-test.git
cd ubains-module-test
git checkout platform-auto-test

# 2. 创建 Python 虚拟环境
python3 -m venv venv
source venv/bin/activate

# 3. 安装依赖
cd backend
pip install -r requirements.txt

# 4. 安装 Playwright Chromium
playwright install chromium

# 5. 创建数据目录
mkdir -p data/screenshots data/reports

# 6. 配置 systemd 服务（见下方）
```

### 3.4 systemd 服务配置

```ini
# /etc/systemd/system/plat-auto-test-backend.service
[Unit]
Description=平台自动化测试系统 - 后端服务
After=network.target

[Service]
Type=simple
User=www-data
WorkingDirectory=/opt/ubains-module-test/backend
Environment="PATH=/opt/ubains-module-test/venv/bin"
Environment="PLAYWRIGHT_HEADLESS=true"
Environment="LOG_LEVEL=INFO"
ExecStart=/opt/ubains-module-test/venv/bin/uvicorn app.main:app --host 0.0.0.0 --port 8001 --workers 2
Restart=always
RestartSec=5
StandardOutput=journal
StandardError=journal

[Install]
WantedBy=multi-user.target
```

### 3.5 前端部署

```bash
# 1. 安装依赖并构建
cd frontend
npm ci
npm run build

# 2. 构建产物在 frontend/dist/ 目录
# 配置 Nginx 指向该目录（见下方）
```

### 3.6 Nginx 配置（裸机版）

```nginx
# /etc/nginx/sites-available/plat-auto-test
server {
    listen 80;
    server_name _;

    # 前端静态文件
    root /opt/ubains-module-test/frontend/dist;
    index index.html;

    # SPA 路由
    location / {
        try_files $uri $uri/ /index.html;
    }

    # API 反向代理
    location /api/ {
        proxy_pass http://127.0.0.1:8001;
        proxy_set_header Host $host;
        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_read_timeout 300s;
        proxy_send_timeout 300s;
    }

    # WebSocket 代理
    location /api/executions/ws/ {
        proxy_pass http://127.0.0.1:8001;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
        proxy_set_header Host $host;
        proxy_read_timeout 86400s;
    }

    # 文件服务
    location /api/files/ {
        proxy_pass http://127.0.0.1:8001;
        proxy_set_header Host $host;
    }

    # 静态资源缓存
    location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2)$ {
        expires 7d;
        add_header Cache-Control "public, immutable";
    }

    # 日志
    access_log /var/log/nginx/plat-auto-test-access.log;
    error_log  /var/log/nginx/plat-auto-test-error.log;
}
```

```bash
# 启用站点
sudo ln -sf /etc/nginx/sites-available/plat-auto-test /etc/nginx/sites-enabled/
sudo nginx -t
sudo systemctl reload nginx
```

---

## 4. 部署前改造清单

### 4.1 后端代码改造（Windows → Linux 兼容）

| 文件 | 改造内容 | 说明 |
|------|---------|------|
| `main.py` | 条件判断 `sys.platform == 'win32'` 不用改 | 已写好，Linux 自动跳过 ProactorEventLoop |
| `config.py` | 无需修改 | 环境变量配置已支持 |
| `database.py` | 无需修改 | SQLAlchemy 跨平台兼容 |
| `playwright_executor.py` | 无需修改 | 同步 API 在 Linux 上同样工作 |

**注意**：`config.py` 中 `DATABASE_URL` 默认使用相对路径 `./data/`，生产环境建议通过环境变量覆盖为绝对路径，如：
```bash
export DATABASE_URL="sqlite+aiosqlite:////opt/data/test_platform.db"
```

### 4.2 安全性加固

```python
# config.py 中的 CORS 配置
# 生产环境应改为明确的域名，而非 "*"
CORS_ORIGINS: list = os.getenv(
    "CORS_ORIGINS",
    "http://your-domain.com"
).split(",")
```

### 4.3 数据库迁移策略

**当前**：SQLite 文件数据库  
**生产建议**：考虑迁移到 PostgreSQL（如需高并发）

```bash
# 数据备份（迁移前）
cp data/test_platform.db data/test_platform.db.bak.$(date +%Y%m%d)
```

### 4.4 日志持久化

```yaml
# docker-compose 补充
services:
  backend:
    volumes:
      - ../data:/app/data
      - ../logs:/app/logs  # 日志持久化
```

---

## 5. CI/CD 集成

### 5.1 GitHub Actions 示例

```yaml
# .github/workflows/deploy.yml
name: Deploy to Linux Server

on:
  push:
    branches: [platform-auto-test]

jobs:
  build-and-deploy:
    runs-on: ubuntu-latest

    steps:
      - uses: actions/checkout@v4

      - name: Build Docker images
        run: |
          docker build -t plat-auto-test-backend -f deploy/Dockerfile.backend .
          docker build -t plat-auto-test-frontend -f deploy/Dockerfile.frontend .

      - name: Push to registry (可选)
        run: |
          docker tag plat-auto-test-backend ${{ secrets.REGISTRY }}/plat-auto-test-backend:latest
          docker push ${{ secrets.REGISTRY }}/plat-auto-test-backend:latest

      - name: Deploy to server
        uses: appleboy/ssh-action@v1.0.0
        with:
          host: ${{ secrets.SERVER_HOST }}
          username: ${{ secrets.SERVER_USER }}
          key: ${{ secrets.SERVER_SSH_KEY }}
          script: |
            cd /opt/ubains-module-test
            git pull
            cd deploy
            docker-compose pull
            docker-compose up -d --build
```

### 5.2 手动部署命令

```bash
# 推送代码到远程
git push origin platform-auto-test

# SSH 到服务器，拉取并部署
ssh user@your-server
cd /opt/ubains-module-test
git pull origin platform-auto-test
cd deploy
docker-compose up -d --build
```

---

## 6. 运维与监控

### 6.1 常用运维命令

```bash
# Docker 部署
docker-compose logs -f          # 查看实时日志
docker-compose logs backend     # 查看后端日志
docker-compose logs frontend    # 查看前端日志
docker-compose down             # 停止服务
docker-compose up -d            # 启动服务
docker-compose restart backend  # 重启后端
docker-compose restart frontend # 重启前端

# 数据备份
docker exec plat-auto-test-backend sh -c "cp /app/data/test_platform.db /app/data/backup_$(date +%Y%m%d).db"

# 进入容器
docker exec -it plat-auto-test-backend /bin/bash
```

### 6.2 健康检查

```bash
# 检查所有服务
curl http://localhost:8001/health
# 预期响应:
# {"status":"healthy","service":"平台自动化测试系统","version":"1.0.0","debug":false}

# 检查 Docker 容器状态
docker-compose ps

# 检查系统资源
docker stats
```

### 6.3 监控告警建议

| 指标 | 告警阈值 | 说明 |
|------|---------|------|
| CPU 使用率 | > 80% | Playwright 执行时 CPU 可能飙升 |
| 内存使用率 | > 80% | 每个浏览器实例约 200-500MB |
| 磁盘空间 | < 20% | 截图和报告会占用空间 |
| 服务宕机 | 任何不可用 | 自动重启 |

### 6.4 定期任务

```bash
# 数据清理脚本（建议 crontab 每周执行）
# 清理 30 天前的截图和报告
find /opt/ubains-module-test/data/screenshots -type f -mtime +30 -delete
find /opt/ubains-module-test/data/reports -type f -mtime +30 -delete

# 数据库备份（每天凌晨 3 点）
0 3 * * * cp /opt/ubains-module-test/data/test_platform.db /opt/backups/db_$(date +\%Y\%m\%d).db
```

---

## 7. 常见问题

### 7.1 Playwright 在 Linux 容器中运行失败

**现象**：`BrowserType.launch` 报错 `Missing libraries`  
**原因**：容器缺少 Chromium 系统依赖  
**解决**：Dockerfile 中已包含所有依赖库，确保 `apt-get install` 步骤完整

### 7.2 WebSocket 连接失败

**现象**：前端执行页面 WebSocket 无法连接  
**原因**：Nginx 未配置 WebSocket 升级头  
**解决**：确保 Nginx 配置包含：
```nginx
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
```

### 7.3 SQLite 数据库锁定

**现象**：`database is locked` 错误  
**原因**：多 worker 并发写入 SQLite  
**解决**：
- 方案 1：`uvicorn` 启动时只使用 `--workers 1`（SQLite 不推荐多 worker）
- 方案 2：迁移到 PostgreSQL（推荐生产环境）

### 7.4 前端 API 请求 404

**现象**：前端请求 `/api/api/cases` 双重前缀  
**原因**：Nginx 代理路径与前端 baseURL 叠加  
**解决**：前端 `baseURL` 保持空字符串，Nginx 中 `location /api/` 直接代理到后端

### 7.5 截图目录权限

**现象**：执行时截图保存失败  
**解决**：
```bash
# 确保 data 目录可写
chown -R 1000:1000 /opt/ubains-module-test/data
# 或直接开放权限
chmod -R 777 /opt/ubains-module-test/data
```

---

## 8. 附录：脚本模板

### 8.1 快速部署脚本（Docker 版）

```bash
# quick-deploy.sh
# 用法: curl -sSL https://your-server/quick-deploy.sh | bash
set -e

REPO_URL="http://git.ubainsyun.com/bing/ubains-module-test.git"
BRANCH="platform-auto-test"
INSTALL_DIR="/opt/ubains-module-test"

echo "🔄 克隆代码..."
git clone -b $BRANCH $REPO_URL $INSTALL_DIR

echo "📦 构建并启动..."
cd $INSTALL_DIR/deploy
docker-compose up -d --build

echo ""
echo "✅ 部署完成！"
echo "访问地址: http://$(curl -s ifconfig.me)"
```

### 8.2 数据库迁移到 PostgreSQL（可选）

```python
# backend/app/config.py
# 生产环境使用 PostgreSQL 只需设置环境变量：
# export DATABASE_URL="postgresql+asyncpg://user:password@localhost:5432/plat_auto_test"

# 需要在 requirements.txt 中增加：
# asyncpg==0.29.0
# psycopg2-binary==2.9.9
```

### 8.3 HTTPS 配置（Let's Encrypt）

```bash
# 安装 certbot
sudo apt install -y certbot python3-certbot-nginx

# 获取证书
sudo certbot --nginx -d your-domain.com

# 自动续期（certbot 默认自动添加 systemd timer）
sudo certbot renew --dry-run
```

---

## 部署路线图

| 阶段 | 内容 | 预计时间 |
|------|------|---------|
| **Phase 1** | 代码改造 + 提交 Dockerfile/Compose/Nginx 配置 | 1 天 |
| **Phase 2** | 服务器环境准备（Docker 安装、网络配置） | 0.5 天 |
| **Phase 3** | 部署测试 + 功能验证（执行用例验证） | 1 天 |
| **Phase 4** | HTTPS 配置 + 域名绑定 | 0.5 天 |
| **Phase 5** | CI/CD 接入 + 监控告警 | 1 天 |
| **Phase 6** | （可选）迁移 PostgreSQL | 2 天 |

---

*本文档由 Claude Code 生成，建议根据实际服务器环境（操作系统、网络策略、资源限制）调整参数。*