# 部署指南 本文介绍如何从零部署 `platform-tools` 设备自动化后台。 --- ## 1. 环境准备 ### 1.1 Python 环境 - **Python 3.10+**(推荐 3.12) - 安装后确认 `python --version` 和 `pip` 可用 ```bash # 验证 python --version # 应输出 3.10+ pip --version ``` ### 1.2 OpenSTF 服务 本项目依赖 OpenSTF 管理设备池。如已有 STF 服务,跳过本节。 STF 部署参考官方文档:https://github.com/DeviceFarmer/stf **需要获取的信息**: - STF 服务地址(如 `http://192.168.20.220:7100`) - STF API Token(在 STF 个人设置 → API Keys 里生成) ### 1.3 adb 工具 项目自带 adb 二进制在 `bin/adb/` 目录: - **Windows**:`bin/adb/adb.exe` + 依赖 dll(已包含) - **Linux**:`bin/adb/adb`(需 `chmod +x`) - **macOS**:`bin/adb/adb`(需 `chmod +x`) 如需替换为自己的 adb 版本,把对应平台的 adb 放进 `bin/adb/` 即可,`config.py` 会自动识别操作系统。 ### 1.4 设备准备 设备需满足: - 开启 **USB 调试**(设置 → 开发者选项) - 或通过 **adb 网络连接**(设置 → 开发者选项 → 无线调试,获取 IP:5555) - 已接入 STF 设备池(STF 显示 present=True, ready=True) --- ## 2. 安装部署 ### 2.1 获取代码 ```bash # 方式一:直接拷贝项目目录 # 方式二:解压打包文件(python scripts/pack.py 生成的 zip) ``` ### 2.2 安装依赖 ```bash cd platform-tools pip install -r requirements.txt ``` **依赖清单**(`requirements.txt`): | 包 | 版本 | 用途 | |----|------|------| | Flask | >=2.3,<4.0 | Web 框架 | | Flask-Login | >=0.6 | 用户认证 | | Flask-SQLAlchemy | >=3.0,<4.0 | SQLite ORM | | APScheduler | >=3.10,<4.0 | 定时调度 | | requests | >=2.28 | HTTP 客户端 | | uiautomator2 | >=3.0 | Android UI 自动化 | | uiautodev | >=0.14 | UI 元素抓取 | | pyaxmlparser | >=0.3.27 | APK 元信息解析 | | rapidocr_onnxruntime | >=1.4 | 屏幕 OCR(条件判断的 OCR识别 选择器,中英文模型随包内置,跨平台) | > 服务器(无显示器/Linux)环境建议把 opencv-python 换成 `opencv-python-headless`(rapidocr 依赖 cv2,两者取一)。 > uiautomator2 首次连接设备时会自动推送 atx-agent 到设备,无需手动安装。 ### 2.3 修改配置 **密钥类配置统一放项目根目录 `.env`**(已被 `.gitignore` 排除,不会提交到 git; `config.py` 不再内置任何密钥): ```ini # .env 示例 STF_TOKEN=你的STF_API_Token WEB_SECRET_KEY=随机字符串(会话密钥,python -c "import secrets;print(secrets.token_hex(32))" 生成) TAILSCALE_API_KEY=你的Tailscale_API_key ``` `config.py` 只改非密钥项,如 `STF_URL`: ```python STF_URL = "http://你的STF地址:端口" ``` 其他配置按需调整: | 配置项 | 默认值 | 何时修改 | |-------|--------|---------| | `WEB_HOST` | `0.0.0.0` | 仅本机访问改为 `127.0.0.1` | | `WEB_PORT` | `18050` | 端口冲突时修改 | | `ADB_PATH` | 自动识别 | 用自定义 adb 时修改 | | `STF_SSH_TARGET` | `stf@192.168.20.220` | 维护页"一键重启 STF 容器"与工具页"STF 设备管理"的 SSH 目标 | | `STF_SSH_PASSWORD` | (空) | SSH 密码认证(.env 配置后走 paramiko 密码登录,跨平台);留空则退回系统 ssh 免密密钥(BatchMode) | | `STF_DOCKER_CONTAINER` | `stf` | 220 上 STF Docker 容器名(`docker ps` 查看) | | `STF_ADB_CONTAINER` | `adb` | 220 上 adb Docker 容器名(STF 设备管理执行 `docker exec adb adb ...`) | | `STF_SCRIPT_PATH` | `/mnt/data/openstf/connect_devices.sh` | STF 设备池脚本路径(工具页增删设备会读写其 DEVICES 列表) | | `TAILSCALE_TAILNET` | 按邮箱前缀 | tailnet 名称/ID(个人账号一般为登录邮箱前缀) | > **未配置 `WEB_SECRET_KEY`**:启动时随机生成(每次重启登录态失效,生产务必配置固定值)。 > **工具页 Tailscale 管理前置**:`.env` 写入 `TAILSCALE_API_KEY` 后重启服务; > 未配置时管理分区显示明确提示,不影响其他功能。 > **维护页 STF 重启 / 工具页 STF 设备管理前置**:需能 SSH 到部署机——两种方式任选: > 1. `.env` 配置 `STF_SSH_PASSWORD` 走密码认证(paramiko,无需额外工具,推荐) > 2. 免密密钥:本机公钥加入目标机 `authorized_keys` > 且 `STF_DOCKER_CONTAINER`/`STF_ADB_CONTAINER` 与真实容器名一致;未配置好时按钮会返回明确错误。 ### 2.4 启动服务 **命令行启动** ```bash python web_server.py ``` (推荐配合 `scripts/supervise.sh` 进程守护,见 3.1 节) ### 2.5 验证部署 1. 控制台看到 `启动服务: http://localhost:18050/` 即成功 2. 浏览器访问 `http://localhost:18050/` 3. 用 `admin/admin123` 登录 4. 监控页应显示 STF 设备池中的设备 --- ## 3. 生产部署建议 ### 3.1 进程守护 用进程守护工具确保服务自动重启: **Windows(NSSM)**: ```bat nssm install platform-tools "C:\Python312\python.exe" "D:\platform-tools\web_server.py" nssm start platform-tools ``` **Linux(systemd)**: ```ini # /etc/systemd/system/platform-tools.service [Unit] Description=Platform Tools Web Server After=network.target [Service] Type=simple User=www WorkingDirectory=/opt/platform-tools ExecStart=/usr/bin/python3 web_server.py Restart=always RestartSec=10 [Install] WantedBy=multi-user.target ``` **通用脚本(Linux/macOS,无需 systemd)**: 项目自带 `scripts/supervise.sh`:web_server 崩溃自动重启、带退避和重启上限(防崩溃死循环)。 ```bash # 用 venv 的 python 守护 PYTHON=./.venv/bin/python bash scripts/supervise.sh ``` > 进程守护只保证服务重启,不恢复已运行的任务(worker 状态在内存)。 > > **悬空设备自愈**:崩溃后残留的设备占用(STF 仍显示占用)可配置启动自动清理。单实例部署时在 `.env` 设置 `AUTO_RELEASE_STALE_OCCUPY=true`,web_server 启动会自动释放本账户残留占用;**多实例共用 STF 账户时不要开**(会误放另一实例的任务)。默认关,仅启动时提示。 **Docker(生产 python-app 容器)**: docker-compose 已配置 `restart: unless-stopped`,web_server 进程退出 → 容器退出 → Docker 自动重启。建议再加健康检查,让编排感知服务存活: ```yaml # docker-compose.yaml 的 python-app 服务下添加 healthcheck: test: ["CMD", "python", "-c", "import urllib.request; urllib.request.urlopen('http://127.0.0.1:18050/api/health', timeout=3)"] interval: 30s timeout: 5s retries: 3 start_period: 10s ``` ### 3.2 反向代理(可选) 如需 HTTPS 或 80 端口,用 Nginx 反向代理: ```nginx server { listen 80; server_name auto.example.com; location / { proxy_pass http://127.0.0.1:18050; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; } } ``` ### 3.3 安全加固 - **修改默认密码**:登录后立即在"用户"Tab 修改 admin 密码 - **最小权限分配**:需要多人使用后台时,在"用户"Tab 创建普通用户并只勾选必要权限 (任务管理/设备控制/应用管理/日志查看),不要把 admin 密码共享出去 - **修改 SECRET_KEY**:编辑 `web_server.py`,把 `app.config["SECRET_KEY"]` 改成随机字符串 - **限制访问**:生产环境把 `WEB_HOST` 改为 `127.0.0.1`,配合反向代理 - **防火墙**:只开放必要端口 ### 3.4 日志管理 - 日志自动滚动(10MB 一份,保留 5 份) - 日志目录 `logs/`,可在"日志"Tab 在线查看 - 长期运行建议定期清理或配置 logrotate ### 3.5 数据备份 - 数据库 `data/users.db` 包含用户/分组/任务数据 - APK 文件在 `data/apks/` - 建议定期备份 `data/` 目录 --- ## 4. 网络配置 ### 4.1 端口说明 | 端口 | 服务 | 说明 | |------|------|------| | 18050 | Web 后台 | 主服务端口(config.py 可改) | | 20242 | uiautodev | 元素抓取服务(自动启动,固定端口) | | 7100 | STF | STF 服务端口(STF 自己的配置) | | 5555 | adb | 设备 adb 网络端口(设备端) | ### 4.2 Windows 端口问题(仅 Windows) Windows 可能将某些端口范围划为动态排除范围,导致绑定失败(WinError 10013)。 ```bash # 查看排除的端口范围 netsh interface ipv4 show excludedportrange protocol=tcp ``` 如果 18050 在排除范围内,修改 `config.py` 的 `WEB_PORT` 到一个不在排除范围内的端口。 ### 4.3 局域网访问 - `WEB_HOST = "0.0.0.0"` 允许局域网访问 - 需要添加防火墙入站规则(Windows 手动添加,或参考 `netsh advfirewall firewall add rule`) - 局域网其他机器访问 `http://部署机IP:18050/` --- ## 5. 更新升级 ### 5.1 代码更新 ```bash # 1. 停止服务 # 2. 替换代码文件(或解压新的 zip) # 3. 重新安装依赖(如有新增) pip install -r requirements.txt # 4. 启动服务 python web_server.py ``` ### 5.2 数据库迁移 - SQLite 表结构变化时,`init_db()` 会自动 `db.create_all()` 创建新表 - 旧 `groups.json` / `jobs.json` 首次启动自动迁移到 SQLite - 迁移后 JSON 文件归档为 `.migrated`(保留备份,不再迁移) ### 5.3 注意事项 - **修改 `core/` 目录下的文件后必须重启 web_server**(`debug=False` 不热重载) - **修改 `templates/` 下的 HTML 文件**:Flask 模板默认不缓存,但建议重启确保生效 - **修改 `tasks/` 下的文件后必须重启**(任务注册在启动时完成) --- ## 6. 故障排查 ### 6.1 启动失败 | 现象 | 原因 | 解决 | |------|------|------| | `ModuleNotFoundError: No module named 'flask'` | 依赖未安装 | `pip install -r requirements.txt` | | `WinError 10013` | 端口被排除/权限不足 | 改端口或用管理员运行 | | `WinError 10048` | 端口被占用 | 改端口或杀占用进程 | | STF 获取设备列表失败 | STF 地址/token 错误 | 检查 `config.py` 的 `STF_URL` 和 `STF_TOKEN` | ### 6.2 设备连接失败 | 现象 | 原因 | 解决 | |------|------|------| | `DeviceOfflineError` | 设备掉线/STF provider 卡死 | 检查设备网络/STF 状态 | | `u2.connect 超时` | atx-agent 无响应 | 重启设备/重新推送 atx-agent | | `adb connect failed` | 设备网络不通/端口未开放 | 检查设备 IP 和 5555 端口 | | STF 设备显示离线 | STF 状态缓存 | 用"扫描前台App"复测 | ### 6.3 任务不执行 | 现象 | 原因 | 解决 | |------|------|------| | 任务列表有任务但不执行 | 任务未启用 / cron 未到点 | 检查 `enabled` 和 `schedule` | | 立即执行无反应 | 无可用设备 | 检查设备池是否有空闲设备 | | worker 状态 error | 查看日志的 `last_error` | 查看 `logs/core.log` 和 `logs/task.log` | | 看门狗误杀 | 长操作未心跳 | 在长循环内加 `self.heartbeat()` | ### 6.4 日志查看 ```bash # 查看核心日志 # 方式一:Web 后台"日志"Tab # 方式二:直接看文件 # logs/core.log — STF/adb/worker/task_manager # logs/task.log — 任务执行 # logs/web.log — Web 请求 # logs/action.log — 操作执行 ```