[英]Compiling go library without GCO to run on alpine, error in libczmq
When trying to run my binary on alpine
, I got the error:尝试在
alpine
上运行我的二进制文件时,出现错误:
... binary not found
which typically happens when there's a problem with architecture, or as I found, glibc
.这通常发生在架构出现问题时,或者我发现的
glibc
。 I searched and discovered that alpine
instead uses muslc
, an alternative C
library.我搜索并发现
alpine
使用muslc
,一个替代的C
库。 I then found this Installed Go binary not found in path on Alpine Linux Docker that teaches how to compile without CGO
, which is the thing that permits loading C
libraries from go:然后我发现在 Alpine Linux Docker 的路径中找不到这个Installed Go 二进制文件,它教了如何在没有
CGO
情况下进行编译,这是允许从 go 加载C
库的东西:
GOOS=linux GOARCH=amd64 CGO_ENABLED=0 go build -o [name of binary]
When I run this, I get:当我运行这个时,我得到:
go build gopkg.in/zeromq/goczmq.v4: no buildable Go source files in /home/lucas/Go/src/gopkg.in/zeromq/goczmq.v4
I suspect that it's because libczmq
is just a wrapper for the C
written libzmq
.我怀疑这是因为
libczmq
只是C
编写的libzmq
的包装器。 In this case, how do I compile in a way that I can use libczmq
?在这种情况下,如何以可以使用
libczmq
的方式进行编译? Why exactly CGO
must be disabled in alpine?为什么必须在高山中禁用
CGO
?
What's exactly CGO
? CGO
到底是什么? Shouldn't it use libc
when available but muslc
when not?它不应该在可用时使用
libc
在不可用时使用muslc
吗? I'd like to know more about what's happening in the background.我想更多地了解背景中发生的事情。
Note: I'm compiling outside alpine, in ubuntu, is that a problem?注意:我在 alpine 之外编译,在 ubuntu 中,这是一个问题吗?
I know this question is about three years old now but there is nothing on the Internet about this and I was facing the exact same problem and it took about two days before I finally found a proper solution.我知道这个问题现在大约三年了,但互联网上没有关于这个的任何内容,我面临着完全相同的问题,我花了大约两天的时间才最终找到了合适的解决方案。
In short, I had a Go project that uses goczmq
that I wanted to compile to a complete binary to put it into a FROM scratch
Docker container (although in this case, an alpine container would work just as well).简而言之,我有一个使用
goczmq
的 Go 项目,我想将其编译为一个完整的二进制文件,然后将其放入一个FROM scratch
Docker 容器(尽管在这种情况下,一个 alpine 容器也能正常工作)。 On the Internet, people now tend to tell you to set CGO_ENABLED=0
and everything will work fine, which is true.在互联网上,人们现在倾向于告诉你设置
CGO_ENABLED=0
一切都会正常工作,这是真的。 This tells Go to not use CGO, which allows you to use C libraries in your Go code, but needs those C libraries to be available on the system when you run your code.这告诉 Go 不要使用 CGO,这允许您在 Go 代码中使用 C 库,但在运行代码时需要这些 C 库在系统上可用。 As you have figured out, alpine does not have those C libraries (or, well, it has different ones in
muslc
instead of glibc
).正如您所发现的,alpine 没有那些 C 库(或者,它在
muslc
而不是glibc
有不同的muslc
)。
However, in our case, this is not helpful.但是,在我们的情况下,这没有帮助。 We want our Go code to be able to use existing C libraries because we use
goczmq
, which, as you have identified, is a Go wrapper for czmq
which itself is a C wrapper for libzmq
(which is written in C++, which makes our life even harder).我们希望我们的Go代码,以便能够使用现有的C库,因为我们使用
goczmq
,正如你已经确定,是一个围棋包装为czmq
这本身就是一个C包装libzmq
(这是用C ++编写,这使我们的生活更难)。
I solved this problem by using a static binary.我通过使用静态二进制文件解决了这个问题。 Instead of linking to whatever C libraries are available on the target system (in this case
muslc
on alpine) dynamically, I compile my code AND all the libraries ( muslc
, czmq
and libzmq
) into one unified binary.我没有动态链接到目标系统上可用的任何 C 库(在这种情况下是
muslc
在 alpine 上),而是将我的代码和所有库( muslc
、 czmq
和libzmq
) libzmq
成一个统一的二进制文件。
I'm using Docker multi-stage builds based on alpine here but in theory you could also do this directly on your computer.我在这里使用基于 alpine 的Docker 多阶段构建,但理论上您也可以直接在您的计算机上执行此操作。
# stage 1: build the binary
# we are using alpine Linux with the latest version of golang
FROM golang:1.13-alpine as golang
# first install some dependencies
# (we are using the static versions for each for them as we want these libraries included statically, not dynamically!)
# czmq requires libzmq which in turn requires libsodium
# on alpine Linux we also need to install some specific tools to build C and C++ programs
# libsodium also requires libuuid, which is included in util-linux-dev
RUN apk add --no-cache libzmq-static czmq-dev libsodium-static build-base util-linux-dev
# now we do the magic command mentioned here
# https://stackoverflow.com/questions/34729748/installed-go-binary-not-found-in-path-on-alpine-linux-docker?noredirect=1&lq=1
# this fools the C compiler into thinking we have glibc installed while we are actually using musl
# since we are compiling statically, it makes sense to use musl as it is smaller
# (and it uses the more permissive MIT license if you want to distribute your binary in some form, but check your other libraries before!)
RUN mkdir /lib64 && ln -s /lib/libc.musl-x86_64.so.1 /lib64/ld-linux-x86-64.so.2
# create your project directory for the Go project
WORKDIR /go/src/github.com/<username>/<projectname>/
# copy in all your Go files, assuming these are in the same directory as your Dockerfile
COPY . .
# here is the first hack: we need to tell CGO to use g++ instead of gcc or else it will struggle with libzmq, which is written in C++
# creating and empty C++ file actually works for this
RUN touch ./dummy.cc
# now run go install (go build could also work here but your binary would end up in a different directory)
# the -ldflags will be passed along the CGO toolchain
# -extldflags is especially important here, it has two important flags that we need:
# -static tells the compiler to use static linking, which does the actual magic of putting everything into one binary
# -luuid is needed to correctly find the uuid library that czmq uses
RUN go install -a -ldflags '-linkmode external -w -s -extldflags "-static -luuid" ' .
# stage 2: here is your actual image that will later run on your Docker host
# you can also use alpine here if you so choose
FROM scratch
# now we just copy over the completed binary from the old builder image
COPY --from=golang /go/bin/<projectname> bin
# and we start our program
ENTRYPOINT ["./bin"]
Now this almost works!现在这几乎有效! The only thing left is to add this statement to the beginning of your
main.go
file or else CGO is confused about what you are doing:唯一剩下的就是将此语句添加到
main.go
文件的开头,否则 CGO 对您在做什么感到困惑:
import "C"
This is important even if you don't directly use CGO in your program.即使您不在程序中直接使用 CGO,这也很重要。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.