From 0c766db63dc38c2a8a1e94a157964c72ba25f525 Mon Sep 17 00:00:00 2001 From: ReSerendipity Date: Mon, 21 Sep 2026 22:34:53 +0800 Subject: [PATCH] =?UTF-8?q?ci(docker):=20=E4=B8=B4=E6=97=B6=E6=8E=A2?= =?UTF-8?q?=E9=92=88=E4=BD=9C=E4=B8=9A=EF=BC=8C=E7=94=A8=E6=9D=A5=E5=AE=9A?= =?UTF-8?q?=E4=BD=8D=E9=95=9C=E5=83=8F=20apt=20=E5=B1=82=E7=9A=84=20update?= =?UTF-8?q?-alternatives=20=E6=8A=A5=E9=94=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit main 的 `Build & Scan Image` 与 `Boot hardened container & probe` 从 12:19 起确定性失败在 `apt-get` 阶段:`update-alternatives: error: alternative path /usr/share/man/man7/bash-builtins.7.gz doesn't exist`(重试两次一模一样,与 PR 内容无关)。 不在 Dockerfile 上盲改的理由有两条:① 本机没有 docker daemon(只有 CLI,也没有 colima/podman),改完无法自验;② `docker-build.yml` 一轮 20+ 分钟(25 GB 上下文 + Trivy), 拿它当试错循环既慢又把两条门禁反复刷红。 所以先花几分钟跑这个探针:在托管 runner 的 container job 里用**同一个基础镜像** (nvidia/cuda:12.1.0-runtime-ubuntu22.04),把 Dockerfile 的 apt 层拆成 A/B/C 三段逐条执行, 失败时 dump 出:谁维护这条 alternative、bash/man-db/manpages 的 dpkg 状态、 /usr/share/man 现状、`apt-get -s upgrade` 会动到谁、相关 postinst 里有没有 update-alternatives。 主要嫌疑是 runtime 段那句 `apt-get upgrade -y`(它会把 bash/man-db/manpages 一起升上来, 而 `--no-install-recommends` 又可能没装 man-db 的配套),三段拆开就能定位。 定位完成后删掉本文件 —— 它是一次性探针,不是常开门禁。 Signed-off-by: ReSerendipity --- .github/workflows/_docker-apt-probe.yml | 72 +++++++++++++++++++++++++ 1 file changed, 72 insertions(+) create mode 100644 .github/workflows/_docker-apt-probe.yml diff --git a/.github/workflows/_docker-apt-probe.yml b/.github/workflows/_docker-apt-probe.yml new file mode 100644 index 0000000..aafffca --- /dev/null +++ b/.github/workflows/_docker-apt-probe.yml @@ -0,0 +1,72 @@ +# 临时诊断作业:定位 Dockerfile 里 apt 层报的 +# `update-alternatives: error: alternative path /usr/share/man/man7/bash-builtins.7.gz doesn't exist` +# 为什么单独开一个作业而不是直接改 Dockerfile 猜: +# - 本机没有 docker daemon(只有 CLI,也没 colima/podman),改完无法自验; +# - `docker-build.yml` 的完整构建一轮要 20+ 分钟(25 GB 上下文 + Trivy 扫描), +# 用它当"试错循环"既慢又会把两条门禁反复刷红; +# - 这个作业只在托管 runner 的 container job 里拉同一个基础镜像、逐条跑 Dockerfile 的 apt 命令, +# 几分钟出结论,并顺手把 dpkg/man-db 状态打出来。 +# 定位完成后本文件会被删除(它是一次性探针,不是常开门禁)。 +name: Docker apt probe (temporary) + +on: + pull_request: + branches: [main] + paths: + - ".github/workflows/_docker-apt-probe.yml" + - "Dockerfile" + workflow_dispatch: + +permissions: + contents: read + +jobs: + probe: + name: Reproduce apt layers from the Dockerfile + runs-on: ubuntu-latest + timeout-minutes: 20 + container: + image: nvidia/cuda:12.1.0-runtime-ubuntu22.04 + env: + DEBIAN_FRONTEND: noninteractive + steps: + - name: Base image identity + run: | + . /etc/os-release + echo "PRETTY_NAME=$PRETTY_NAME" + dpkg --print-architecture + + - name: "Layer A = Dockerfile builder 第一段(software-properties-common git git-lfs ffmpeg ca-certificates)" + run: | + apt-get update + apt-get install -y --no-install-recommends \ + software-properties-common git git-lfs ffmpeg ca-certificates + + - name: "Layer B = Dockerfile runtime 的 apt-get upgrade -y(头号嫌疑:它会把 bash/man-db/manpages 一起升上来)" + run: | + apt-get update + apt-get upgrade -y + + - name: "Layer C = deadsnakes PPA + python3.12/-venv(与 Dockerfile 同序)" + run: | + add-apt-repository -y ppa:deadsnakes/ppa + apt-get update + apt-get install -y --no-install-recommends python3.12 python3.12-venv + + - name: 失败时 dump 诊断信息 + if: failure() + run: | + set +e + echo "=== 谁在维护这个 alternative ===" + grep -rls "bash-builtins" /var/lib/dpkg/info/ /var/lib/dpkg/alternatives/ 2>/dev/null | head -10 + echo "=== dpkg 里 bash / man-db / manpages 的状态 ===" + dpkg -l | grep -E "^(ii|iU|rc|hi)? *(bash|man-db|manpages|manpages-dev|debconf|libc-bin) " | head -12 + echo "=== /usr/share/man 现状 ===" + ls -l /usr/share/man/man7/ 2>/dev/null | head -8 + echo "=== apt-get -s upgrade 会动谁 ===" + apt-get -s upgrade | head -25 + echo "=== 相关包的 postinst 是否调 update-alternatives ===" + for f in /var/lib/dpkg/info/bash.postinst /var/lib/dpkg/info/man-db.postinst; do + echo "--- $f"; test -f "$f" && grep -n "update-alternatives\|man" "$f" | head -6 + done + exit 0