Étant donné un binaire, compilé avec Go en utilisant GOOS=linux
et GOARCH=AMD64
, déployé dans un conteneur docker
basé sur Alpine:3.3
, le binaire ne fonctionnera pas si l'hôte du moteur docker est Ubuntu (15.10):
sh: /bin/artisan: not found
Ce même binaire (compilé pour le même OS et Arch) fonctionnera très bien si l'hôte du moteur docker est busybox
(qui est le base pour Alpine
) déployée dans une VirtualBox VM sur Mac OS X.
Ce même binaire fonctionnera également parfaitement si le conteneur est basé sur l'une des images Ubuntu.
Une idée de ce qui manque à ce binaire?
Voici ce que j'ai fait pour reproduire (exécution réussie dans VirtualBox/busybox sur OS X non illustré):
Build (construction explicite avec des drapeaux même si l'Arch correspond):
➜ artisan git:(master) ✗ GOOS=linux GOARCH=AMD64 go build
Vérifiez qu'il peut fonctionner sur l'hôte:
➜ artisan git:(master) ✗ ./artisan
10:14:04.925 [ERROR] artisan: need a command, one of server, provision or build
Copier dans le répertoire docker, construire, exécuter:
➜ 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
Modification de la base de l'image en 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
Par défaut, si vous utilisez le package net
, une génération produira probablement un binaire avec une liaison dynamique, par exemple à libc. Vous pouvez inspecter dynamiquement ou statiquement le lien en affichant le résultat de ldd output.bin
Il y a deux solutions que j'ai rencontrées:
CGO_ENABLED=0
go build -tags netgo -a -v
, ceci est implémenté pour certaines plateformesDe https://golang.org/doc/go1.2 :
Le package net nécessite cgo par défaut car le système d'exploitation hôte doit en général arbitrer la configuration des appels réseau. Sur certains systèmes, cependant, il est possible d'utiliser le réseau sans cgo, et utile de le faire, par exemple pour éviter la liaison dynamique. La nouvelle balise de construction netgo (désactivée par défaut) permet la construction d'un package net en pure Go sur les systèmes où cela est possible.
Ce qui précède suppose que la seule dépendance CGO est le package net
de la bibliothèque standard.
J'ai eu le même problème avec un binaire go, et je l'ai fait fonctionner après avoir ajouté cela à mon fichier docker:
RUN apk add --no-cache \ libc6-compat
Le compilateur Go de votre machine de génération relie probablement votre binaire à des bibliothèques situées à des emplacements différents de ceux d'Alpine. Dans mon cas, il a été compilé avec des dépendances sous/lib64 mais Alpine n'utilise pas ce dossier.
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"]
Je travaille sur un article sur ce problème. Vous pouvez trouver un brouillon avec cette solution ici http://kefblog.com/2017-07-04/Golang-ang-docker .