From 8e88a73d91aae75c4bc31b5701b25c0ac6e74202 Mon Sep 17 00:00:00 2001 From: Michael SCHAL Date: Fri, 21 Aug 2026 21:35:42 +0200 Subject: [PATCH] =?UTF-8?q?Image=20:=20le=20build=20CUDA=20=C3=A9tait=20tu?= =?UTF-8?q?=C3=A9=20par=20l'OOM,=20pas=20en=20=C3=A9chec=20de=20compilatio?= =?UTF-8?q?n?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Le premier build de l'image avec whisper CUDA a échoué après huit minutes sans écrire une seule ligne d'erreur : le journal s'arrête à 92 % de la compilation, puis « Complete job ». Un compilateur qui échoue dit pourquoi ; un compilateur tué ne dit rien. Ces 92 % étaient atteints en TREIZE secondes. Ce ne sont pas des compilations terminées, ce sont des nvcc lancés simultanément. « cmake --build -j » sans nombre autorise chez Make un parallélisme illimité. ggml-cuda compte environ 200 fichiers d'instanciation de gabarits et chaque nvcc réclame 1 à 2 Go : le runner (16 Go) mourait d'un OOM. L'étape CPU y survivait — peu de fichiers, compilation légère — ce qui a rendu le piège invisible jusqu'à l'arrivée de CUDA. - -j"$(nproc)" sur les deux étapes, et un test le vérifie désormais : cette faute est indétectable à la lecture et ne se manifeste qu'en CI. - Le runner libère dotnet, android et ghc avant de bâtir. L'empilement nvidia/cuda-devel + runtime CUDA + Chromium approche les 14 Go disponibles ; cette part-là reste une hypothèse, mais elle coûte une minute à écarter. - whisper-server CUDA est strippé : les symboles de débogage d'un binaire ggml-cuda pèsent plusieurs centaines de mégaoctets dans l'image finale. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01W3ewMAsXhkw9RY11kb9Dc9 --- .github/workflows/docker-publish.yml | 11 +++++++++++ Dockerfile | 12 ++++++++++-- internal/loki/web_transcribe_test.go | 13 +++++++++++++ 3 files changed, 34 insertions(+), 2 deletions(-) diff --git a/.github/workflows/docker-publish.yml b/.github/workflows/docker-publish.yml index 228e87c..c1717cb 100644 --- a/.github/workflows/docker-publish.yml +++ b/.github/workflows/docker-publish.yml @@ -16,6 +16,17 @@ jobs: - name: Checkout uses: actions/checkout@v4 + # Depuis que l'image compile whisper.cpp avec CUDA, le build empile + # nvidia/cuda:*-devel (~6 Go), l'image runtime CUDA de llama.cpp (~4 Go) + # et Chromium/Playwright (~1 Go) — pour ~14 Go libres sur un runner neuf. + # Ces trois répertoires ne servent à rien ici et rendent une dizaine de + # gigaoctets en moins d'une minute. + - name: Libérer de l'espace disque + run: | + df -h / + sudo rm -rf /usr/share/dotnet /usr/local/lib/android /opt/ghc + df -h / + - name: Setup Buildx uses: docker/setup-buildx-action@v3 diff --git a/Dockerfile b/Dockerfile index 4299b37..307695a 100644 --- a/Dockerfile +++ b/Dockerfile @@ -64,11 +64,18 @@ RUN apt-get update && apt-get install -y --no-install-recommends \ && rm -rf /var/lib/apt/lists/* RUN git clone --depth 1 https://github.com/ggml-org/whisper.cpp /w \ && cmake -S /w -B /w/build ${WHISPER_CMAKE_FLAGS} \ - && cmake --build /w/build -j --target whisper-server + && cmake --build /w/build -j"$(nproc)" --target whisper-server # Version CUDA. La MAJEURE de CUDA doit correspondre à celle de l'image # runtime : le binaire est lié dynamiquement à libcudart, fournie par # LLAMACPP_IMAGE et non par cette étape. +# +# ⚠️ -j"$(nproc)" et JAMAIS -j nu. Avec Make, « -j » sans nombre autorise un +# parallélisme ILLIMITÉ. ggml-cuda compte ~200 fichiers d'instanciation de +# gabarits, et chaque nvcc réclame 1 à 2 Go : le runner GitHub (16 Go) était +# tué par l'OOM après avoir lancé 92 % des compilations en treize secondes, +# sans écrire la moindre ligne d'erreur. L'étape CPU y survivait — peu de +# fichiers, compilation légère — ce qui rendait le piège invisible jusqu'ici. FROM nvidia/cuda:12.4.1-devel-ubuntu22.04 AS whisperbuild-cuda ARG WHISPER_CMAKE_FLAGS RUN apt-get update && apt-get install -y --no-install-recommends \ @@ -76,7 +83,8 @@ RUN apt-get update && apt-get install -y --no-install-recommends \ && rm -rf /var/lib/apt/lists/* RUN git clone --depth 1 https://github.com/ggml-org/whisper.cpp /w \ && cmake -S /w -B /w/build ${WHISPER_CMAKE_FLAGS} -DGGML_CUDA=ON \ - && cmake --build /w/build -j --target whisper-server + && cmake --build /w/build -j"$(nproc)" --target whisper-server \ + && strip /w/build/bin/whisper-server # ── Étape 2 : runtime sur l'image serveur CUDA officielle ─────────────── FROM ${LLAMACPP_IMAGE} AS runtime diff --git a/internal/loki/web_transcribe_test.go b/internal/loki/web_transcribe_test.go index d4219f0..dd319ee 100644 --- a/internal/loki/web_transcribe_test.go +++ b/internal/loki/web_transcribe_test.go @@ -2,6 +2,7 @@ package loki import ( "os" + "regexp" "strings" "testing" ) @@ -80,6 +81,18 @@ func TestDockerfileWhisperNonNatif(t *testing.T) { if !strings.Contains(txt, "whisper-server") { t.Errorf("étape %s : ne construit pas la cible whisper-server", nom) } + // « -j » nu autorise un parallélisme ILLIMITÉ chez Make. Sur l'étape + // CUDA (~200 nvcc à 1-2 Go pièce), le runner GitHub était tué par + // l'OOM sans écrire une ligne d'erreur. L'étape CPU y survivait, ce + // qui rendait le piège invisible. + for _, ligne := range strings.Split(txt, "\n") { + if !strings.Contains(ligne, "cmake --build") { + continue + } + if regexp.MustCompile(`-j(\s|$|\\)`).MatchString(ligne) { + t.Errorf("étape %s : « cmake --build -j » sans nombre — parallélisme illimité, le runner sera tué par l'OOM. Utiliser -j\"$(nproc)\".", nom) + } + } } }