From e177d54e39178d269dad3ec78a7358731e03458e Mon Sep 17 00:00:00 2001 From: Michael SCHAL Date: Sat, 22 Aug 2026 04:12:52 +0200 Subject: [PATCH] =?UTF-8?q?Image=20:=20l'ARG=20des=20drapeaux=20whisper=20?= =?UTF-8?q?=C3=A9tait=20hors=20de=20port=C3=A9e=20=E2=80=94=20cmake=20tour?= =?UTF-8?q?nait=20=C3=A0=20nu?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Le build de 51 minutes a échoué au lien final : « libcuda.so.1 not found », références cuMem* non résolues, et une libggml-cuda.so PARTAGÉE alors que BUILD_SHARED_LIBS=OFF est censé être passé. Ce dernier détail était le vrai indice : les drapeaux n'atteignaient pas cmake du tout. ARG WHISPER_CMAKE_FLAGS="…" était déclaré entre deux étapes. Règle Docker : un ARG posé après un FROM appartient à l'étape où il apparaît ; les « ARG » nus des étapes whisperbuild-* héritent, eux, du scope GLOBAL — où rien n'était défini. Valeur vide, cmake sans aucun drapeau : ggml en bibliothèques partagées (échec de lien contre les stubs du pilote), et surtout -march=native — le SIGILL de la PR #18 revenu en silence sur les deux binaires. Les tests du Dockerfile n'y voyaient rien : ils vérifiaient le texte, pas les règles de portée de Docker. - L'ARG remonte avant le premier FROM, à côté de LLAMACPP_IMAGE. - Chaque étape vérifie désormais SON binaire : un ldd qui montre libggml ou libwhisper en dynamique fait échouer le build sur-le-champ, au lieu de laisser partir un binaire qui ne trouvera pas ses .so dans l'image finale. - TestDockerfileArgFlagsGlobal verrouille la position de l'ARG, ancré en début de ligne — une première version se laissait berner par une occurrence en commentaire, sa contre-épreuve l'a montré. libcuda.so.1 reste une dépendance dynamique normale du binaire CUDA : c'est le pilote, injecté à l'exécution par le NVIDIA Container Toolkit, et l'édition de liens la résout via les stubs du toolkit. Co-Authored-By: Claude Fable 5 Claude-Session: https://claude.ai/code/session_01W3ewMAsXhkw9RY11kb9Dc9 --- Dockerfile | 30 +++++++++++++++++++++------- internal/loki/web_transcribe_test.go | 25 +++++++++++++++++++++++ 2 files changed, 48 insertions(+), 7 deletions(-) diff --git a/Dockerfile b/Dockerfile index 53ad936..aa1c93a 100644 --- a/Dockerfile +++ b/Dockerfile @@ -14,6 +14,18 @@ # Déclaré avant le premier FROM : requis pour être utilisable dans un FROM. ARG LLAMACPP_IMAGE=ghcr.io/ggml-org/llama.cpp:server-cuda +# ⚠️ AVANT le premier FROM, obligatoirement. Un ARG posé entre deux étapes +# appartient à l'étape où il apparaît ; les « ARG WHISPER_CMAKE_FLAGS » nus des +# étapes whisperbuild-* héritent du scope GLOBAL — c'est-à-dire d'ici. Placé +# plus bas, ils héritaient d'une valeur VIDE : cmake tournait sans aucun +# drapeau, ggml se construisait en bibliothèques partagées (échec de lien sur +# libcuda.so.1) et surtout en -march=native — le SIGILL de la PR #18 revenu en +# silence. Un test (TestDockerfileArgFlagsGlobal) verrouille cette position. +ARG WHISPER_CMAKE_FLAGS="-DCMAKE_BUILD_TYPE=Release -DBUILD_SHARED_LIBS=OFF \ + -DWHISPER_BUILD_TESTS=OFF -DWHISPER_BUILD_EXAMPLES=ON \ + -DGGML_NATIVE=OFF -DGGML_AVX512=OFF -DGGML_AMX_TILE=OFF \ + -DGGML_AMX_INT8=OFF -DGGML_AMX_BF16=OFF" + # ── Étape 1 : binaire Go loki ─────────────────────────────────────────── FROM golang:1.25 AS gobuild WORKDIR /src @@ -50,11 +62,6 @@ RUN GOBIN=/out GOTOOLCHAIN=auto go install golang.org/x/tools/gopls@latest # pleine transcription : « AMX is not ready to be used! », puis plus rien, # l'interface affichant un échec sans raison. OFF retombe sur la ligne de base # AVX2/FMA/F16C de ggml, présente sur tout x86-64 depuis 2013. -ARG WHISPER_CMAKE_FLAGS="-DCMAKE_BUILD_TYPE=Release -DBUILD_SHARED_LIBS=OFF \ - -DWHISPER_BUILD_TESTS=OFF -DWHISPER_BUILD_EXAMPLES=ON \ - -DGGML_NATIVE=OFF -DGGML_AVX512=OFF -DGGML_AMX_TILE=OFF \ - -DGGML_AMX_INT8=OFF -DGGML_AMX_BF16=OFF" - # Ubuntu 22.04 : glibc plus ancienne que l'image runtime, donc compatible quoi # qu'elle embarque. FROM ubuntu:22.04 AS whisperbuild-cpu @@ -62,9 +69,13 @@ ARG WHISPER_CMAKE_FLAGS RUN apt-get update && apt-get install -y --no-install-recommends \ build-essential cmake git ca-certificates \ && rm -rf /var/lib/apt/lists/* +# Le grep final vérifie que ggml est bien lié EN STATIQUE : l'image finale ne +# reçoit que le binaire, une .so ggml manquante ne se verrait qu'au premier +# clic sur le micro. Des libs partagées ici = drapeaux non transmis. 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"$(nproc)" --target whisper-server + && cmake --build /w/build -j"$(nproc)" --target whisper-server \ + && ( ! ldd /w/build/bin/whisper-server | grep -E "libggml|libwhisper" ) # Version CUDA : la MÊME que celle de l'image amont (llama.cpp bâtit avec # CUDA 12.8.1 sur Ubuntu 24.04). Le binaire est lié dynamiquement à libcudart, @@ -93,11 +104,16 @@ ARG CUDA_ARCHS="75;86;89;120" RUN apt-get update && apt-get install -y --no-install-recommends \ build-essential cmake git ca-certificates \ && rm -rf /var/lib/apt/lists/* +# Même vérification statique que l'étape CPU. libcuda.so.1 (le PILOTE) reste +# elle une dépendance dynamique normale : absente du runner de build, injectée +# à l'exécution par le NVIDIA Container Toolkit — l'édition de liens la +# résout via les stubs du toolkit. 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 \ -DCMAKE_CUDA_ARCHITECTURES="${CUDA_ARCHS}" \ && cmake --build /w/build -j"$(nproc)" --target whisper-server \ - && strip /w/build/bin/whisper-server + && strip /w/build/bin/whisper-server \ + && ( ! ldd /w/build/bin/whisper-server | grep -E "libggml|libwhisper" ) # ── É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 f2170c8..78b2942 100644 --- a/internal/loki/web_transcribe_test.go +++ b/internal/loki/web_transcribe_test.go @@ -155,6 +155,31 @@ func TestDockerfileCudaAssezRecent(t *testing.T) { } } +// L'ARG qui porte les drapeaux doit être déclaré AVANT le premier FROM. Un ARG +// posé entre deux étapes appartient à l'étape où il apparaît ; les « ARG » nus +// des étapes whisperbuild-* héritent alors du scope global — vide. cmake a +// tourné sans aucun drapeau : ggml en bibliothèques partagées (échec de lien), +// et -march=native — le SIGILL de la PR #18 revenu en silence. 51 minutes de +// build pour le découvrir. +func TestDockerfileArgFlagsGlobal(t *testing.T) { + b, err := os.ReadFile("../../Dockerfile") + if err != nil { + t.Fatalf("lecture du Dockerfile : %v", err) + } + src := string(b) + // Ancré en début de ligne : une occurrence dans un commentaire ne compte + // pas — c'est précisément ainsi qu'une première version de ce test s'est + // laissée berner par sa propre contre-épreuve. + argLoc := regexp.MustCompile(`(?m)^ARG WHISPER_CMAKE_FLAGS=`).FindStringIndex(src) + fromLoc := regexp.MustCompile(`(?m)^FROM `).FindStringIndex(src) + if argLoc == nil { + t.Fatal("ARG WHISPER_CMAKE_FLAGS= introuvable dans le Dockerfile") + } + if fromLoc != nil && argLoc[0] > fromLoc[0] { + t.Error("ARG WHISPER_CMAKE_FLAGS= est déclaré APRÈS un FROM : les étapes whisperbuild-* hériteront d'une valeur vide et cmake tournera sans aucun drapeau") + } +} + func clefs(m map[string]string) []string { out := make([]string, 0, len(m)) for k := range m {