# 运维常用命令

> 服务地址：`http://<内网IP>:5019`

## 1. 服务管理

```bash
# 启动所有服务
docker compose up -d

# 停止所有服务
docker compose down

# 重启所有服务
docker compose restart

# 重启单个服务
docker compose restart app
docker compose restart queue
docker compose restart pdf-worker

# 查看运行状态
docker compose ps

# 查看进程状态（nginx + php-fpm）
docker exec math_cms_app ps aux | grep -E "nginx|php-fpm"
```

## 2. 日志查看

### Laravel 应用日志

```bash
# 查看最近日志
docker exec math_cms_app tail -100 storage/logs/laravel.log

# 只看 ERROR 级别
docker exec math_cms_app grep "\.ERROR" storage/logs/laravel.log | tail -50

# 实时监控日志
docker exec math_cms_app tail -f storage/logs/laravel.log

# 清空日志
docker exec math_cms_app truncate -s 0 storage/logs/laravel.log

# 组合：先显示最近 200 行，然后实时监控
docker exec math_cms_app tail -200f storage/logs/laravel.log
```

### Nginx 日志

```bash
# 访问日志（看请求状态码）
docker exec math_cms_app tail -100 /var/log/nginx/access.log

# 错误日志
docker exec math_cms_app tail -50 /var/log/nginx/error.log

# 只看 5xx 错误的请求
docker exec math_cms_app grep '" 5[0-9][0-9] ' /var/log/nginx/access.log | tail -30
```

### PHP-FPM 日志

```bash
# 慢请求日志（超过5秒的请求）
docker exec math_cms_app cat /var/log/php-fpm-slow.log 2>/dev/null || echo "暂无慢请求"
```

### 队列日志

```bash
# 查看队列 Worker 日志
docker logs math_cms_queue --tail=50
docker logs math_cms_pdf --tail=50

# 实时监控 PDF 队列
docker logs -f math_cms_pdf
```

### 一键查看错误

```bash
docker exec math_cms_app sh -c "echo '=== Laravel Errors ===' && grep '\.ERROR' storage/logs/laravel.log | tail -10 && echo '=== Nginx 5xx ===' && grep '\" 5[0-9][0-9] ' /var/log/nginx/access.log | tail -5"
```

## 3. 代码更新部署

```bash
# 1. 拉取最新代码
git pull

# 2. 重新构建并启动
docker compose down
docker compose build --no-cache
docker compose up -d

# 或一键操作（增量构建）
docker compose up -d --build

# 3. 热更新（不重新构建，只复制代码）
docker cp app/ math_cms_app:/app/
docker cp app/ math_cms_queue:/app/
docker cp app/ math_cms_pdf:/app/
docker compose restart app queue pdf-worker
```

## 4. 缓存清理

```bash
# 进入容器清理 Laravel 缓存
docker exec math_cms_app php artisan optimize:clear

# 重启队列（代码更新后必须）
docker compose restart queue pdf-worker

# 清理临时文件
docker exec math_cms_app rm -rf storage/app/temp/*
```

## 5. 问题排查

```bash
# 查看容器资源使用
docker stats --no-stream

# 进入容器调试
docker exec -it math_cms_app sh

# 测试数据库连接
docker exec math_cms_app php artisan db:show

# 测试 Redis 连接
docker exec math_cms_app php artisan tinker --execute="dump(app('redis')->ping());"

# 测试 Chrome（PDF 生成）
docker exec math_cms_pdf chromium-browser --version

# 查看失败的队列任务
docker exec math_cms_app php artisan queue:failed

# 重试失败的任务
docker exec math_cms_app php artisan queue:retry all

# 查看失败任务详情
docker exec math_cms_app php artisan tinker --execute="\$job = DB::table('failed_jobs')->latest()->first(); echo \$job->exception ?? 'no failed jobs';"
```

## 6. 紧急处理

```bash
# CPU 满载 - 停止 PDF 服务
docker compose stop pdf-worker

# 强制杀死所有 Chrome 进程
docker exec math_cms_pdf pkill -9 chromium

# 清理临时文件
docker exec math_cms_pdf rm -rf /tmp/chrome-profile-*
docker exec math_cms_pdf rm -rf /tmp/exam_pdf_*

# 重启 PDF 服务
docker compose restart pdf-worker

# 修复 storage 权限问题
docker exec math_cms_app chmod -R 777 /app/storage
```

## 7. 健康检查

```bash
# 检查 Web 服务
curl http://localhost:5019/health

# 检查容器状态
docker compose ps

# 检查容器健康状态
docker inspect --format='{{.State.Health.Status}}' math_cms_app
docker inspect --format='{{.State.Health.Status}}' math_cms_queue
docker inspect --format='{{.State.Health.Status}}' math_cms_pdf

# 检查 PHP-FPM 进程数
docker exec math_cms_app ps aux | grep php-fpm | wc -l

# 检查 Nginx Worker 数
docker exec math_cms_app ps aux | grep nginx
```

## 8. 备份与恢复

```bash
# 备份日志
cp -r storage/logs storage/logs_backup_$(date +%Y%m%d)

# 备份上传文件
cp -r storage/app/public storage/app/public_backup_$(date +%Y%m%d)
```

