DSAWiki
12使用 · 运维

部署与运维:让它稳定地每天上班

从 GitHub Actions 到 Docker、服务器与定时任务,以及出问题时的排查顺序。

约 6 分钟 实战 第 12 章 · 使用

让一个分析系统「跑一次」不难,难的是它每天 18:00 都准时上班、失败能自愈、账单不爆炸、数据不丢。这一章把部署、调度、安全、备份与排障串成一套可执行的运维清单。

先选「运行位置」,再谈配置

GitHub Actions 适合「零服务器、每天一次」;Docker 适合「长期在线、还要 Web」;本地 Python 适合「开发调试」。三者的配置大体相同,差别在调度入口、数据持久化与网络暴露。

三条部署路径怎么选

维度GitHub ActionsDocker本地 Python
服务器成本无一台 1–2G 小机器自己的电脑
Web 工作台不支持支持支持
每天自动cron容器常驻调度 / 宿主机 cron--schedule / 系统定时
数据持久化Artifacts(30 天)+ 无数据库持久化挂载 data/ 目录本地 data/
调试便利看日志较强最强
适合体验、轻量日常长期稳定使用开发、改代码

一个常见组合是:GitHub Actions 跑每日分析 + 本地/服务器只开 Web 看历史。但要注意,Actions 的数据库不持久化,历史报告不会自动同步回本地;如果需要长期历史,请在 Docker/本地跑。

GitHub Actions:零服务器的每日任务

工作流在 .github/workflows/00-daily-analysis.yml,关键事实:

  • 触发:cron 0 10 * * 1-5(UTC)= 北京时间周一至周五 18:00;支持手动 full / market-only / stocks-only 与 force_run。
  • 并发组 stock-analysis,同一时间只跑一个,不取消正在运行的任务。
  • Ubuntu + Python 3.11,依赖缓存;启动前随机 sleep 0–60 秒错峰。
  • 默认 MAX_WORKERS=1、REPORT_TYPE=simple,降低云端资源与限流风险。
  • 超时由 ANALYSIS_TIMEOUT_MINUTES 控制(默认 30 分钟,可配)。
  • 产物 reports/、logs/ 上传为 Artifacts,保留 30 天。

它只执行命令行分析,不会启动 Web,也不会把 SQLite 带回来。把每次运行的报告当作「快照」,而不是完整的历史数据库。

Actions 的“环境变量”有两个位置

工作流里有一个 environment: STOCK_LIST,但这不意味着你必须在 Environments 里建一个同名环境。推荐仍然把 STOCK_LIST 放在 Repository variables 或 Secrets 中;两者都被工作流读取。

Docker:长期运行的标准答案

镜像采用多阶段构建:Node 20 构建前端,Python 3.11 运行后端;以非 root 用户 dsa(UID 1000)运行,健康检查访问 /api/health,默认命令是 python main.py --schedule。

cp .env.example .env
# 填写 STOCK_LIST、模型 Key、通知、API_PORT

# 定时分析
docker compose -f ./docker/docker-compose.yml up -d analyzer

# Web/API
docker compose -f ./docker/docker-compose.yml up -d server

# 两者同时(确保只保留一个调度入口)
docker compose -f ./docker/docker-compose.yml up -d
挂载 / 端口作用注意
./data → /app/dataSQLite、缓存、认证凭据必须持久化,否则重启丢历史
./logs、./reports日志与本地报告定期清理或归档
./strategies → /app/strategies自定义策略只读挂载;改完重启生效
API_PORT:8000Web/API公网暴露前先开认证 + HTTPS

内存建议 1G 以上;512M 仅适合轻量 Web/API、单股或低并发,并建议 MAX_WORKERS=1,不要同时跑 analyzer 与 server。容器内时区默认 Asia/Shanghai,避免 cron 与交易日判断错位。

本地 / 服务器:可控性最高

git clone https://github.com/ZhuLinsen/daily_stock_analysis.git
cd daily_stock_analysis
python3.11 -m venv .venv
source .venv/bin/activate
pip install -r requirements.txt
cp .env.example .env
vim .env

python main.py --schedule --no-run-immediately   # 常驻定时,不立即跑
python main.py --serve-only                      # 只开 Web/API

在 Linux 服务器上,用 systemd 托管比 nohup 可靠:

[Unit]
Description=DSA daily stock analysis
After=network-online.target

