mirror of
https://github.com/R0m1k3/Loki.git
synced 2026-10-13 18:28:03 +02:00
L'image embarque loki-rs (étape Alpine, binaire musl statique) et l'entrypoint en fait la porte d'entrée : `loki-rs serve` sur le port public, le Go derrière lui sur 127.0.0.1:8091. LOKI_FACADE=0 revient à l'ancien schéma si la façade pose problème. Au démarrage, le loki-rs de l'image installe la dernière release « rolling » dans /data/bin/<build>/ ; l'entrypoint prend ensuite loki et loki-rs du même dossier (which-bin), et un lien dans /usr/local/sbin fait que `docker exec loki loki …` vise lui aussi la version installée. C'est toujours le loki-rs de l'image qui met à jour : un binaire publié défectueux ne peut pas bloquer le correctif suivant. CI : job Rust (fmt, clippy -D warnings, tests). Publication : la release « rolling » part à chaque push sur main avec le même numéro de run que l'image (LOKI_BUILD) ; IMAGE_ABI, source unique, empêche d'installer des binaires qui exigent une image plus récente. manifest.json est envoyé en dernier. Message corrigé quand l'image est plus récente que la release : rien à tirer dans ce cas. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
182 lines
9.7 KiB
Docker
182 lines
9.7 KiB
Docker
# ── Loki — image GPU (fork d'AJEAN, https://github.com/nathaninline/ajean)
|
|
#
|
|
# Deux étapes seulement :
|
|
# 1. compilation du binaire Go `loki` (UI embarquée via go:embed) ;
|
|
# 2. runtime = l'image serveur CUDA OFFICIELLE de llama.cpp — llama-server
|
|
# y est précompilé et maintenu par l'équipe amont (base nvidia/cuda
|
|
# runtime, backends .so dans /app, architectures GPU courantes).
|
|
# Aucune compilation CUDA ici : le build complet prend quelques minutes.
|
|
#
|
|
# Épingler une version : --build-arg LLAMACPP_IMAGE=ghcr.io/ggml-org/llama.cpp:server-cuda-b10423
|
|
# Variante CPU (test sans GPU) : --build-arg LLAMACPP_IMAGE=ghcr.io/ggml-org/llama.cpp:server
|
|
# Pré-requis d'exécution GPU : NVIDIA Container Toolkit sur l'hôte.
|
|
|
|
# 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 SHERPA_* » nus de l'étape
|
|
# asrfetch héritent du scope GLOBAL — c'est-à-dire d'ici. Placés plus bas, ils
|
|
# hériteraient d'une valeur VIDE, et l'étape téléchargerait une URL tronquée.
|
|
# Un test (TestDockerfileArgFlagsGlobal) verrouille cette position.
|
|
#
|
|
# La version est ÉPINGLÉE, et l'empreinte avec : le binaire de dictée est
|
|
# exécuté sur la machine de l'utilisateur, il ne se prend pas « au dernier ».
|
|
ARG SHERPA_ONNX_VERSION=v1.13.7
|
|
ARG SHERPA_ONNX_SHA256=2aa3965b37aa235e56aa3ebbb94942c20a23960059a006b4442b4bf893967983
|
|
|
|
# ── Étape 1 : binaire Go loki ───────────────────────────────────────────
|
|
FROM golang:1.25 AS gobuild
|
|
WORKDIR /src
|
|
COPY go.mod go.sum ./
|
|
RUN go mod download
|
|
COPY cmd/ cmd/
|
|
COPY internal/ internal/
|
|
COPY tools/ tools/
|
|
RUN CGO_ENABLED=0 go build -trimpath -ldflags "-s -w" -o /out/loki ./cmd/loki
|
|
# gopls : serveur LSP Go, consommé par le vérificateur de code du mode code
|
|
# (lsp.go). Compilé ici parce que l'image finale n'a pas de toolchain Go.
|
|
# GOTOOLCHAIN=auto : gopls@latest exige régulièrement un Go plus récent que
|
|
# celui de l'image (v0.23 veut ≥ 1.26 quand l'image est en 1.25) ; en local,
|
|
# l'installation échouait net. Auto télécharge le toolchain requis — coût
|
|
# limité à cette étape de build.
|
|
RUN GOBIN=/out GOTOOLCHAIN=auto go install golang.org/x/tools/gopls@latest
|
|
|
|
# ── Étape 1 ter : façade Rust loki-rs ───────────────────────────────────
|
|
# Premier lot de la migration Go → Rust (rust/, voir docs/superpowers/specs/).
|
|
# Alpine = musl : binaire STATIQUE, indépendant de la glibc de l'image runtime —
|
|
# le même que celui publié dans la release « rolling » et installé à chaud
|
|
# dans /data/bin par `loki-rs self-update`.
|
|
FROM rust:1-alpine AS rustbuild
|
|
RUN apk add --no-cache musl-dev
|
|
WORKDIR /src
|
|
COPY rust/ ./
|
|
RUN cargo build --release --locked && mkdir -p /out && cp target/release/loki-rs /out/loki-rs
|
|
|
|
# ── Étape 1 bis : serveur de dictée (Parakeet via sherpa-onnx) ──────────
|
|
# On TÉLÉCHARGE un binaire préconstruit, on ne compile plus rien. C'est le
|
|
# changement de fond par rapport à whisper.cpp, qu'il remplace : deux étapes de
|
|
# compilation (dont une CUDA de ~200 fichiers nvcc, tuée par l'OOM du runner
|
|
# plus d'une fois) laissent place à une extraction de 35 Mo.
|
|
#
|
|
# Le build « static-no-tts » lie sherpa-onnx ET onnxruntime en statique : rien à
|
|
# fournir côté image en dehors de la libc et de libstdc++, déjà présentes. Il
|
|
# n'existe qu'en variante CPU — c'est assumé : un modèle de 0,6 B en int8 sur
|
|
# des tranches de quelques secondes n'a pas besoin de la carte, qui reste au
|
|
# moteur de chat. La variante CUDA existe mais tirerait onnxruntime-gpu et
|
|
# cuDNN dans l'image, pour un gain sans intérêt à cette taille de tranche.
|
|
#
|
|
# L'empreinte est vérifiée : le binaire s'exécute sur la machine de
|
|
# l'utilisateur, une release remplacée en amont ne doit pas passer en silence.
|
|
FROM debian:12-slim AS asrfetch
|
|
ARG SHERPA_ONNX_VERSION
|
|
ARG SHERPA_ONNX_SHA256
|
|
RUN apt-get update && apt-get install -y --no-install-recommends \
|
|
curl ca-certificates bzip2 \
|
|
&& rm -rf /var/lib/apt/lists/*
|
|
RUN set -eu; \
|
|
nom="sherpa-onnx-${SHERPA_ONNX_VERSION}-linux-x64-static-no-tts"; \
|
|
curl -fsSL -o /tmp/sherpa.tar.bz2 \
|
|
"https://github.com/k2-fsa/sherpa-onnx/releases/download/${SHERPA_ONNX_VERSION}/${nom}.tar.bz2"; \
|
|
echo "${SHERPA_ONNX_SHA256} /tmp/sherpa.tar.bz2" | sha256sum -c -; \
|
|
mkdir -p /out; \
|
|
tar -xjf /tmp/sherpa.tar.bz2 -C /out --strip-components=2 \
|
|
"${nom}/bin/sherpa-onnx-offline-websocket-server"; \
|
|
chmod +x /out/sherpa-onnx-offline-websocket-server; \
|
|
/out/sherpa-onnx-offline-websocket-server --help >/dev/null 2>&1 || true
|
|
|
|
# ── Étape 2 : runtime sur l'image serveur CUDA officielle ───────────────
|
|
FROM ${LLAMACPP_IMAGE} AS runtime
|
|
|
|
# Marqueur de build (sha court), injecté par le workflow GitHub.
|
|
ARG LOKI_VERSION=dev
|
|
LABEL org.opencontainers.image.revision="${LOKI_VERSION}" \
|
|
org.opencontainers.image.source="https://github.com/R0m1k3/Loki" \
|
|
org.opencontainers.image.description="Loki — fork conteneurisé d'AJEAN (github.com/nathaninline/ajean, MIT)"
|
|
|
|
# git pour les outils de l'agent ; nodejs/npm pour les serveurs MCP lancés
|
|
# via npx ; tini en PID 1. curl et libgomp1 sont déjà dans l'image amont.
|
|
# Node 22 depuis NodeSource et non depuis Ubuntu (qui livre Node 18) : Playwright
|
|
# et la plupart des serveurs MCP réclament Node ≥ 20.
|
|
# python3 + python3-venv : l'installeur et le serveur du moteur Strata
|
|
# (backend_strata.go) sont en Python ≥ 3.10 et créent leur propre .venv dans
|
|
# /data/strata. Le moteur et le modèle ne sont PAS dans l'image : téléchargés
|
|
# à la demande depuis Paramètres → Moteur.
|
|
RUN apt-get update && apt-get install -y --no-install-recommends \
|
|
git tini ca-certificates curl gnupg python3 python3-venv \
|
|
&& curl -fsSL https://deb.nodesource.com/setup_22.x | bash - \
|
|
&& apt-get install -y --no-install-recommends nodejs \
|
|
&& rm -rf /var/lib/apt/lists/*
|
|
|
|
# Serveurs LSP du mode code (lsp.go) : TypeScript/JavaScript et Python via
|
|
# npm, Go via gopls compilé à l'étape 1. Un serveur absent désactive juste son
|
|
# langage — mais autant livrer l'image complète.
|
|
RUN npm install -g --no-audit --no-fund typescript typescript-language-server pyright && npm cache clean --force
|
|
|
|
# uv/uvx : lanceur des serveurs MCP écrits en Python (mcp-server-git, -fetch,
|
|
# -time, sqlite, docker…). Sans lui, une bonne partie du catalogue embarqué
|
|
# s'affiche mais ne peut pas démarrer. Deux binaires statiques, ~35 Mo.
|
|
COPY --from=ghcr.io/astral-sh/uv:latest /uv /uvx /usr/local/bin/
|
|
|
|
# Playwright + Chromium : l'outil web_screenshot capture une page RENDUE
|
|
# (JavaScript exécuté), ce que le moteur web intégré ne sait pas faire.
|
|
# Coût assumé : ~500 Mo à 1 Go d'image. Mettre PLAYWRIGHT=0 pour bâtir une image
|
|
# légère — l'outil se désactive alors tout seul (le binaire absent est détecté).
|
|
ARG PLAYWRIGHT=1
|
|
ARG PLAYWRIGHT_VERSION=latest
|
|
ENV PLAYWRIGHT_BROWSERS_PATH=/opt/pw-browsers
|
|
# Les polices sont INDISPENSABLES : sans elles, Chromium rend les pages sans
|
|
# AUCUN texte — boutons et titres sortent vides, seuls les images et les aplats
|
|
# apparaissent. Noto couvre l'essentiel des écritures, Liberation fournit les
|
|
# substituts d'Arial/Times que réclament la plupart des sites.
|
|
RUN if [ "$PLAYWRIGHT" = "1" ]; then \
|
|
npm i -g playwright@${PLAYWRIGHT_VERSION} \
|
|
&& playwright install --with-deps chromium \
|
|
&& apt-get update \
|
|
&& apt-get install -y --no-install-recommends \
|
|
fonts-liberation fonts-dejavu-core fonts-noto-core \
|
|
fonts-noto-cjk fonts-noto-color-emoji \
|
|
&& fc-cache -f \
|
|
&& rm -rf /root/.npm /var/lib/apt/lists/* ; \
|
|
fi
|
|
|
|
COPY --from=gobuild /out/loki /usr/local/bin/loki
|
|
COPY --from=gobuild /out/gopls /usr/local/bin/gopls
|
|
COPY --from=rustbuild /out/loki-rs /usr/local/bin/loki-rs
|
|
# Dictée vocale (POST /api/transcribe). Le modèle (~500 Mo) n'est PAS dans
|
|
# l'image : téléchargé à la demande dans /data/asr/.
|
|
COPY --from=asrfetch /out/sherpa-onnx-offline-websocket-server /usr/local/bin/sherpa-onnx-offline-websocket-server
|
|
COPY docker-entrypoint.sh /usr/local/bin/docker-entrypoint.sh
|
|
RUN chmod +x /usr/local/bin/docker-entrypoint.sh && mkdir -p /data /models
|
|
|
|
# llama-server et ses .so vivent dans /app (convention de l'image amont).
|
|
ENV LOKI_CONTAINER=1 \
|
|
LOKI_HOME=/data \
|
|
LOKI_MODEL_DIRS=/models \
|
|
LOKI_ENGINE_BIN=/app/llama-server \
|
|
LOKI_ASR_SERVER=/usr/local/bin/sherpa-onnx-offline-websocket-server \
|
|
LD_LIBRARY_PATH=/app \
|
|
NVIDIA_VISIBLE_DEVICES=all \
|
|
NVIDIA_DRIVER_CAPABILITIES=compute,utility
|
|
|
|
# Mise à jour au démarrage (loki-rs self-update) : IMAGE_ABI dit quelles
|
|
# dépendances d'image les binaires exigent — un binaire publié pour une autre
|
|
# ABI n'est jamais installé. LOKI_BUILD = numéro de run CI de cette image : un
|
|
# binaire de /data/bin n'est pris que s'il est PLUS RÉCENT. Posés ici, en fin
|
|
# d'image, pour ne pas invalider le cache des couches lourdes à chaque build.
|
|
COPY IMAGE_ABI /etc/loki/IMAGE_ABI
|
|
ARG LOKI_BUILD=0
|
|
ENV LOKI_BUILD=${LOKI_BUILD}
|
|
|
|
# 8090 : interface web (seule à exposer). Le moteur (8080) reste interne au
|
|
# conteneur — il n'est pas authentifié par défaut.
|
|
EXPOSE 8090
|
|
|
|
# Remplace le HEALTHCHECK de l'image amont (qui vise le moteur sur :8080).
|
|
HEALTHCHECK --interval=30s --timeout=5s --start-period=20s --retries=3 \
|
|
CMD curl -fsS "http://localhost:${LOKI_WEB_PORT:-8090}/" >/dev/null || exit 1
|
|
|
|
WORKDIR /data
|
|
# Remplace l'ENTRYPOINT de l'image amont (/app/llama-server).
|
|
ENTRYPOINT ["tini", "--", "docker-entrypoint.sh"]
|