diff --git a/deploy/ocr-worker/app.py b/deploy/ocr-worker/app.py index 88a2a68..8311afc 100644 --- a/deploy/ocr-worker/app.py +++ b/deploy/ocr-worker/app.py @@ -112,12 +112,18 @@ def run_ocr_on_bytes(content: bytes) -> dict: @app.on_event("startup") def warmup(): - buf = __import__("io").BytesIO() - Image.new("RGB", (120, 40), color=(255, 255, 255)).save(buf, format="JPEG") - try: - run_ocr_on_bytes(buf.getvalue()) - except Exception: - pass + """延迟预热,不阻塞 HTTP 服务启动。""" + import threading + + def _run(): + buf = __import__("io").BytesIO() + Image.new("RGB", (120, 40), color=(255, 255, 255)).save(buf, format="JPEG") + try: + run_ocr_on_bytes(buf.getvalue()) + except Exception: + pass + + threading.Thread(target=_run, daemon=True, name="ocr-warmup").start() @app.get("/health") diff --git a/deploy/ocr-worker/check.sh b/deploy/ocr-worker/check.sh new file mode 100644 index 0000000..7acc5ae --- /dev/null +++ b/deploy/ocr-worker/check.sh @@ -0,0 +1,91 @@ +#!/usr/bin/env bash +# OCR Worker 一键诊断 +set -uo pipefail + +ROOT="$(cd "$(dirname "$0")" && pwd)" +PORT="${OCR_PORT:-23567}" +VENV="${ROOT}/.venv" + +echo "========== OCR Worker 诊断 ==========" +echo "目录: ${ROOT}" +echo "端口: ${PORT}" +echo "" + +echo "--- 1. 虚拟环境 ---" +if [[ -d "${VENV}" ]]; then + echo "OK .venv 存在" +else + echo "FAIL 未安装,请运行: bash install.sh" +fi + +echo "" +echo "--- 2. 端口监听 ---" +if command -v ss >/dev/null; then + if ss -tln | grep -q ":${PORT} "; then + echo "OK 端口 ${PORT} 正在监听" + ss -tlnp | grep ":${PORT} " || true + else + echo "FAIL 端口 ${PORT} 无服务 — 请先启动:" + echo " OCR_USE_GPU=true bash start.sh" + echo " 或: sudo bash install-service.sh" + fi +else + netstat -tln 2>/dev/null | grep ":${PORT} " || echo "FAIL 端口 ${PORT} 无服务" +fi + +echo "" +echo "--- 3. HTTP 健康检查 ---" +if command -v curl >/dev/null; then + resp="$(curl -sS -m 3 "http://127.0.0.1:${PORT}/health" 2>&1)" || true + if [[ -n "${resp}" ]]; then + echo "OK ${resp}" + else + echo "FAIL curl 无响应(服务未启动或启动失败)" + fi +else + echo "跳过(无 curl)" +fi + +echo "" +echo "--- 4. GPU ---" +if command -v nvidia-smi >/dev/null; then + nvidia-smi --query-gpu=name,memory.used,memory.total --format=csv,noheader +else + echo "未检测到 nvidia-smi" +fi + +echo "" +echo "--- 5. Python 依赖 ---" +if [[ -d "${VENV}" ]]; then + # shellcheck disable=SC1091 + source "${VENV}/bin/activate" + python3 - <<'PY' || true +try: + import paddle + print("OK paddle", paddle.__version__) +except Exception as e: + print("FAIL paddle:", e) +try: + import paddleocr + print("OK paddleocr") +except Exception as e: + print("FAIL paddleocr:", e) +try: + import fastapi, uvicorn + print("OK fastapi/uvicorn") +except Exception as e: + print("FAIL fastapi:", e) +PY +fi + +echo "" +echo "--- 6. systemd ---" +if systemctl is-active ocr-worker &>/dev/null; then + systemctl status ocr-worker --no-pager -l | head -15 +elif [[ -f /etc/systemd/system/ocr-worker.service ]]; then + echo "服务已安装但未运行: sudo systemctl start ocr-worker" +else + echo "未安装 systemd 服务(可选): sudo bash install-service.sh" +fi + +echo "====================================" diff --git a/deploy/ocr-worker/install-service.sh b/deploy/ocr-worker/install-service.sh new file mode 100644 index 0000000..113a471 --- /dev/null +++ b/deploy/ocr-worker/install-service.sh @@ -0,0 +1,53 @@ +#!/usr/bin/env bash +# 注册 OCR Worker 为 systemd 服务(后台常驻) +set -euo pipefail + +ROOT="$(cd "$(dirname "$0")" && pwd)" +PORT="${OCR_PORT:-23567}" + +if [[ "${EUID:-$(id -u)}" -ne 0 ]]; then + echo "请使用 root 运行: sudo bash install-service.sh" + exit 1 +fi + +if [[ ! -d "${ROOT}/.venv" ]]; then + echo "请先运行: bash ${ROOT}/install.sh" + exit 1 +fi + +chmod +x "${ROOT}/start.sh" "${ROOT}/check.sh" "${ROOT}/install.sh" + +cat > /etc/systemd/system/ocr-worker.service < 服务状态:" +systemctl status ocr-worker --no-pager -l | head -20 + +echo "" +echo "==> 健康检查:" +curl -sS "http://127.0.0.1:${PORT}/health" || echo "(尚未就绪,请稍等 30 秒后重试: bash check.sh)" +echo "" +echo "局域网地址: http://$(hostname -I 2>/dev/null | awk '{print $1}'):${PORT}" +echo "成绩档案系统设置 OCR 地址填: http://192.168.8.6:${PORT} (按实际 IP 修改)" diff --git a/deploy/ocr-worker/install.sh b/deploy/ocr-worker/install.sh index 855d1b4..dbadbb2 100644 --- a/deploy/ocr-worker/install.sh +++ b/deploy/ocr-worker/install.sh @@ -1,5 +1,5 @@ #!/usr/bin/env bash -# 在带 NVIDIA 显卡(如 RTX 3060 Ti)的 Linux 机器上安装 OCR Worker +# 在带 NVIDIA 显卡的 Linux 机器上安装 OCR Worker set -euo pipefail ROOT="$(cd "$(dirname "$0")" && pwd)" @@ -9,37 +9,65 @@ PORT="${OCR_PORT:-23567}" echo "==> OCR Worker 安装目录: ${ROOT}" if ! command -v python3 >/dev/null; then - echo "请先安装 python3" + echo "错误: 请先安装 python3" exit 1 fi -python3 -m venv "${VENV}" +if [[ -d "${VENV}" ]]; then + echo "==> 已有虚拟环境,跳过 python3 -m venv" +else + python3 -m venv "${VENV}" +fi + # shellcheck disable=SC1091 source "${VENV}/bin/activate" pip install -U pip wheel -# Paddle GPU(CUDA 11.8,适配多数 3060 Ti 驱动) -pip install paddlepaddle-gpu==2.6.2 -i https://www.paddlepaddle.org.cn/packages/stable/cu118/ +install_paddle() { + if command -v nvidia-smi >/dev/null 2>&1; then + local cuda_major + cuda_major="$(nvidia-smi 2>/dev/null | sed -n 's/.*CUDA Version: \([0-9]*\)\.[0-9]*/\1/p' | head -1)" + cuda_major="${cuda_major:-11}" + echo "==> 检测到 NVIDIA GPU,CUDA 主版本: ${cuda_major}" + if [[ "${cuda_major}" -ge 12 ]]; then + echo "==> 安装 paddlepaddle-gpu (CUDA 12.x)…" + pip install paddlepaddle-gpu==2.6.2 -i https://www.paddlepaddle.org.cn/packages/stable/cu123/ \ + || pip install paddlepaddle-gpu==2.6.2 -i https://www.paddlepaddle.org.cn/packages/stable/cu118/ + else + echo "==> 安装 paddlepaddle-gpu (CUDA 11.x)…" + pip install paddlepaddle-gpu==2.6.2 -i https://www.paddlepaddle.org.cn/packages/stable/cu118/ + fi + else + echo "==> 未检测到 GPU,安装 CPU 版 paddlepaddle…" + pip install paddlepaddle==2.6.2 + fi +} + +install_paddle pip install -r "${ROOT}/requirements.txt" +echo "" +echo "==> 验证依赖…" +python3 -c "import fastapi, uvicorn, paddle; print('paddle', paddle.__version__, 'OK')" + cat < 启动 OCR Worker: http://${HOST}:${PORT} (GPU=${OCR_USE_GPU})" +echo " 按 Ctrl+C 停止。后台运行请用: sudo bash install-service.sh" +echo "" + +exec uvicorn app:app --host "${HOST}" --port "${PORT}" --log-level info