Go-compiled binary won't run in an alpine docker container on Ubuntu host


Go-compiled binary won't run in an alpine docker container on Ubuntu host



Given a binary, compiled with Go using GOOS=linux and GOARCH=amd64, deployed to a docker container based on alpine:3.3, the binary will not run if the docker engine host is Ubuntu (15.10):


GOOS=linux


GOARCH=amd64


docker


alpine:3.3


sh: /bin/artisan: not found



This same binary (compiled for the same OS and arch) will run just fine if the docker engine host is busybox (which is the base for alpine) deployed within a VirtualBox VM on Mac OS X.


busybox


alpine



This same binary will also run perfectly fine if the container is based on one of Ubuntu images.



Any idea what this binary is missing?



This is what I've done to reproduce (successful run in VirtualBox/busybox on OS X not shown):



Build (building explicitly with flags even though the arch matches):


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



Check it can run on the host:


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



Copy to docker dir, build, run:


➜ 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



Now changing the image base to phusion/baseimage:


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





Does adding CGO_ENABLED=0 help?
– Martin Gallagher
Mar 29 '16 at 8:42





Magic, it does. Could you please elaborate in an answer and I will accept.
– Oleg Sklyar
Mar 29 '16 at 8:44





Could you please try go build -tags netgo -a -v std with CGO_ENABLED=1? I think it could be issues with the net package, causing dynamic linking.
– Martin Gallagher
Mar 29 '16 at 8:57


go build -tags netgo -a -v std





As you suggested this helped CGO_ENABLED=1 go build -tags netgo -a -v. If I understand correctly in contrast to CGO_ENABLED=0 this will also preserve the TLS functionality in the net package, which otherwise would be lost, is that right? Can I see somehow what is linked into the binary statically and what is left for dynamic linking?
– Oleg Sklyar
Mar 29 '16 at 9:05



CGO_ENABLED=1 go build -tags netgo -a -v





By default CGO can be used for the net package - using the above tag or CGO_ENABLED=0 forces the Go std implementation for lookups to be used - what you can do is do: ldd output.bin on each build variant to see if they're truly statically compiled or if there's any dynamic linking going on.
– Martin Gallagher
Mar 29 '16 at 9:09



ldd output.bin




3 Answers
3



By default, if using the net package a build will likely produce a binary with some dynamic linking, e.g. to libc. You can inspect dynamically vs. statically link by viewing the result of ldd output.bin


net


ldd output.bin



There are two solutions I've come across:


CGO_ENABLED=0


go build -tags netgo -a -v



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



The net package requires cgo by default because the host operating system must in general mediate network call setup. On some systems, though, it is possible to use the network without cgo, and useful to do so, for instance to avoid dynamic linking. The new build tag netgo (off by default) allows the construction of a net package in pure Go on those systems where it is possible.



The above assumes that the only CGO dependency is the standard library's net package.


net





CGO_ENABLED=0 also fixed my problem: Trying to run alpine-built go program on non-alpine docker. The error in my case just said docker: Error response from daemon: Container command not found or does not exist..
– Vlad A Ionescu
Apr 21 '16 at 14:03


CGO_ENABLED=0


docker: Error response from daemon: Container command not found or does not exist..





For anyone using Bazel, the above can be accomplished with the --features=static --features=pure flags.
– Ben Elgar
Apr 16 at 13:51


--features=static --features=pure



Go compiler from your build machine probably links your binary with libraries on different location than in Alpine. In my case it was compiled with dependencies under /lib64 but Alpine does not use that folder.


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"]



I'm working on article about this issue. You can find draft with this solution here http://kefblog.com/2017-07-04/Golang-ang-docker .



I had the same issue with a go binary, and I got it to work after adding this to my docker file:




RUN apk add --no-cache
libc6-compat



RUN apk add --no-cache
libc6-compat






By clicking "Post Your Answer", you acknowledge that you have read our updated terms of service, privacy policy and cookie policy, and that your continued use of the website is subject to these policies.

Comments

Popular posts from this blog

paramiko-expect timeout is happening after executing the command

Export result set on Dbeaver to CSV

Opening a url is failing in Swift