fix:增加定时推动
This commit is contained in:
@@ -20,6 +20,9 @@ RUN python -m pip install -U pip \
|
|||||||
COPY app /app/app
|
COPY app /app/app
|
||||||
COPY alembic /app/alembic
|
COPY alembic /app/alembic
|
||||||
COPY alembic.ini /app/alembic.ini
|
COPY alembic.ini /app/alembic.ini
|
||||||
|
COPY docker-entrypoint.sh /app/docker-entrypoint.sh
|
||||||
|
|
||||||
|
RUN chmod +x /app/docker-entrypoint.sh
|
||||||
|
|
||||||
EXPOSE 8000
|
EXPOSE 8000
|
||||||
|
|
||||||
@@ -29,4 +32,7 @@ EXPOSE 8000
|
|||||||
# - 参考文档:server/README.md
|
# - 参考文档:server/README.md
|
||||||
|
|
||||||
# 生产镜像默认不开启 reload
|
# 生产镜像默认不开启 reload
|
||||||
CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "8000"]
|
# 默认行为:只启动 API(与之前一致)
|
||||||
|
# 如需同时启动定时推送相关进程(Celery Worker/Beat),可在运行时注入:
|
||||||
|
# -e START_ALL=1
|
||||||
|
ENTRYPOINT ["/app/docker-entrypoint.sh"]
|
||||||
|
|||||||
85
server/docker-entrypoint.sh
Normal file
85
server/docker-entrypoint.sh
Normal file
@@ -0,0 +1,85 @@
|
|||||||
|
#!/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;避免误配。
|
||||||
|
if [ "$START_BEAT" = "1" ] && [ "$START_WORKER" != "1" ]; then
|
||||||
|
log "提示:已启用 START_BEAT=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
|
||||||
|
|
||||||
@@ -11,24 +11,28 @@ set -euo pipefail
|
|||||||
# ./run.sh --env prod # 使用 .env.prod(若存在且可被 source)
|
# ./run.sh --env prod # 使用 .env.prod(若存在且可被 source)
|
||||||
# ./run.sh --port 9000 # 改端口
|
# ./run.sh --port 9000 # 改端口
|
||||||
# ./run.sh --no-reload # 关闭热更新
|
# ./run.sh --no-reload # 关闭热更新
|
||||||
|
# ./run.sh --with-worker --with-beat # 同时启动 Celery Worker + Beat(用于定时推送)
|
||||||
# ./run.sh --install-only # 只安装依赖,不启动
|
# ./run.sh --install-only # 只安装依赖,不启动
|
||||||
|
|
||||||
usage() {
|
usage() {
|
||||||
cat <<'EOF'
|
cat <<'EOF'
|
||||||
用法:
|
用法:
|
||||||
./run.sh [--env dev|prod] [--host 0.0.0.0] [--port 8000] [--no-reload] [--skip-install] [--install-only]
|
./run.sh [--env dev|prod] [--host 0.0.0.0] [--port 8000] [--no-reload] [--with-worker] [--with-beat] [--skip-install] [--install-only]
|
||||||
|
|
||||||
参数:
|
参数:
|
||||||
--env dev|prod 优先尝试加载 .env.dev 或 .env.prod(如果存在)。
|
--env dev|prod 优先尝试加载 .env.dev 或 .env.prod(如果存在)。
|
||||||
--host <host> uvicorn host(默认 0.0.0.0)
|
--host <host> uvicorn host(默认 0.0.0.0)
|
||||||
--port <port> uvicorn port(默认 8000)
|
--port <port> uvicorn port(默认 8000)
|
||||||
--no-reload 关闭 uvicorn --reload
|
--no-reload 关闭 uvicorn --reload
|
||||||
|
--with-worker 同时启动 Celery Worker(处理异步/ETA 任务)
|
||||||
|
--with-beat 同时启动 Celery Beat(定时调度,例如每日生成推送排程)
|
||||||
--skip-install 跳过依赖安装(默认会安装/更新 requirements.txt)
|
--skip-install 跳过依赖安装(默认会安装/更新 requirements.txt)
|
||||||
--install-only 只安装依赖,不启动服务
|
--install-only 只安装依赖,不启动服务
|
||||||
-h, --help 显示帮助
|
-h, --help 显示帮助
|
||||||
|
|
||||||
说明:
|
说明:
|
||||||
- 若你的 .env.* 不是 shell 可 source 的格式(例如包含空格/特殊字符未加引号),建议改成 KEY=value 形式。
|
- 若你的 .env.* 不是 shell 可 source 的格式(例如包含空格/特殊字符未加引号),建议改成 KEY=value 形式。
|
||||||
|
- 仅启动 API 并不会生成 `push_send_log`;要测试“定时推送”,需要 Beat 调度 `tasks.push.generate_daily_schedule`,并由 Worker 执行后续 ETA 任务。
|
||||||
- 启动后访问:
|
- 启动后访问:
|
||||||
/healthz 健康检查
|
/healthz 健康检查
|
||||||
/docs OpenAPI 文档
|
/docs OpenAPI 文档
|
||||||
@@ -45,6 +49,8 @@ PORT="8000"
|
|||||||
RELOAD="1"
|
RELOAD="1"
|
||||||
SKIP_INSTALL="0"
|
SKIP_INSTALL="0"
|
||||||
INSTALL_ONLY="0"
|
INSTALL_ONLY="0"
|
||||||
|
WITH_WORKER="0"
|
||||||
|
WITH_BEAT="0"
|
||||||
|
|
||||||
while [[ $# -gt 0 ]]; do
|
while [[ $# -gt 0 ]]; do
|
||||||
case "$1" in
|
case "$1" in
|
||||||
@@ -64,6 +70,14 @@ while [[ $# -gt 0 ]]; do
|
|||||||
RELOAD="0"
|
RELOAD="0"
|
||||||
shift 1
|
shift 1
|
||||||
;;
|
;;
|
||||||
|
--with-worker)
|
||||||
|
WITH_WORKER="1"
|
||||||
|
shift 1
|
||||||
|
;;
|
||||||
|
--with-beat)
|
||||||
|
WITH_BEAT="1"
|
||||||
|
shift 1
|
||||||
|
;;
|
||||||
--skip-install)
|
--skip-install)
|
||||||
SKIP_INSTALL="1"
|
SKIP_INSTALL="1"
|
||||||
shift 1
|
shift 1
|
||||||
@@ -99,6 +113,15 @@ if [[ -f "$ENV_FILE" ]]; then
|
|||||||
set +a
|
set +a
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
# 让 API/Celery 统一使用同一个 APP_ENV(影响 Redis key 前缀、定时任务配置等)
|
||||||
|
export APP_ENV="${APP_ENV:-$ENV_NAME}"
|
||||||
|
|
||||||
|
# Beat 只负责“投递任务到队列”,真正执行仍需要 Worker;这里自动补齐,避免误用。
|
||||||
|
if [[ "$WITH_BEAT" == "1" && "$WITH_WORKER" == "0" ]]; then
|
||||||
|
echo "提示:已启用 --with-beat,自动同时启用 --with-worker(否则队列无人执行)。"
|
||||||
|
WITH_WORKER="1"
|
||||||
|
fi
|
||||||
|
|
||||||
# 选择 python 命令(优先 python3)
|
# 选择 python 命令(优先 python3)
|
||||||
PY_BIN=""
|
PY_BIN=""
|
||||||
if command -v python3 >/dev/null 2>&1; then
|
if command -v python3 >/dev/null 2>&1; then
|
||||||
@@ -140,6 +163,45 @@ if [[ "$RELOAD" == "1" ]]; then
|
|||||||
UVICORN_ARGS+=(--reload)
|
UVICORN_ARGS+=(--reload)
|
||||||
fi
|
fi
|
||||||
|
|
||||||
echo "启动服务:uvicorn ${UVICORN_ARGS[*]}"
|
if [[ "$WITH_WORKER" == "0" && "$WITH_BEAT" == "0" ]]; then
|
||||||
exec uvicorn "${UVICORN_ARGS[@]}"
|
echo "启动服务:uvicorn ${UVICORN_ARGS[*]}"
|
||||||
|
exec uvicorn "${UVICORN_ARGS[@]}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
PIDS=()
|
||||||
|
|
||||||
|
cleanup() {
|
||||||
|
# 避免重复清理导致脚本退出码被覆盖
|
||||||
|
set +e
|
||||||
|
if [[ ${#PIDS[@]} -gt 0 ]]; then
|
||||||
|
echo ""
|
||||||
|
echo "正在停止后台进程..."
|
||||||
|
# 先尝试温和退出
|
||||||
|
for pid in "${PIDS[@]}"; do
|
||||||
|
kill -TERM "$pid" >/dev/null 2>&1 || true
|
||||||
|
done
|
||||||
|
# 等待一点时间,再强制杀掉仍存活的(防止残留)
|
||||||
|
sleep 1
|
||||||
|
for pid in "${PIDS[@]}"; do
|
||||||
|
kill -KILL "$pid" >/dev/null 2>&1 || true
|
||||||
|
done
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
trap cleanup EXIT INT TERM
|
||||||
|
|
||||||
|
if [[ "$WITH_WORKER" == "1" ]]; then
|
||||||
|
echo "启动 Celery Worker:celery -A app.worker:celery_app worker -l info"
|
||||||
|
celery -A app.worker:celery_app worker -l info &
|
||||||
|
PIDS+=("$!")
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ "$WITH_BEAT" == "1" ]]; then
|
||||||
|
echo "启动 Celery Beat:celery -A app.worker:celery_app beat -l info"
|
||||||
|
celery -A app.worker:celery_app beat -l info &
|
||||||
|
PIDS+=("$!")
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "启动服务:uvicorn ${UVICORN_ARGS[*]}"
|
||||||
|
uvicorn "${UVICORN_ARGS[@]}"
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user