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
|
||||
|
||||
PKG_NAME:=secubox-app-localai
|
||||
PKG_RELEASE:=11
|
||||
PKG_RELEASE:=12
|
||||
PKG_VERSION:=0.1.0
|
||||
PKG_ARCH:=all
|
||||
PKG_MAINTAINER:=CyberMind Studio <contact@cybermind.fr>
|
||||
|
||||
@ -33,8 +33,8 @@ Service Control:
|
||||
Runtimes:
|
||||
lxc - LXC container with rootfs extracted from Docker image
|
||||
(includes all backends: llama-cpp, whisper, etc.)
|
||||
Falls back to standalone binary if no docker/podman available
|
||||
docker - Run Docker container directly
|
||||
Downloads via Docker Registry API - no daemon required!
|
||||
docker - Run Docker container directly (requires dockerd)
|
||||
podman - Run Podman container directly (rootless)
|
||||
|
||||
Configuration: /etc/config/localai
|
||||
@ -185,21 +185,8 @@ lxc_install() {
|
||||
local rootfs="$lxc_path/$CONTAINER_NAME/rootfs"
|
||||
local config="$lxc_path/$CONTAINER_NAME/config"
|
||||
|
||||
# Check if we can extract from Docker image (preferred - includes all backends)
|
||||
local use_docker_extract=0
|
||||
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
|
||||
# Extract Docker image via Registry API (no daemon needed)
|
||||
lxc_create_docker_rootfs "$rootfs" || return 1
|
||||
|
||||
# Create LXC config
|
||||
lxc_create_config "$config" "$rootfs"
|
||||
@ -209,121 +196,79 @@ lxc_install() {
|
||||
return 0
|
||||
}
|
||||
|
||||
# Check if container runtime daemon is actually working
|
||||
runtime_is_working() {
|
||||
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() {
|
||||
# Extract Docker image via Registry API (no daemon required!)
|
||||
lxc_create_docker_rootfs() {
|
||||
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
|
||||
if command -v podman >/dev/null 2>&1 && runtime_is_working podman; then
|
||||
rt="podman"
|
||||
elif command -v docker >/dev/null 2>&1 && runtime_is_working docker; then
|
||||
rt="docker"
|
||||
else
|
||||
log_error "Need working podman or docker to extract image"
|
||||
log_error "Docker installed but daemon not running? Start with: /etc/init.d/dockerd start"
|
||||
return 1
|
||||
fi
|
||||
# Detect architecture for Docker manifest
|
||||
case "$(uname -m)" in
|
||||
x86_64) arch="amd64" ;;
|
||||
aarch64) arch="arm64" ;;
|
||||
armv7l) arch="arm" ;;
|
||||
*) arch="amd64" ;;
|
||||
esac
|
||||
|
||||
log_info "Extracting LocalAI rootfs from Docker image..."
|
||||
log_info "Image: $docker_image"
|
||||
log_info "Extracting LocalAI Docker image ($arch)..."
|
||||
log_info "Image: $image:$tag"
|
||||
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"
|
||||
log_info "Exporting rootfs (2-4GB, please wait)..."
|
||||
|
||||
if $rt export "$temp_container" | tar -xf - -C "$rootfs" 2>/dev/null; then
|
||||
log_info "Rootfs extracted successfully"
|
||||
else
|
||||
log_error "Failed to extract rootfs"
|
||||
$rt rm -f "$temp_container" >/dev/null 2>&1
|
||||
return 1
|
||||
fi
|
||||
# Get Docker Hub token
|
||||
log_info "Authenticating with Docker Hub..."
|
||||
local token=$(wget -q -O - "https://auth.docker.io/token?service=registry.docker.io&scope=repository:$image:pull" | jsonfilter -e '@.token')
|
||||
[ -z "$token" ] && { log_error "Failed to get Docker Hub token"; return 1; }
|
||||
|
||||
# Cleanup temp container
|
||||
$rt rm -f "$temp_container" >/dev/null 2>&1
|
||||
# Get manifest list (multi-arch)
|
||||
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
|
||||
# $rt rmi "$docker_image" >/dev/null 2>&1
|
||||
# Find digest for our architecture
|
||||
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"
|
||||
|
||||
# Setup resolv.conf
|
||||
echo "nameserver 8.8.8.8" > "$rootfs/etc/resolv.conf"
|
||||
# Ensure /bin/sh exists
|
||||
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)
|
||||
log_info "Rootfs size: $rootfs_size"
|
||||
log_info "All LocalAI backends are now available!"
|
||||
|
||||
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"
|
||||
log_info "LocalAI Docker image extracted successfully"
|
||||
log_info "All backends available: llama-cpp, whisper, stablediffusion, etc."
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user