feat(service): dockerize — Dockerfile + docker-compose + 统一入口

把项目打包成可部署的数据同步服务:

- Dockerfile (multi-stage builder/runtime, slim base, 含 healthcheck)
- docker-compose.yml (postgres:16-alpine + market_sync, 持久化 pgdata + logs)
- .dockerignore (减 build context)
- bin/service_run.sh (单进程跑 uvicorn + scheduler 线程)
- app/entrypoints/worker.py 抽 start_scheduler_thread() (幂等)

部署:
  docker compose up -d
  # 然后访问 http://localhost:8100/docs 看 FastAPI

设计选择:
  - 单容器一服务(uvicorn 主 + scheduler daemon thread),不用 supervisord
  - 原 systemd timer 由进程内 scheduler 取代(配置已存在于 config 表)
  - PG 用 docker volume 持久化;同步日志单独 volume
This commit is contained in:
gao
2026-07-07 16:30:56 +08:00
parent a8ea132924
commit fbc3cc84f0
5 changed files with 299 additions and 1 deletions
+52 -1
View File
@@ -1,11 +1,14 @@
"""纯 worker 模式(无 web)— 启动调度器 + 健康监控。
用法:python -m app.worker
用法:
python -m app.worker # 独立 worker 进程
python -c "from app.entrypoints.worker import start_scheduler_thread; start_scheduler_thread()" # 在 web 进程内 inline 跑
"""
from __future__ import annotations
import json
import sys
import threading
import time
from pathlib import Path
@@ -21,6 +24,54 @@ setup_logging()
logger = get_logger("worker")
def start_scheduler_thread() -> None:
"""在当前进程内启动 scheduler + 健康监控(daemon thread)。
给 service_run.sh 用:在 uvicorn 同进程跑后台 scheduler,避免
docker 一个容器跑两个进程(supervisord / s6 那种方案)。
幂等:多次调用只启动一次。
"""
# 1. 注册数据源 + seed config
from app.core.datasource.registry import (
build_default_registry,
seed_datasource_configs,
start_health_monitor,
)
build_default_registry()
seed_datasource_configs()
# 2. seed sync registry + 恢复卡死任务
from app.core.sync.registry import recover_interrupted_syncs, seed_sync_registry
seed_sync_registry()
n = recover_interrupted_syncs()
if n > 0:
logger.warning(f"[worker] 恢复了 {n} 个中断的同步任务")
# 3. seed 节假日
if settings.holidays_list:
db_ops.upsert_config(
"trading_calendar_holidays",
json.dumps(settings.holidays_list, ensure_ascii=False),
category="general",
description="A 股休市日",
)
# 4. seed 默认计划任务
if settings.scheduler_auto_seed:
from app.core.scheduler.scheduler import seed_schedule_configs
seed_schedule_configs()
# 5. 注册 sync job + 启动调度器 + 健康监控
from app.core.scheduler.scheduler import (
register_sync_jobs,
start_scheduler,
)
register_sync_jobs()
start_scheduler()
start_health_monitor()
logger.info("[worker] scheduler + health monitor 已 inline 启动")
def main():
logger.info("[worker] market_data_sync worker 启动")