From 55d868085115db47fd953a511d83bd150ecb7fde Mon Sep 17 00:00:00 2001 From: InfinityPacer <160988576+InfinityPacer@users.noreply.github.com> Date: Tue, 1 Sep 2026 21:36:23 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BC=98=E5=8C=96=20Docker=20=E9=95=9C?= =?UTF-8?q?=E5=83=8F=E5=A4=8D=E5=88=B6=E5=B1=82=20(#6540)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * perf(docker): consolidate runtime copy layers * test(docker): align control rootfs contract --- docker/Dockerfile | 36 +++++++++++++-------------- tests/test_docker_bootstrap.py | 14 +++++++---- tests/test_docker_payload_contract.py | 5 +++- 3 files changed, 30 insertions(+), 25 deletions(-) diff --git a/docker/Dockerfile b/docker/Dockerfile index b399f2168..de0e8c764 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -268,17 +268,21 @@ FROM prepare_payload AS prepare_control WORKDIR /control COPY docker/ ./ -RUN mkdir -p /bundle/control /bundle/nginx /bundle/bin \ - && find . -maxdepth 1 -type f -name '*.sh' ! -name 'launcher.sh' -exec cp -f -t /bundle/control {} + \ - && cp -f launcher.sh /bundle/entrypoint.sh \ - && cp -f nginx.common.conf /bundle/nginx/common.conf \ - && cp -f nginx.template.conf /bundle/nginx/nginx.template.conf \ - && cp -f docker_http_proxy.conf /bundle/nginx/docker_http_proxy.conf \ - && printf '%s\n' '#!/usr/bin/env bash' 'set -euo pipefail' 'cd /app' 'exec "${VENV_PATH:-/opt/venv}/bin/python3" -m app.cli "$@"' > /bundle/bin/moviepilot \ - && bash -n /bundle/entrypoint.sh \ - && for control_script in /bundle/control/*.sh; do bash -n "${control_script}" || exit 1; done \ - && chmod 755 /bundle/entrypoint.sh /bundle/bin/moviepilot \ - && chmod 500 /bundle/control/*.sh +RUN mkdir -p \ + /bundle/rootfs/etc/nginx \ + /bundle/rootfs/usr/local/bin \ + /bundle/rootfs/usr/local/lib/moviepilot/control \ + && find . -maxdepth 1 -type f -name '*.sh' ! -name 'launcher.sh' \ + -exec cp -f -t /bundle/rootfs/usr/local/lib/moviepilot/control {} + \ + && cp -f launcher.sh /bundle/rootfs/entrypoint.sh \ + && cp -f nginx.common.conf /bundle/rootfs/etc/nginx/common.conf \ + && cp -f nginx.template.conf /bundle/rootfs/etc/nginx/nginx.template.conf \ + && cp -f docker_http_proxy.conf /bundle/rootfs/etc/nginx/docker_http_proxy.conf \ + && printf '%s\n' '#!/usr/bin/env bash' 'set -euo pipefail' 'cd /app' 'exec "${VENV_PATH:-/opt/venv}/bin/python3" -m app.cli "$@"' > /bundle/rootfs/usr/local/bin/moviepilot \ + && bash -n /bundle/rootfs/entrypoint.sh \ + && for control_script in /bundle/rootfs/usr/local/lib/moviepilot/control/*.sh; do bash -n "${control_script}" || exit 1; done \ + && chmod 755 /bundle/rootfs/entrypoint.sh /bundle/rootfs/usr/local/bin/moviepilot \ + && chmod 500 /bundle/rootfs/usr/local/lib/moviepilot/control/*.sh # final 阶段: 安装运行时依赖和配置最终镜像 FROM prepare_package AS final @@ -287,8 +291,7 @@ ENV LD_PRELOAD="/usr/local/lib/libjemalloc.so" \ MOVIEPILOT_DOCKER_KEEPALIVE_ON_FAILURE="true" # 引入支持 amr 编码的静态 ffmpeg -COPY --from=ffmpeg /ffmpeg /usr/local/bin/ -COPY --from=ffmpeg /ffprobe /usr/local/bin/ +COPY --from=ffmpeg /ffmpeg /ffprobe /usr/local/bin/ COPY --from=rclone /usr/local/bin/rclone /usr/bin/rclone # python 环境 @@ -306,12 +309,7 @@ RUN playwright install-deps chromium \ /var/tmp/* # 准备容器控制面和运行目录 -COPY --from=prepare_control /bundle/entrypoint.sh /entrypoint.sh -COPY --from=prepare_control /bundle/control /usr/local/lib/moviepilot/control -COPY --from=prepare_control /bundle/nginx/common.conf /etc/nginx/common.conf -COPY --from=prepare_control /bundle/nginx/nginx.template.conf /etc/nginx/nginx.template.conf -COPY --from=prepare_control /bundle/nginx/docker_http_proxy.conf /etc/nginx/docker_http_proxy.conf -COPY --from=prepare_control /bundle/bin/moviepilot /usr/local/bin/moviepilot +COPY --link --from=prepare_control /bundle/rootfs/ / RUN mkdir -p ${HOME} \ && groupadd -r moviepilot -g 918 \ diff --git a/tests/test_docker_bootstrap.py b/tests/test_docker_bootstrap.py index c81038b10..80fb9f083 100644 --- a/tests/test_docker_bootstrap.py +++ b/tests/test_docker_bootstrap.py @@ -42,18 +42,22 @@ def test_dockerfile_control_bundle_build_checks_fail_closed() -> None: assert "requirements.in" not in dockerfile assert "${VENV_PATH}/bin/pip" not in dockerfile assert "FROM prepare_payload AS prepare_control" in dockerfile - assert "-exec cp -f -t /bundle/control {} +" in dockerfile - assert "bash -n /bundle/entrypoint.sh" in dockerfile assert ( - "COPY --from=prepare_control /bundle/control " - "/usr/local/lib/moviepilot/control" in dockerfile + "-exec cp -f -t /bundle/rootfs/usr/local/lib/moviepilot/control {} +" + in dockerfile + ) + assert "bash -n /bundle/rootfs/entrypoint.sh" in dockerfile + assert ( + "COPY --link --from=prepare_control /bundle/rootfs/ /" in dockerfile ) assert 'ENTRYPOINT [ "/usr/bin/tini", "-g", "--", "/entrypoint.sh" ]' in dockerfile assert "CMD /usr/bin/curl -fsS" in dockerfile assert '"http://127.0.0.1:${PORT:-3001}/health/ready"' in dockerfile assert "system/global?token=moviepilot" not in dockerfile assert ( - 'for control_script in /bundle/control/*.sh; do bash -n "${control_script}" || exit 1; done' + "for control_script in " + '/bundle/rootfs/usr/local/lib/moviepilot/control/*.sh; do ' + 'bash -n "${control_script}" || exit 1; done' in dockerfile ) diff --git a/tests/test_docker_payload_contract.py b/tests/test_docker_payload_contract.py index 722567589..6ded8a150 100644 --- a/tests/test_docker_payload_contract.py +++ b/tests/test_docker_payload_contract.py @@ -75,7 +75,10 @@ def test_dockerfile_assigns_each_payload_to_an_independent_stage() -> None: assert positions == sorted(positions) for copy in copies: assert final.count(copy) == 1 - assert "COPY --from=prepare_control /bundle/entrypoint.sh /entrypoint.sh" in final + assert "COPY --link --from=prepare_control /bundle/rootfs/ /" in final + assert final.count("--from=prepare_control ") == 1 + assert "COPY --from=ffmpeg /ffmpeg /ffprobe /usr/local/bin/" in final + assert final.count("COPY --from=ffmpeg ") == 1 assert "RUN rm -rf /app/frontend-dist" in dockerfile