Компиляция библиотеки go без GCO для работы на alpine, ошибка в libczmq

При попытке запустить мой бинарник на alpine я получил ошибку:

... binary not found

что обычно происходит, когда есть проблема с архитектурой или, как я обнаружил, glibc. Я поискал и обнаружил, что alpine вместо этого использует muslc, альтернативную библиотеку C. Затем я обнаружил этот установленный двоичный файл Go, не найденный в path в Alpine Linux Docker, который учит, как компилировать без CGO, в чем суть что позволяет загружать C библиотек из go:

GOOS=linux GOARCH=amd64 CGO_ENABLED=0 go build -o [name of binary]

Когда я запускаю это, я получаю:

go build gopkg.in/zeromq/goczmq.v4: no buildable Go source files in /home/lucas/Go/src/gopkg.in/zeromq/goczmq.v4

Я подозреваю, что это потому, что libczmq это просто оболочка для C написанного libzmq. В этом случае, как мне скомпилировать так, чтобы я мог использовать libczmq? Почему именно CGO нужно отключить в alpine?

Что именно CGO? Разве он не должен использовать libc, когда доступно, и muslc, когда нет? Я хотел бы узнать больше о том, что происходит на заднем плане.

Примечание. Я компилирую вне Alpine в Ubuntu, это проблема?


person Community    schedule 28.02.2017    source источник
comment
Пожалуйста, проясните, что вы пытаетесь сделать. У вас проблемы с запуском или компиляцией программы?   -  person Marco    schedule 28.02.2017
comment
@Марко, это в компиляции, извини. Первая строка была, когда я пытался запустить бинарный файл без флагов компиляции. Ошибка возникает, когда я пытаюсь скомпилировать Ubuntu с флагами   -  person    schedule 28.02.2017
comment
Вы не можете связать библиотеку C без CGO. Именно для этого и предназначен CGO.   -  person JimB    schedule 28.02.2017
comment
@JimB, но использует ли CGO libc исключительно для ссылок? Почему muslc не работает, если он совместим?   -  person    schedule 28.02.2017
comment
@GuerlandoOCs: я не понимаю вопроса. musl — это реализация libc, но вы отключаете cgo, поэтому не собираетесь его использовать. Я бы попытался собрать czmq и Go вместе с musl, чтобы убедиться, что все правильно компилируется и компонуется. Вероятно, вы можете просто использовать контейнер go alpine docker для этого.   -  person JimB    schedule 28.02.2017


Ответы (1)


Я знаю, что этому вопросу уже около трех лет, но в Интернете об этом ничего нет, и я столкнулся с точно такой же проблемой, и потребовалось около двух дней, прежде чем я наконец нашел правильное решение.

Короче говоря, у меня был проект Go, использующий goczmq, который я хотел скомпилировать в полный двоичный файл, чтобы поместить его в контейнер FROM scratch Docker (хотя в этом случае альпийский контейнер тоже подойдет). В Интернете люди сейчас склонны говорить вам, что вы должны установить CGO_ENABLED=0, и все будет работать нормально, и это правда. Это говорит Go не использовать CGO, что позволяет вам использовать библиотеки C в вашем коде Go, но требует, чтобы эти библиотеки C были доступны в системе при запуске вашего кода. Как вы уже поняли, у alpine нет этих библиотек C (или, ну, у них есть другие в muslc вместо glibc).

Однако в нашем случае это бесполезно. Мы хотим, чтобы наш код Go мог использовать существующие библиотеки C, потому что мы используем goczmq, который, как вы определили, является оболочкой Go для czmq, которая сама является оболочкой C для libzmq (которая написана на C++, что делает нашу жизнь Еще труднее).

Я решил эту проблему, используя статический двоичный файл. Вместо динамического связывания с любыми библиотеками C, доступными в целевой системе (в данном случае muslc в alpine), я компилирую свой код И все библиотеки (muslc, czmq и libzmq) в один унифицированный двоичный файл.

Я использую многоэтапные сборки Docker на основе alpine здесь но теоретически вы также можете сделать это прямо на своем компьютере.

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

Теперь это почти работает! Осталось только добавить этот оператор в начало вашего файла main.go, иначе CGO не поймет, что вы делаете:

import "C"

Это важно, даже если вы не используете CGO в своей программе напрямую.

person Tobias Pfandzelter    schedule 07.01.2020
comment
На случай, если это поможет кому-то еще, мне пришлось добавить -lstdc++ к аргументам -extldflags, чтобы заставить goczmq работать (поскольку ZMQ использует C++). Мне пришлось сделать это, даже несмотря на наличие файла dummy.cc. - person Travis DePrato; 26.03.2021