feat(localai): Use Docker Registry API for LXC install (no daemon needed)
- Download Docker image layers directly via Registry API - No dockerd or podman daemon required anymore - Same approach as mitmproxy and magicmirror2 packages - All backends included (llama-cpp, whisper, etc.) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
parent
55914b8b3c
commit
1cce649751
@ -1,7 +1,7 @@
|
|||||||
include $(TOPDIR)/rules.mk
|
include $(TOPDIR)/rules.mk
|
||||||
|
|
||||||
PKG_NAME:=secubox-app-localai
|
PKG_NAME:=secubox-app-localai
|
||||||
PKG_RELEASE:=11
|
PKG_RELEASE:=12
|
||||||
PKG_VERSION:=0.1.0
|
PKG_VERSION:=0.1.0
|
||||||
PKG_ARCH:=all
|
PKG_ARCH:=all
|
||||||
PKG_MAINTAINER:=CyberMind Studio <contact@cybermind.fr>
|
PKG_MAINTAINER:=CyberMind Studio <contact@cybermind.fr>
|
||||||
|
|||||||
@ -33,8 +33,8 @@ Service Control:
|
|||||||
Runtimes:
|
Runtimes:
|
||||||
lxc - LXC container with rootfs extracted from Docker image
|
lxc - LXC container with rootfs extracted from Docker image
|
||||||
(includes all backends: llama-cpp, whisper, etc.)
|
(includes all backends: llama-cpp, whisper, etc.)
|
||||||
Falls back to standalone binary if no docker/podman available
|
Downloads via Docker Registry API - no daemon required!
|
||||||
docker - Run Docker container directly
|
docker - Run Docker container directly (requires dockerd)
|
||||||
podman - Run Podman container directly (rootless)
|
podman - Run Podman container directly (rootless)
|
||||||
|
|
||||||
Configuration: /etc/config/localai
|
Configuration: /etc/config/localai
|
||||||
@ -185,21 +185,8 @@ lxc_install() {
|
|||||||
local rootfs="$lxc_path/$CONTAINER_NAME/rootfs"
|
local rootfs="$lxc_path/$CONTAINER_NAME/rootfs"
|
||||||
local config="$lxc_path/$CONTAINER_NAME/config"
|
local config="$lxc_path/$CONTAINER_NAME/config"
|
||||||
|
|
||||||
# Check if we can extract from Docker image (preferred - includes all backends)
|
# Extract Docker image via Registry API (no daemon needed)
|
||||||
local use_docker_extract=0
|
lxc_create_docker_rootfs "$rootfs" || return 1
|
||||||
if command -v podman >/dev/null 2>&1 && runtime_is_working podman; then
|
|
||||||
use_docker_extract=1
|
|
||||||
elif command -v docker >/dev/null 2>&1 && runtime_is_working docker; then
|
|
||||||
use_docker_extract=1
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [ "$use_docker_extract" = "1" ]; then
|
|
||||||
lxc_install_from_docker "$rootfs" || return 1
|
|
||||||
else
|
|
||||||
log_warn "No working Docker/Podman daemon - using standalone binary"
|
|
||||||
log_warn "For full backend support, start Docker: /etc/init.d/dockerd start"
|
|
||||||
lxc_install_standalone "$rootfs" || return 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Create LXC config
|
# Create LXC config
|
||||||
lxc_create_config "$config" "$rootfs"
|
lxc_create_config "$config" "$rootfs"
|
||||||
@ -209,121 +196,79 @@ lxc_install() {
|
|||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
# Check if container runtime daemon is actually working
|
# Extract Docker image via Registry API (no daemon required!)
|
||||||
runtime_is_working() {
|
lxc_create_docker_rootfs() {
|
||||||
local rt="$1"
|
|
||||||
case "$rt" in
|
|
||||||
podman)
|
|
||||||
# Podman is daemonless, just check command works
|
|
||||||
podman info >/dev/null 2>&1
|
|
||||||
;;
|
|
||||||
docker)
|
|
||||||
# Docker needs daemon running
|
|
||||||
docker info >/dev/null 2>&1
|
|
||||||
;;
|
|
||||||
*)
|
|
||||||
return 1
|
|
||||||
;;
|
|
||||||
esac
|
|
||||||
}
|
|
||||||
|
|
||||||
# Extract rootfs from Docker image (includes all backends)
|
|
||||||
lxc_install_from_docker() {
|
|
||||||
local rootfs="$1"
|
local rootfs="$1"
|
||||||
local rt=""
|
local image="localai/localai"
|
||||||
|
local tag="${LOCALAI_VERSION}-ffmpeg"
|
||||||
|
local registry="registry-1.docker.io"
|
||||||
|
local arch
|
||||||
|
|
||||||
# Detect available AND WORKING runtime for extraction
|
# Detect architecture for Docker manifest
|
||||||
if command -v podman >/dev/null 2>&1 && runtime_is_working podman; then
|
case "$(uname -m)" in
|
||||||
rt="podman"
|
x86_64) arch="amd64" ;;
|
||||||
elif command -v docker >/dev/null 2>&1 && runtime_is_working docker; then
|
aarch64) arch="arm64" ;;
|
||||||
rt="docker"
|
armv7l) arch="arm" ;;
|
||||||
else
|
*) arch="amd64" ;;
|
||||||
log_error "Need working podman or docker to extract image"
|
esac
|
||||||
log_error "Docker installed but daemon not running? Start with: /etc/init.d/dockerd start"
|
|
||||||
return 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
log_info "Extracting LocalAI rootfs from Docker image..."
|
log_info "Extracting LocalAI Docker image ($arch)..."
|
||||||
log_info "Image: $docker_image"
|
log_info "Image: $image:$tag"
|
||||||
log_info "This includes ALL backends (llama-cpp, whisper, etc.)"
|
log_info "This includes ALL backends (llama-cpp, whisper, etc.)"
|
||||||
|
|
||||||
# Pull the image
|
|
||||||
log_info "Pulling image (this may take a while)..."
|
|
||||||
if ! $rt pull "$docker_image"; then
|
|
||||||
log_error "Failed to pull image"
|
|
||||||
return 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Create temp container to export
|
|
||||||
local temp_container="localai-extract-$$"
|
|
||||||
log_info "Creating temporary container..."
|
|
||||||
$rt create --name "$temp_container" "$docker_image" >/dev/null 2>&1
|
|
||||||
|
|
||||||
# Export and extract rootfs
|
|
||||||
mkdir -p "$rootfs"
|
mkdir -p "$rootfs"
|
||||||
log_info "Exporting rootfs (2-4GB, please wait)..."
|
|
||||||
|
|
||||||
if $rt export "$temp_container" | tar -xf - -C "$rootfs" 2>/dev/null; then
|
# Get Docker Hub token
|
||||||
log_info "Rootfs extracted successfully"
|
log_info "Authenticating with Docker Hub..."
|
||||||
else
|
local token=$(wget -q -O - "https://auth.docker.io/token?service=registry.docker.io&scope=repository:$image:pull" | jsonfilter -e '@.token')
|
||||||
log_error "Failed to extract rootfs"
|
[ -z "$token" ] && { log_error "Failed to get Docker Hub token"; return 1; }
|
||||||
$rt rm -f "$temp_container" >/dev/null 2>&1
|
|
||||||
return 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Cleanup temp container
|
# Get manifest list (multi-arch)
|
||||||
$rt rm -f "$temp_container" >/dev/null 2>&1
|
log_info "Fetching manifest..."
|
||||||
|
local manifest=$(wget -q -O - --header="Authorization: Bearer $token" \
|
||||||
|
--header="Accept: application/vnd.docker.distribution.manifest.list.v2+json" \
|
||||||
|
"https://$registry/v2/$image/manifests/$tag")
|
||||||
|
|
||||||
# Optionally remove the Docker image to save space
|
# Find digest for our architecture
|
||||||
# $rt rmi "$docker_image" >/dev/null 2>&1
|
local digest=$(echo "$manifest" | jsonfilter -e "@.manifests[@.platform.architecture='$arch'].digest")
|
||||||
|
[ -z "$digest" ] && { log_error "No manifest found for $arch"; return 1; }
|
||||||
|
|
||||||
# Create necessary directories
|
# Get image manifest with layer digests
|
||||||
|
local img_manifest=$(wget -q -O - --header="Authorization: Bearer $token" \
|
||||||
|
--header="Accept: application/vnd.docker.distribution.manifest.v2+json" \
|
||||||
|
"https://$registry/v2/$image/manifests/$digest")
|
||||||
|
|
||||||
|
# Extract layer digests
|
||||||
|
local layers=$(echo "$img_manifest" | jsonfilter -e '@.layers[*].digest')
|
||||||
|
local layer_count=$(echo "$layers" | wc -w)
|
||||||
|
log_info "Downloading $layer_count layers (this will take a while, ~4GB)..."
|
||||||
|
|
||||||
|
local i=0
|
||||||
|
for layer_digest in $layers; do
|
||||||
|
i=$((i + 1))
|
||||||
|
log_info " Layer $i/$layer_count: ${layer_digest:7:12}..."
|
||||||
|
wget -q -O - --header="Authorization: Bearer $token" \
|
||||||
|
"https://$registry/v2/$image/blobs/$layer_digest" | \
|
||||||
|
tar xzf - -C "$rootfs" 2>&1 | grep -v "Cannot change ownership" || true
|
||||||
|
done
|
||||||
|
|
||||||
|
# Configure container
|
||||||
|
echo "nameserver 8.8.8.8" > "$rootfs/etc/resolv.conf"
|
||||||
mkdir -p "$rootfs/models" "$rootfs/build" "$rootfs/tmp"
|
mkdir -p "$rootfs/models" "$rootfs/build" "$rootfs/tmp"
|
||||||
|
|
||||||
# Setup resolv.conf
|
# Ensure /bin/sh exists
|
||||||
echo "nameserver 8.8.8.8" > "$rootfs/etc/resolv.conf"
|
if [ ! -x "$rootfs/bin/sh" ]; then
|
||||||
|
log_warn "/bin/sh not found, attempting to fix..."
|
||||||
|
if [ -x "$rootfs/bin/bash" ]; then
|
||||||
|
ln -sf bash "$rootfs/bin/sh"
|
||||||
|
elif [ -x "$rootfs/bin/dash" ]; then
|
||||||
|
ln -sf dash "$rootfs/bin/sh"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
local rootfs_size=$(du -sh "$rootfs" 2>/dev/null | cut -f1)
|
local rootfs_size=$(du -sh "$rootfs" 2>/dev/null | cut -f1)
|
||||||
log_info "Rootfs size: $rootfs_size"
|
log_info "Rootfs size: $rootfs_size"
|
||||||
log_info "All LocalAI backends are now available!"
|
log_info "LocalAI Docker image extracted successfully"
|
||||||
|
log_info "All backends available: llama-cpp, whisper, stablediffusion, etc."
|
||||||
return 0
|
|
||||||
}
|
|
||||||
|
|
||||||
# Fallback: Download standalone binary (limited backends)
|
|
||||||
lxc_install_standalone() {
|
|
||||||
local rootfs="$1"
|
|
||||||
|
|
||||||
log_warn "No Docker/Podman available - using standalone binary"
|
|
||||||
log_warn "Note: Standalone binary has LIMITED backend support"
|
|
||||||
|
|
||||||
# Create directories
|
|
||||||
mkdir -p "$rootfs/usr/bin" "$rootfs/data" "$rootfs/models" "$rootfs/tmp" "$rootfs/etc"
|
|
||||||
mkdir -p "$rootfs/bin" "$rootfs/lib" "$rootfs/proc" "$rootfs/sys" "$rootfs/dev"
|
|
||||||
|
|
||||||
# Detect architecture (v3.x format: local-ai-v3.10.0-linux-arm64)
|
|
||||||
local arch
|
|
||||||
case "$(uname -m)" in
|
|
||||||
x86_64) arch="linux-amd64" ;;
|
|
||||||
aarch64) arch="linux-arm64" ;;
|
|
||||||
armv7l) arch="linux-arm64" ;;
|
|
||||||
*) arch="linux-amd64" ;;
|
|
||||||
esac
|
|
||||||
|
|
||||||
# Download LocalAI binary (v3.x URL format)
|
|
||||||
local binary_url="https://github.com/mudler/LocalAI/releases/download/${lxc_version}/local-ai-${lxc_version}-${arch}"
|
|
||||||
log_info "Downloading LocalAI $lxc_version for $arch..."
|
|
||||||
log_info "URL: $binary_url"
|
|
||||||
|
|
||||||
if ! wget -q --show-progress -O "$rootfs/usr/bin/local-ai" "$binary_url"; then
|
|
||||||
log_error "Failed to download LocalAI binary"
|
|
||||||
return 1
|
|
||||||
fi
|
|
||||||
chmod +x "$rootfs/usr/bin/local-ai"
|
|
||||||
log_info "Binary downloaded: $(ls -sh "$rootfs/usr/bin/local-ai" | cut -d' ' -f1)"
|
|
||||||
|
|
||||||
# Create resolv.conf
|
|
||||||
echo "nameserver 8.8.8.8" > "$rootfs/etc/resolv.conf"
|
|
||||||
|
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user