[Service]
WorkingDirectory=/opt/dsa
Environment=TZ=Asia/Shanghai
ExecStart=/opt/dsa/.venv/bin/python main.py --schedule
Restart=always
RestartSec=10

[Install]
WantedBy=multi-user.target

若要让 Web 对外访问,推荐 Nginx/Caddy 反向代理到 127.0.0.1:8000,并关闭 SSE 路径的代理缓冲:

location /api/v1/analysis/tasks/stream {
    proxy_pass http://127.0.0.1:8000;
    proxy_buffering off;
    proxy_read_timeout 3600s;
}

调度:只能有一个「发令枪」

项目有三类调度入口:CLI --schedule、Web 内置 RuntimeScheduler、GitHub Actions cron。它们都最终调用同一条分析流水线,但不要同时开多个入口去跑同一批股票。

  • Actions 用 concurrency group 防并发。
  • 本地进程用文件锁。
  • 定时任务通过 SQLite 做原子认领,尽量保证「至多一次」。
  • 多机部署且数据库不共享时,认领失效,可能重复推送。

如果你在 Docker 里同时起了 analyzer 和 server,而 server 又启用了 Web 调度,就等于两把发令枪。正确做法是只保留一个:要么让 analyzer 负责定时、server 只提供 Web;要么反过来。

安全:公网部署的底线

认证是单管理员模式,密码用 PBKDF2-SHA256(10 万次迭代)存哈希,Cookie 为 HttpOnly + SameSite=Lax,默认 24 小时;登录失败 5 次/300 秒限流;登出会轮换密钥使旧会话全部失效。

不要把「本机方便」变成「公网裸奔」

--host 0.0.0.0 会把服务暴露到所有网卡。没有认证时,任何人访问你的 IP 就能看到持仓、配置与报告,甚至触发分析消耗你的模型额度。公网部署前,先完成上面的清单。

日常运维:备份、升级与排障

备份

核心资产是 data/stock_analysis.db(历史报告、决策信号、持仓、会话)、data/.session_secret、data/.admin_password_hash 与 reports/。SQLite 使用 WAL 模式,最稳妥的备份方式是停服务后复制,或用 SQLite 的在线备份命令;直接 cp 正在写入的库可能拿到不一致快照。

升级

git pull --ff-only
pip install -r requirements.txt
# 前端有改动时重新构建
cd apps/dsa-web && npm ci && npm run build
# 重启服务;数据库迁移会在启动时自动执行

迁移是幂等的「自愈式」检查:缺列补列、缺索引补索引。升级前仍然建议备份数据库,尤其是跨多个版本时。

成本大概是多少

项目量级说明
GitHub Actions0 元起公开仓库通常免费;私有仓库计入账号额度
LLM与模型单价 × token 成正比单股一次约几千到数万 token;批量与 Agent 更高。看「用量」页而非猜
服务器一台 1C1G/2G 小机器个人使用足够;本地 NAS 也可
数据源0 元起免费源可用;Tushare/Longbridge 等按各自规则

控制成本最有效的手段不是「少分析」,而是选对模型、控制上下文、限制并发、善用回退。一次分析的输入里,新闻与 Prompt 往往比行情更占 token。

排障顺序:先看哪一层

现象先查常见解法
任务没触发交易日 / cron / Actions 是否启用--force-run、检查时区、手动触发
触发但没结果日志中 empty_stock_list / 数据源异常检查 STOCK_LIST、换数据源、看数据中心
有结果但没推送通知渠道配置 / 降噪 / 路由--check-notify、看降噪日志、检查 NOTIFICATION_*
LLM 超时/报错模型额度、网络、并发降并发、换回退模型、缩短上下文
Web 空白 / Not Built前端静态产物构建前端或用 Docker 镜像
重复推送是否多个调度入口 / 多实例数据库不共享只保留一个发令枪,共享 SQLite
数据库锁/写失败磁盘、并发写、WAL降并发、检查磁盘、避免多实例
自测一下

你在 Docker 里同时启动了 analyzer(默认 --schedule)和 server,并在 Web 设置里也开启了定时任务。最可能发生什么?

原因:analyzer 容器默认以 --schedule 运行,server 的 Web 调度也是独立入口。两者共享同一个 SQLite 时,原子认领能降低重复概率,但并不能把所有竞态消灭;更稳妥的做法是明确只保留一个调度入口。不要依赖「系统应该会处理」。

源码与文档锚点