fix(single-binary): bundle ld.so (#2602)

* debug

* fix copy command/silly muscle memory

Signed-off-by: Ettore Di Giacinto <mudler@localai.io>

* remove tmate

* Debugging

* Start binary with ld.so if present in libdir

Signed-off-by: Ettore Di Giacinto <mudler@localai.io>

* small refactor

Signed-off-by: Ettore Di Giacinto <mudler@localai.io>

---------

Signed-off-by: Ettore Di Giacinto <mudler@localai.io>
This commit is contained in:
Ettore Di Giacinto 2024-06-18 22:43:43 +02:00 committed by GitHub
parent 06de542032
commit 89a11e15e7
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
6 changed files with 58 additions and 11 deletions

View file

@ -5,11 +5,20 @@ import (
"os"
"path/filepath"
"runtime"
"github.com/rs/zerolog/log"
)
/*
This file contains functions to load libraries from the asset directory to keep the business logic clean.
*/
// skipLibraryPath checks if LOCALAI_SKIP_LIBRARY_PATH is set
var skipLibraryPath = os.Getenv("LOCALAI_SKIP_LIBRARY_PATH") != ""
// LoadExtractedLibs loads the extracted libraries from the asset dir
func LoadExtractedLibs(dir string) {
// Skip this if LOCALAI_SKIP_LIBRARY_PATH is set
if os.Getenv("LOCALAI_SKIP_LIBRARY_PATH") != "" {
if skipLibraryPath {
return
}
@ -18,9 +27,38 @@ func LoadExtractedLibs(dir string) {
}
}
// LoadLDSO checks if there is a ld.so in the asset dir and if so, prefixes the grpc process with it.
// In linux, if we find a ld.so in the asset dir we prefix it to run with the libs exposed in
// LD_LIBRARY_PATH for more compatibility
// If we don't do this, we might run into stack smash
// See also: https://stackoverflow.com/questions/847179/multiple-glibc-libraries-on-a-single-host/851229#851229
// In this case, we expect a ld.so in the lib asset dir.
// If that's present, we use it to run the grpc backends as supposedly built against
// that specific version of ld.so
func LoadLDSO(assetDir string, args []string, grpcProcess string) ([]string, string) {
if skipLibraryPath {
return args, grpcProcess
}
if runtime.GOOS != "linux" {
return args, grpcProcess
}
// Check if there is a ld.so file in the assetDir, if it does, we need to run the grpc process with it
ldPath := filepath.Join(assetDir, "backend-assets", "lib", "ld.so")
if _, err := os.Stat(ldPath); err == nil {
log.Debug().Msgf("ld.so found")
// We need to run the grpc process with the ld.so
args = append(args, grpcProcess)
grpcProcess = ldPath
}
return args, grpcProcess
}
// LoadExternal sets the LD_LIBRARY_PATH to include the given directory
func LoadExternal(dir string) {
// Skip this if LOCALAI_SKIP_LIBRARY_PATH is set
if os.Getenv("LOCALAI_SKIP_LIBRARY_PATH") != "" {
if skipLibraryPath {
return
}

View file

@ -11,6 +11,7 @@ import (
"time"
grpc "github.com/go-skynet/LocalAI/pkg/grpc"
"github.com/go-skynet/LocalAI/pkg/library"
"github.com/go-skynet/LocalAI/pkg/xsysinfo"
"github.com/klauspost/cpuid/v2"
"github.com/phayes/freeport"
@ -326,8 +327,13 @@ func (ml *ModelLoader) grpcModel(backend string, o *Options) func(string, string
return "", fmt.Errorf("failed allocating free ports: %s", err.Error())
}
// Make sure the process is executable
if err := ml.startProcess(grpcProcess, o.model, serverAddress); err != nil {
args := []string{}
// Load the ld.so if it exists
args, grpcProcess = library.LoadLDSO(o.assetDir, args, grpcProcess)
// Make sure the process is executable in any circumstance
if err := ml.startProcess(grpcProcess, o.model, serverAddress, args...); err != nil {
return "", err
}

View file

@ -69,7 +69,7 @@ func (ml *ModelLoader) GetGRPCPID(id string) (int, error) {
return strconv.Atoi(p.PID)
}
func (ml *ModelLoader) startProcess(grpcProcess, id string, serverAddress string) error {
func (ml *ModelLoader) startProcess(grpcProcess, id string, serverAddress string, args ...string) error {
// Make sure the process is executable
if err := os.Chmod(grpcProcess, 0700); err != nil {
return err
@ -82,7 +82,7 @@ func (ml *ModelLoader) startProcess(grpcProcess, id string, serverAddress string
grpcControlProcess := process.New(
process.WithTemporaryStateDir(),
process.WithName(grpcProcess),
process.WithArgs("--addr", serverAddress),
process.WithArgs(append(args, []string{"--addr", serverAddress}...)...),
process.WithEnvironment(os.Environ()...),
)