Go-kompilierte Binärdateien werden nicht in einem alpinen Docker-Container auf dem Ubuntu-Host ausgeführt

78

Bei einer Binärdatei, die mit Go unter Verwendung von kompiliert GOOS=linuxund GOARCH=amd64in einem dockerContainer bereitgestellt wurde alpine:3.3, wird die Binärdatei nicht ausgeführt, wenn der Docker-Engine-Host Ubuntu (15.10) ist:

sh: /bin/artisan: not found

Dieselbe Binärdatei (kompiliert für dasselbe Betriebssystem und denselben Arch) läuft einwandfrei, wenn der Docker-Engine-Host busybox(der die Basis dafür ist alpine) in einer VirtualBox-VM unter Mac OS X bereitgestellt wird.

Dieselbe Binärdatei läuft auch einwandfrei, wenn der Container auf einem der Ubuntu-Images basiert.

Irgendeine Idee, was diese Binärdatei fehlt?

Folgendes habe ich getan, um zu reproduzieren (erfolgreiche Ausführung in VirtualBox / Busybox unter OS X nicht gezeigt):

Erstellen (explizit mit Flaggen erstellen, obwohl der Bogen übereinstimmt):

➜  artisan git:(master) ✗ GOOS=linux GOARCH=amd64 go build

Überprüfen Sie, ob es auf dem Host ausgeführt werden kann:

➜  artisan git:(master) ✗ ./artisan 
10:14:04.925 [ERROR] artisan: need a command, one of server, provision or build 

In Docker-Verzeichnis kopieren, erstellen, ausführen:

➜  artisan git:(master) ✗ cp artisan docker/build/bin/        
➜  artisan git:(master) ✗ cd docker 
➜  docker git:(master) ✗ cat Dockerfile 
FROM docker:1.10
COPY build/ /
➜  docker git:(master) ✗ docker build -t artisan .
Sending build context to Docker daemon 10.15 MB
Step 1 : FROM docker:1.10
...
➜  docker git:(master) ✗ docker run -it artisan sh
/ # /bin/artisan 
sh: /bin/artisan: not found

Ändern Sie nun die Bildbasis in phusion/baseimage:

➜  docker git:(master) ✗ cat Dockerfile 
#FROM docker:1.10
FROM phusion/baseimage
COPY build/ /
➜  docker git:(master) ✗ docker build -t artisan .
Sending build context to Docker daemon 10.15 MB
Step 1 : FROM phusion/baseimage
...
➜  docker git:(master) ✗ docker run -it artisan sh
# /bin/artisan
08:16:39.424 [ERROR] artisan: need a command, one of server, provision or build 
Oleg Sklyar
quelle
4
Hilft das Hinzufügen von CGO_ENABLED = 0?
Martin Gallagher
Magie tut es. Könnten Sie bitte eine Antwort ausarbeiten und ich werde akzeptieren.
Oleg Sklyar
Könnten Sie es bitte go build -tags netgo -a -v stdmit CGO_ENABLED = 1 versuchen ? Ich denke, es könnten Probleme mit dem Netzpaket sein, die eine dynamische Verknüpfung verursachen.
Martin Gallagher
Wie Sie vorgeschlagen haben, hat dies geholfen CGO_ENABLED=1 go build -tags netgo -a -v. Wenn ich im Gegensatz zu CGO_ENABLED = 0 richtig verstehe, bleibt auch die TLS-Funktionalität im Netzpaket erhalten, die sonst verloren gehen würde, stimmt das? Kann ich irgendwie sehen, was statisch mit der Binärdatei verknüpft ist und was für die dynamische Verknüpfung übrig bleibt?
Oleg Sklyar
2
Standardmäßig kann CGO für das Netzpaket verwendet werden. ldd output.binWenn Sie das obige Tag verwenden oder CGO_ENABLED = 0 erzwingt, dass die Go-Standard-Implementierung für Suchvorgänge verwendet wird, können Sie Folgendes tun: Bei jeder Build-Variante, um festzustellen, ob sie wirklich statisch kompiliert sind oder wenn es eine dynamische Verknüpfung gibt.
Martin Gallagher

Antworten:

92

Standardmäßig erzeugt netein Build bei Verwendung des Pakets wahrscheinlich eine Binärdatei mit einer dynamischen Verknüpfung, z. B. zu libc. Sie können die Verknüpfung dynamisch oder statisch überprüfen, indem Sie das Ergebnis von anzeigenldd output.bin

Ich bin auf zwei Lösungen gestoßen:

  • Deaktivieren Sie CGO über CGO_ENABLED=0
  • Erzwingen Sie die Verwendung der Go-Implementierung von Netzabhängigkeiten, netgo via go build -tags netgo -a -v, dies ist für eine bestimmte Plattform implementiert

Von https://golang.org/doc/go1.2 :

