#!/bin/sh set -eu # 容器入口: # - 默认只启动 API(与原 Dockerfile 行为一致) # - 如需测试“定时推送”,可额外启动 Celery Worker + Beat # # 环境变量: # - START_API=1|0(默认 1) # - START_WORKER=1|0(默认 0) # - START_BEAT=1|0(默认 0) # - START_ALL=1(等价于 START_WORKER=1 + START_BEAT=1) # - HOST / PORT(API 监听地址,默认 0.0.0.0:8000) log() { echo "[entrypoint] $*" } START_API="${START_API:-1}" START_WORKER="${START_WORKER:-0}" START_BEAT="${START_BEAT:-0}" if [ "${START_ALL:-0}" = "1" ]; then START_WORKER="1" START_BEAT="1" fi # Beat 只负责“投递任务到队列”,真正执行仍需要 Worker。 # 说明: # - 单容器模式:若启动了 API(START_API=1)且同时启用了 Beat,则自动补齐 Worker,避免误配导致“只排程不执行”。 # - 多容器模式:允许单独启动 beat 容器(START_API=0, START_BEAT=1),不做自动补齐。 if [ "$START_API" = "1" ] && [ "$START_BEAT" = "1" ] && [ "$START_WORKER" != "1" ]; then log "提示:已启用 START_BEAT=1(且 START_API=1),自动同时启用 START_WORKER=1(否则队列无人执行)。" START_WORKER="1" fi PIDS="" stop_children() { # 温和退出 for pid in $PIDS; do kill -TERM "$pid" >/dev/null 2>&1 || true done } on_term() { log "收到退出信号,正在停止子进程..." stop_children # 等待子进程退出,避免残留 wait >/dev/null 2>&1 || true exit 0 } trap on_term INT TERM if [ "$START_WORKER" = "1" ]; then log "启动 Celery Worker:celery -A app.worker:celery_app worker -l info" celery -A app.worker:celery_app worker -l info & PIDS="$PIDS $!" fi if [ "$START_BEAT" = "1" ]; then log "启动 Celery Beat:celery -A app.worker:celery_app beat -l info" celery -A app.worker:celery_app beat -l info & PIDS="$PIDS $!" fi if [ "$START_API" = "1" ]; then HOST="${HOST:-0.0.0.0}" PORT="${PORT:-8000}" log "启动 API:uvicorn app.main:app --host $HOST --port $PORT" uvicorn app.main:app --host "$HOST" --port "$PORT" & API_PID="$!" PIDS="$PIDS $API_PID" # 以 API 生命周期为准:API 退出则容器退出,并清理其他进程 wait "$API_PID" CODE="$?" log "API 已退出(code=$CODE),正在停止其他进程..." stop_children wait >/dev/null 2>&1 || true exit "$CODE" fi # 未启动 API:就阻塞等待其他进程(一般用于仅跑 worker/beat 的容器) log "未启动 API,等待后台进程..." wait