## 9. 定时任务（加入 crontab）

```bash
# 清理临时文件（每小时）
0 * * * * cd /path/to/math_cms && rm -rf storage/app/temp/* 2>/dev/null

# 清理旧日志（每天凌晨 3 点）
0 3 * * * find /path/to/math_cms/storage/logs -name "*.log" -mtime +7 -delete
```

## 10. 容器资源限制

```bash
# 查看资源限制
docker inspect math_cms_pdf --format='{{.HostConfig.Memory}}'

# 临时调整资源限制
docker update --cpus=1 --memory=1g math_cms_pdf
```

---

## 服务架构

```
┌──────────────────────────────────────────────────────────────────┐
│                        Docker Compose                            │
│  ┌──────────────────┐ ┌───────────────┐ ┌──────────────────────┐ │
│  │  math_cms_app    │ │ math_cms_queue│ │   math_cms_pdf       │ │
│  │  (Nginx+PHP-FPM) │ │ (Queue Worker)│ │   (PDF Worker)       │ │
│  │  :5019 → :8000   │ │ default 队列   │ │   pdf 队列            │
│  │  并发: 30 worker │ │ CPU:1 Mem:512M│ │   CPU:2 Mem:2G       │ │
│  └──────────────────┘ └───────────────┘ └──────────────────────┘ │
└──────────────────────────────────────────────────────────────────┘
         │                     │                     │
         └─────────────────────┴─────────────────────┘
                               │
            ┌──────────────────┴──────────────────┐
            ▼                                     ▼
      ┌──────────┐                          ┌──────────┐
      │  Redis   │                          │  MySQL   │
      │ (阿里云)  │                          │ (阿里云)  │
      └──────────┘                          └──────────┘
```

### 队列说明

| 队列 | 容器 | 处理任务 |
|------|------|---------|
| default | math_cms_queue | AI批改、知识点匹配、题目导入等 |
| pdf | math_cms_pdf | PDF生成（需要Chrome） |

---

## 11. PDF 生成问题排查

### 常见问题

**问题1: PDF生成超时（50秒后失败）**
- 原因：HTML模板中引用了CDN资源（KaTeX CSS/JS），容器内网络请求慢
- 解决：代码已优化，使用服务端 Node.js KaTeX 预渲染公式

**问题2: 公式显示为原始 LaTeX 文本**
- 原因：Chrome headless 的 --print-to-pdf 不等待 JavaScript 执行
- 解决：使用 Node.js KaTeX 在服务端预渲染公式，生成静态 HTML

**问题3: dbus 连接错误**
- 表现：日志中出现 `Failed to connect to the bus` 错误
- 解决：docker-entrypoint.sh 已配置自动启动 dbus-daemon

**问题4: Chrome 进程积累导致 CPU 满载**
- 原因：PDF生成任务积压，Chrome进程未正常退出
- 解决：
  ```bash
  # 强制清理Chrome进程
  docker exec math_cms_pdf pkill -9 chromium

  # 清理临时文件
  docker exec math_cms_pdf rm -rf /tmp/chrome-profile-* /tmp/exam_*

  # 重启PDF服务
  docker compose restart pdf-worker
  ```

**问题5: 日志为空（权限问题）**
- 原因：storage 目录权限不正确，php-fpm 无法写入
- 解决：
  ```bash
  docker exec math_cms_app chmod -R 777 /app/storage
  ```

**问题6: GenerateAnalysisPdfJob 失败**
- 原因：Job 未指定 pdf 队列，被 default 队列处理（无 Chrome）
- 解决：代码已修复，Job 使用 `$this->onQueue('pdf')`

### PDF 生成调试

```bash
# 查看PDF生成日志
docker exec math_cms_app grep -i "ExamPdfExportService" storage/logs/laravel.log | tail -50

# 查看KaTeX渲染日志
docker exec math_cms_app grep -i "KatexRenderer\|LaTeX.*预渲染" storage/logs/laravel.log | tail -20

# 测试Node.js KaTeX是否正常工作
docker exec math_cms_app sh -c "echo '\$x^2 + y^2 = z^2\$' | node /app/scripts/katex-render.mjs"

# 测试Chrome是否正常工作
docker exec math_cms_pdf chromium-browser --headless --disable-gpu --no-sandbox --print-to-pdf=/tmp/test.pdf https://www.baidu.com

# 检查Chrome版本
docker exec math_cms_pdf chromium-browser --version

# 检查Node.js版本
docker exec math_cms_app node --version

# 检查dbus状态
docker exec math_cms_pdf pgrep -a dbus

# 查看 PDF 队列处理情况
docker logs math_cms_pdf --tail=30
```

---

## 12. 并发问题排查

### 试卷保存失败（题目数量不匹配）

错误信息：`题目插入数量不匹配：预期 20，实际 40`

**原因：** 并发请求导致 Paper ID 碰撞，两个请求插入到同一个 paper_id

**解决：** 已优化 PaperIdGenerator，增加进程ID区分不同 PHP-FPM Worker

**排查命令：**
```bash
# 查看相关错误
docker exec math_cms_app grep "题目插入数量不匹配" storage/logs/laravel.log | tail -10
```

---

## 联系方式

如有问题请联系开发团队。