Das Netzpaket erfordert standardmäßig cgo, da das Host-Betriebssystem im Allgemeinen die Einrichtung von Netzwerkanrufen vermitteln muss. Auf einigen Systemen ist es jedoch möglich, das Netzwerk ohne cgo zu verwenden, und dies ist nützlich, um beispielsweise eine dynamische Verknüpfung zu vermeiden. Das neue Build-Tag netgo (standardmäßig deaktiviert) ermöglicht die Erstellung eines Netzpakets in pure Go auf den Systemen, auf denen dies möglich ist.

Das Obige setzt voraus, dass die einzige CGO-Abhängigkeit das netPaket der Standardbibliothek ist .

Martin Gallagher
quelle
3
CGO_ENABLED=0Außerdem wurde mein Problem behoben: Der Versuch, ein von Alpen erstelltes Go-Programm auf einem nicht-alpinen Docker auszuführen. Der Fehler in meinem Fall sagte geradedocker: Error response from daemon: Container command not found or does not exist..
Vlad A. Ionescu
Für jeden, der Bazel verwendet, kann das oben Genannte mit den --features=static --features=pureFlags erreicht werden.
Ben Elgar
Vielen Dank für diese Antwort, es hat viel gegoogelt, um sie zu finden.
Kyle Gibbons
Ich denke, eine andere optionale Lösung ist das Kopieren von Quellcode in Image und Exec Go Build in Image.
G10guang
CGO_ENABLED = 0 sollte das Problem beheben. Diese Bedingung ist auch wichtig, GOARCH = amd64
Naren Yellavula
64

Ich hatte das gleiche Problem mit einer Go-Binärdatei und habe sie zum Laufen gebracht, nachdem ich sie meiner Docker-Datei hinzugefügt habe:

RUN apk add --no-cache \ libc6-compat

Zakaria-Amin
quelle
1
hilf mir, CGO in meinem
Alpenbild auszuführen
Viel Zeit gespart. +1
Manwal
1
Leider funktioniert das bei mir nicht und gibterror relocating ...: fprintf chk: symbol not found
TheDiveO
1
Sie müssen in derselben Umgebung erstellen und ausführen. Wenn Sie beispielsweise die Binärdatei auf Ihrem lokalen Computer erstellen und im Docker ausführen, kann dies das Problem verursachen
Zakaria Amine
1
Ich verwende die Go-Binärdatei eines anderen Benutzers in einem alpinen Docker-Container und möchte sie nicht selbst neu kompilieren. Ich würde lieber die öffentlich verfügbare Binärdatei verwenden. Diese Lösung behebt das Problem und lässt mich die Go-Binärdatei in einem Alpine Docker-Container ausführen
Brandon
9

Der Go-Compiler von Ihrem Build-Computer aus verknüpft Ihre Binärdatei wahrscheinlich mit Bibliotheken an einem anderen Speicherort als in Alpine. In meinem Fall wurde es mit Abhängigkeiten unter / lib64 kompiliert, aber Alpine verwendet diesen Ordner nicht.

FROM alpine:edge AS build
RUN apk update
RUN apk upgrade
RUN apk add --update go=1.8.3-r0 gcc=6.3.0-r4 g++=6.3.0-r4
WORKDIR /app
ENV GOPATH /app
ADD src /app/src
RUN go get server # server is name of our application
RUN CGO_ENABLED=1 GOOS=linux go install -a server

FROM alpine:edge
WORKDIR /app
RUN cd /app
COPY --from=build /app/bin/server /app/bin/server
CMD ["bin/server"]

Ich arbeite an einem Artikel zu diesem Thema. Den Entwurf mit dieser Lösung finden Sie hier http://kefblog.com/2017-07-04/Golang-ang-docker .

Norbert Szydlik
quelle
3

Was für mich der Trick war, war die Aktivierung der statischen Verknüpfung in den Linkeroptionen:

$ go build -ldflags '-linkmode external -w -extldflags "-static"'

Die -linkmodeOption weist Go an, den externen Linker zu verwenden, die -extldflagsOption legt Optionen fest, die an den Linker übergeben werden sollen, und das -wFlag deaktiviert DWARF-Debug-Informationen, um die Binärgröße zu verbessern.

Siehe go tool linkund Statisch Go Programme zusammengestellt, immer, auch mit CGO, mit MUSL für weitere Details

msiemens
quelle
0

Beim Ausführen einer Go-Binärdatei in einem Debian-Docker-Container trat dieses Problem auf: /bin/bash: line 10: /my/go/binary: No such file or directory

Die Binärdatei wurde mithilfe von Docker-in-Docker (dind) aus einem alpinen Container mit dem folgenden Befehl erstellt: GOOS=linux GOARCH=amd64 go build

Es wurde behoben, indem beim Erstellen der Binärdatei die folgende env verwendet wurde: CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build

varunrocks6
quelle