From 13b4505ad4b9f0bfb2fede8d35ab2ea4291e11a7 Mon Sep 17 00:00:00 2001 From: shidehai Date: Fri, 26 Jun 2026 11:06:59 +0800 Subject: [PATCH] fix legacy CPU Docker deployment --- .env.example | 4 ++++ Dockerfile | 8 +++++++- README.md | 18 ++++++++++++++++++ backend/pyproject.toml | 7 +++++++ backend/uv.lock | 29 +++++++++++++++++++++++++++-- docker-compose.yml | 2 ++ 6 files changed, 65 insertions(+), 3 deletions(-) diff --git a/.env.example b/.env.example index 5ed6b31..3cce986 100644 --- a/.env.example +++ b/.env.example @@ -14,5 +14,9 @@ HOST=0.0.0.0 PORT=3018 LOG_LEVEL=INFO +# Optional Docker build extras. Set to legacy-cpu on older VPS hosts +# without AVX2/FMA support. +BACKEND_EXTRAS= + # ===== Data ===== DATA_DIR=./data diff --git a/Dockerfile b/Dockerfile index 71641f8..3ec05f1 100644 --- a/Dockerfile +++ b/Dockerfile @@ -3,6 +3,7 @@ ARG USE_CN_MIRROR=1 ARG NPM_REGISTRY=https://registry.npmmirror.com ARG PYPI_INDEX=https://pypi.tuna.tsinghua.edu.cn/simple +ARG BACKEND_EXTRAS= # === Stage 1: 前端构建 === FROM node:20-alpine AS frontend-builder @@ -25,6 +26,7 @@ RUN pnpm build FROM python:3.11-slim AS runtime ARG USE_CN_MIRROR=1 ARG PYPI_INDEX=https://pypi.tuna.tsinghua.edu.cn/simple +ARG BACKEND_EXTRAS= WORKDIR /app # 安装 uv(快) @@ -38,7 +40,11 @@ RUN if [ "$USE_CN_MIRROR" = "1" ]; then \ COPY README.md /README.md COPY backend/pyproject.toml backend/uv.lock* ./ RUN if [ "$USE_CN_MIRROR" = "1" ]; then export UV_DEFAULT_INDEX="$PYPI_INDEX"; fi; \ - uv sync --frozen --no-dev || uv sync --no-dev + set -- --no-dev; \ + for extra in $BACKEND_EXTRAS; do \ + set -- "$@" --extra "$extra"; \ + done; \ + uv sync --frozen "$@" || uv sync "$@" # Backend code # 注意:Docker 里 WORKDIR=/app, 而 config.py 的 _PROJECT_ROOT 是按开发布局 diff --git a/README.md b/README.md index 5ad8fa4..23e8188 100644 --- a/README.md +++ b/README.md @@ -192,6 +192,21 @@ docker compose up --build # 打开 http://localhost:3018 ``` +如果你的 VPS / 物理机 CPU 较老,`docker compose up --build` 时出现类似 +`Missing required CPU features: avx2, fma, bmi1...` 或容器 `exit code 132`, +先在 `.env` 里打开 Polars 兼容运行时再重建: + +```ini +BACKEND_EXTRAS=legacy-cpu +``` + +```bash +docker compose up --build +``` + +`legacy-cpu` 会让后端安装 `polars[rtcompat]`,适用于不支持 AVX2/FMA 的老 CPU; +默认留空则保持 Polars 高性能运行时。 + 首次运行会自动安装前后端依赖(约 1-2 分钟),之后直接启动: @@ -220,6 +235,9 @@ pnpm dev # http://localhost:3011 > **启用回测**:`cd backend && uv sync --extra backtest` > vectorbt → numba 体积较大,故作为可选 extras。macOS / Intel 无预构建 wheel 时需 `brew install cmake` 现场编译。 +> **老 CPU 兼容**:`cd backend && uv sync --extra legacy-cpu` +> 当机器缺少 `avx2/fma` 等指令集时,给 Polars 切到 `rtcompat` 运行时。 + --- ## 🧭 第一次使用 diff --git a/backend/pyproject.toml b/backend/pyproject.toml index 8aa1493..0c7fa66 100644 --- a/backend/pyproject.toml +++ b/backend/pyproject.toml @@ -35,6 +35,13 @@ dependencies = [ ] [project.optional-dependencies] +# Legacy CPU runtime: install the Polars compatibility kernel on +# machines without AVX2/FMA support. +# Enable with: uv sync --extra legacy-cpu +legacy-cpu = [ + "polars[rtcompat]>=1.0", +] + # 回测依赖 vectorbt → numba → llvmlite,体积大且 macOS/Intel 上无预构建 wheel 时 # 需要 brew install cmake 现场编译。挪到可选 extras,主依赖瘦身。 # 启用:`uv sync --extra backtest` diff --git a/backend/uv.lock b/backend/uv.lock index 09cb06f..9c7818b 100644 --- a/backend/uv.lock +++ b/backend/uv.lock @@ -1541,6 +1541,11 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/ea/91/74fc60d94488685a92ac9d49d7ec55f3e91fe9b77942a6235a5fa7f249c3/polars-1.40.1-py3-none-any.whl", hash = "sha256:c0f861219d1319cdea45c4ce4d30355a47176b8f98dcedf95ea8269f131b8abd", size = 828723, upload-time = "2026-04-22T19:14:25.452Z" }, ] +[package.optional-dependencies] +rtcompat = [ + { name = "polars-runtime-compat" }, +] + [[package]] name = "polars-runtime-32" version = "1.40.1" @@ -1557,6 +1562,22 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/65/ad/b33c3022a394f3eb55c3310597cec615412a8a33880055eee191d154a628/polars_runtime_32-1.40.1-cp310-abi3-win_arm64.whl", hash = "sha256:b5cbfaf6b085b420b4bfcbe24e8f665076d1cccfdb80c0484c02a023ce205537", size = 45822104, upload-time = "2026-04-22T19:14:54.192Z" }, ] +[[package]] +name = "polars-runtime-compat" +version = "1.40.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/4c/60/f8340030f94a45231ad02abff171c8c10f45df0984e5a4fd0f39a48500f4/polars_runtime_compat-1.40.1.tar.gz", hash = "sha256:6149aa764439cec26a5f883fb9921a50f61a0f6c4549df51c735626701a73a18", size = 2935443, upload-time = "2026-04-22T19:16:00.906Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/07/c7/978481a2a2f5b50636d485bc176e16084fa1bb3b1d7e7e34f580cef240dc/polars_runtime_compat-1.40.1-cp310-abi3-macosx_10_12_x86_64.whl", hash = "sha256:a683d287237f1dcc3dee95b5b2b6408cec318abbbb412ca49206492513be862f", size = 52016973, upload-time = "2026-04-22T19:15:25.228Z" }, + { url = "https://files.pythonhosted.org/packages/8b/c9/e308b0bbb331330a762f5f495597d34e9933c965ca6d4026a2eb481ef4ad/polars_runtime_compat-1.40.1-cp310-abi3-macosx_11_0_arm64.whl", hash = "sha256:c83750f5593cec088134614fbf783fd10c5a92030d71b35f3dea0fff682d92c6", size = 46246682, upload-time = "2026-04-22T19:15:28.666Z" }, + { url = "https://files.pythonhosted.org/packages/1d/54/eaecb4747695e2e200ed6d13ce40dd7b0cdc7499d0b9af1e64e83af0e46d/polars_runtime_compat-1.40.1-cp310-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ffa15c4a8b7f67911412c849dc3d3aac313f682d834eb3f374d0f6cb7e080efc", size = 50123217, upload-time = "2026-04-22T19:15:32.321Z" }, + { url = "https://files.pythonhosted.org/packages/20/87/0a881905d94341111d38e6a7ae94674511e6797cf3be2500998f06963edf/polars_runtime_compat-1.40.1-cp310-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e0373dabf942135d13b453d50be7a84b14420b495bd1242570545f58866396e7", size = 55869626, upload-time = "2026-04-22T19:15:37.15Z" }, + { url = "https://files.pythonhosted.org/packages/be/6e/ac57e39bb94ebd63d6714895881ee0461e39039daa208916e5cad93174f9/polars_runtime_compat-1.40.1-cp310-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:bcdc488472d2c57dd3315a897456b47cdac222a1520fcf3d8d38410a5bf47ec1", size = 50287716, upload-time = "2026-04-22T19:15:40.829Z" }, + { url = "https://files.pythonhosted.org/packages/e1/f3/b75afc9f79cd4cadff52bdc4566119892b378f739a295573bf6bd0190a6c/polars_runtime_compat-1.40.1-cp310-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:34d03962169fc7c0ef6f6efd324d0a51350e7b5abf3b0cd118eb7a32e4d947ec", size = 53796312, upload-time = "2026-04-22T19:15:44.68Z" }, + { url = "https://files.pythonhosted.org/packages/67/68/0df61011d894d1c94c915b39423c9aab79b3f7b18b836031ed32987227e0/polars_runtime_compat-1.40.1-cp310-abi3-win_amd64.whl", hash = "sha256:d9ccfe58eeb776567cf9556a83b980b9face9f0b1bb629bf2cd2334f4d9daf57", size = 51696665, upload-time = "2026-04-22T19:15:48.65Z" }, + { url = "https://files.pythonhosted.org/packages/45/01/1bc7e868d3e4055b1ae756ad8ccc2a5d2bce57d7c88c3b4427babd43450b/polars_runtime_compat-1.40.1-cp310-abi3-win_arm64.whl", hash = "sha256:0c662e6acf5d4e3784eee8e8a1ec6eb185132ac90689cb84034132b5787903b1", size = 45701223, upload-time = "2026-04-22T19:15:52.189Z" }, +] + [[package]] name = "prompt-toolkit" version = "3.0.52" @@ -2470,7 +2491,7 @@ all = [ [[package]] name = "tickflow-stock-panel-backend" -version = "0.1.44" +version = "0.1.45" source = { editable = "." } dependencies = [ { name = "apscheduler" }, @@ -2508,6 +2529,9 @@ dev = [ { name = "pytest-asyncio" }, { name = "ruff" }, ] +legacy-cpu = [ + { name = "polars", extra = ["rtcompat"] }, +] [package.metadata] requires-dist = [ @@ -2522,6 +2546,7 @@ requires-dist = [ { name = "platformdirs", specifier = ">=4.0" }, { name = "plyer", specifier = ">=2.1" }, { name = "polars", specifier = ">=1.0" }, + { name = "polars", extras = ["rtcompat"], marker = "extra == 'legacy-cpu'", specifier = ">=1.0" }, { name = "pyarrow", specifier = ">=16.0" }, { name = "pydantic", specifier = ">=2.7" }, { name = "pydantic-settings", specifier = ">=2.4" }, @@ -2538,7 +2563,7 @@ requires-dist = [ { name = "vectorbt", marker = "extra == 'backtest'", specifier = ">=0.26" }, { name = "winotify", marker = "sys_platform == 'win32'", specifier = ">=1.1" }, ] -provides-extras = ["backtest", "desktop", "dev"] +provides-extras = ["legacy-cpu", "backtest", "desktop", "dev"] [[package]] name = "tqdm" diff --git a/docker-compose.yml b/docker-compose.yml index 63a4565..0399a4e 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -5,6 +5,8 @@ services: build: context: . dockerfile: Dockerfile + args: + BACKEND_EXTRAS: ${BACKEND_EXTRAS:-} container_name: TickFlow_Stock_Panel ports: - "${PORT:-3018}:3018"