新增 doctor 诊断自救功能

This commit is contained in:
jxxghp
2026-06-12 15:55:24 +08:00
parent 10dcb3727e
commit 735a1ebf27
23 changed files with 1635 additions and 56 deletions

View File

@@ -112,7 +112,8 @@ RUN FRONTEND_VERSION=$(sed -n "s/^FRONTEND_VERSION\s*=\s*'\([^']*\)'/\1/p" /app/
# final 阶段: 安装运行时依赖和配置最终镜像
FROM prepare_package AS final
ENV LD_PRELOAD="/usr/local/lib/libjemalloc.so"
ENV LD_PRELOAD="/usr/local/lib/libjemalloc.so" \
MOVIEPILOT_DOCKER_KEEPALIVE_ON_FAILURE="true"
# 引入支持 amr 编码的静态 ffmpeg
COPY --from=mwader/static-ffmpeg:8.1.1 /ffmpeg /usr/local/bin/
@@ -143,7 +144,8 @@ RUN cp -f /app/docker/nginx.common.conf /etc/nginx/common.conf \
&& cp -f /app/docker/update.sh /usr/local/bin/mp_update.sh \
&& cp -f /app/docker/entrypoint.sh /entrypoint.sh \
&& cp -f /app/docker/docker_http_proxy.conf /etc/nginx/docker_http_proxy.conf \
&& chmod +x /entrypoint.sh /usr/local/bin/mp_update.sh \
&& printf '%s\n' '#!/usr/bin/env bash' 'set -euo pipefail' 'cd /app' 'exec "${VENV_PATH:-/opt/venv}/bin/python3" -m app.cli "$@"' > /usr/local/bin/moviepilot \
&& chmod +x /entrypoint.sh /usr/local/bin/mp_update.sh /usr/local/bin/moviepilot \
&& mkdir -p ${HOME} \
&& groupadd -r moviepilot -g 918 \
&& useradd -r moviepilot -g moviepilot -d ${HOME} -s /bin/bash -u 918 \
@@ -156,4 +158,5 @@ RUN cp -f /app/docker/nginx.common.conf /etc/nginx/common.conf \
EXPOSE 3000
VOLUME [ "${CONFIG_DIR}" ]
HEALTHCHECK --interval=30s --timeout=5s --start-period=60s --retries=3 CMD curl -fsS "http://127.0.0.1:${PORT:-3001}/api/v1/system/global?token=moviepilot" >/dev/null || exit 1
ENTRYPOINT [ "/usr/bin/tini", "-g", "--", "/entrypoint.sh" ]

View File

@@ -43,6 +43,8 @@ function load_config_from_app_env() {
["PROXY_HOST"]=""
["GITHUB_TOKEN"]=""
["MOVIEPILOT_AUTO_UPDATE"]="release"
["MOVIEPILOT_DOCKER_KEEPALIVE_ON_FAILURE"]="true"
["MOVIEPILOT_SAFE_MODE"]="false"
["BROWSER_EMULATION"]="cloakbrowser"
# cert
@@ -197,6 +199,8 @@ function graceful_exit() {
if [ "$reason" = "signal" ]; then
INFO "→ 收到停止信号,执行精准清理程序..."
elif [ "$reason" = "intentional_restart" ]; then
INFO "→ 检测到内置重启流程,执行清理程序..."
else
INFO "→ 主进程已退出 (代码: $exit_code),执行清理程序..."
fi
@@ -224,7 +228,7 @@ function graceful_exit() {
# 根据退出码判断最终日志性质
# 0: 正常退出
# 130/143: 被系统信号终止(通常也视为预期的清理退出)
if [ "$exit_code" -eq 0 ] || [ "$exit_code" -eq 130 ] || [ "$exit_code" -eq 143 ]; then
if [ "$exit_code" -eq 0 ] || [ "$exit_code" -eq 130 ] || [ "$exit_code" -eq 143 ] || [ "$reason" = "intentional_restart" ]; then
INFO "→ 所有服务已按序清理,容器正常退出 (ExitCode: $exit_code)。"
else
# 非预期退出码,使用 ERROR 级别并加重提示
@@ -233,6 +237,32 @@ function graceful_exit() {
exit "$exit_code"
}
# 后端异常退出时默认保留容器,避免无法 docker exec 进入容器运行 doctor。
function diagnostic_keepalive() {
local exit_code=${1:-1}
local keepalive="${MOVIEPILOT_DOCKER_KEEPALIVE_ON_FAILURE:-true}"
keepalive="${keepalive,,}"
if [ "${keepalive}" = "false" ] || [ "${keepalive}" = "0" ] || [ "${keepalive}" = "no" ]; then
graceful_exit "$exit_code" "python_exit"
fi
ERROR "→ 后端主进程异常退出 (ExitCode: ${exit_code}),容器将保持运行以便执行 moviepilot doctor。"
WARN "→ 可运行docker exec <container> moviepilot doctor"
WARN "→ 如需恢复旧行为,可设置 MOVIEPILOT_DOCKER_KEEPALIVE_ON_FAILURE=false。"
if [ "${START_NOGOSU:-false}" = "true" ]; then
"${VENV_PATH}/bin/python3" -m app.cli doctor || true
else
gosu moviepilot:moviepilot "${VENV_PATH}/bin/python3" -m app.cli doctor || true
fi
while true; do
sleep 3600 &
wait $! || true
done
}
# 启动前先检查后端核心依赖是否仍然可导入。
# 插件依赖和主程序共用同一套 venv 时,历史安装记录可能已经污染环境,
# 这里优先在真正拉起后端前做一次自愈,避免容器反复起不来。
@@ -255,12 +285,12 @@ function ensure_backend_runtime_dependencies() {
if ! "${pip_cmd[@]}" > /dev/stdout 2> /dev/stderr; then
ERROR "→ 自动恢复主程序依赖失败,后端无法启动。"
exit 1
diagnostic_keepalive 1
fi
if ! "${VENV_PATH}/bin/python3" -c "${probe_code}" >/dev/null 2>&1; then
ERROR "→ 主程序依赖恢复后仍然异常,后端无法启动。"
exit 1
diagnostic_keepalive 1
fi
INFO "→ 已自动恢复主程序依赖,继续启动后端。"
@@ -376,4 +406,19 @@ wait "$PYTHON_PID" 2>/dev/null
exit_code=$?
# 如果 Python 自己退出了(非信号触发),执行清理
graceful_exit "$exit_code" "python_exit"
INTENTIONAL_RESTART_FLAG="${CONFIG_DIR}/temp/moviepilot.intentional_restart"
if [ -f "${INTENTIONAL_RESTART_FLAG}" ]; then
rm -f "${INTENTIONAL_RESTART_FLAG}"
restart_exit_code="$exit_code"
if [ "$restart_exit_code" -eq 0 ]; then
restart_exit_code=1
fi
WARN "→ 检测到内置手动重启标记,退出容器并交给 Docker 重启策略处理..."
graceful_exit "$restart_exit_code" "intentional_restart"
fi
if [ "$exit_code" -eq 0 ] || [ "$exit_code" -eq 130 ] || [ "$exit_code" -eq 143 ]; then
graceful_exit "$exit_code" "python_exit"
fi
diagnostic_keepalive "$exit_code"