Двоичный файл, скомпилированный Go, не будет работать в контейнере alpine docker на хосте Ubuntu

При наличии двоичного файла, скомпилированного с помощью Go с использованием GOOS=linux и GOARCH=amd64, развернутого в контейнере docker на основе alpine:3.3, двоичный файл не будет работать, если хостом механизма докера является Ubuntu (15.10):

sh: /bin/artisan: not found

Этот же двоичный файл (скомпилированный для той же ОС и той же архитектуры) будет отлично работать, если узел механизма докеров — busybox (который является базовым для alpine), развернутый в виртуальной машине VirtualBox в Mac OS X.

Этот же двоичный файл также будет отлично работать, если контейнер основан на одном из образов Ubuntu.

Есть идеи, чего не хватает этому двоичному файлу?

Это то, что я сделал для воспроизведения (успешный запуск в VirtualBox/busybox на OS X не показан):

Сборка (сборка явно с флагами, даже если арка совпадает):

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

Проверьте, может ли он работать на хосте:

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

Скопируйте в папку докера, соберите, запустите:

➜  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

Теперь меняем базу изображения на 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 

person Oleg Sklyar    schedule 29.03.2016    source источник
comment
Помогает ли добавление CGO_ENABLED=0?   -  person Martin Gallagher    schedule 29.03.2016
comment
Магия, да. Не могли бы вы уточнить в ответе, и я приму.   -  person Oleg Sklyar    schedule 29.03.2016
comment
Не могли бы вы попробовать go build -tags netgo -a -v std с CGO_ENABLED=1? Я думаю, что это могут быть проблемы с сетевым пакетом, вызывающим динамическое связывание.   -  person Martin Gallagher    schedule 29.03.2016
comment
Как вы предположили, это помогло CGO_ENABLED=1 go build -tags netgo -a -v. Если я правильно понимаю, в отличие от CGO_ENABLED=0, это также сохранит функциональность TLS в сетевом пакете, которая в противном случае была бы потеряна, верно? Можно ли как-то посмотреть, что скомпоновано в бинарник статически, а что осталось для динамического линкования?   -  person Oleg Sklyar    schedule 29.03.2016
comment
По умолчанию CGO может использоваться для сетевого пакета - использование вышеуказанного тега или CGO_ENABLED=0 заставляет использовать реализацию Go std для поиска - что вы можете сделать, это сделать: ldd output.bin для каждого варианта сборки, чтобы увидеть, действительно ли они статически скомпилирован или если происходит какое-либо динамическое связывание.   -  person Martin Gallagher    schedule 29.03.2016
comment
Круто, большое спасибо за помощь и объяснение!   -  person Oleg Sklyar    schedule 29.03.2016
comment
Когда вы используете CGO, бинарный файл связывается с glibc, но alpine использует musl libc. (также пакет tls не использует cgo, кроме как для получения корневых сертификатов на darwin)   -  person JimB    schedule 29.03.2016
comment
Это похоже на проблему, которую я имел и решил здесь: stackoverflow.com/questions/34729748/   -  person sheldonk    schedule 30.03.2016
comment
@sheldonk Спасибо за совет. Я попробую, хотя я предпочитаю связывать все статически.   -  person Oleg Sklyar    schedule 30.03.2016
comment
Есть также некоторые базовые образы alpine glibc, которые вы можете попробовать, и они должны решить вашу проблему.   -  person sheldonk    schedule 30.03.2016


Ответы (6)


По умолчанию при использовании пакета net сборка, скорее всего, создаст двоичный файл с некоторой динамической компоновкой, например. к libc. Вы можете проверить динамическую или статическую ссылку, просмотрев результат ldd output.bin

Есть два решения, с которыми я столкнулся:

  • Отключить CGO через CGO_ENABLED=0
  • Принудительно использовать реализацию сетевых зависимостей Go, netgo через go build -tags netgo -a -v, это реализовано для определенных платформ

Из https://golang.org/doc/go1.2:

Пакет net по умолчанию требует cgo, потому что операционная система хоста должна в целом выполнять настройку сетевых вызовов. Однако в некоторых системах можно использовать сеть без cgo, и это полезно, например, чтобы избежать динамического связывания. Новый тег сборки netgo (по умолчанию отключен) позволяет создавать пакеты net на чистом Go в тех системах, где это возможно.

Вышеприведенное предполагает, что единственной зависимостью CGO является пакет net стандартной библиотеки.

person Martin Gallagher    schedule 30.03.2016
comment
CGO_ENABLED=0 также устранил мою проблему: попытка запустить программу go, созданную alpine, на докере, отличном от alpine. Ошибка в моем случае просто сказала docker: Error response from daemon: Container command not found or does not exist.. - person Vlad A. Ionescu; 21.04.2016
comment
Для тех, кто использует Bazel, вышеперечисленное можно выполнить с помощью флагов --features=static --features=pure. - person Ben Elgar; 16.04.2018
comment
Спасибо за этот ответ, пришлось много гуглить, чтобы найти. - person Kyle Gibbons; 25.11.2018
comment
Я думаю, что еще одним необязательным решением является копирование исходного кода в образ и запуск exec в образе. - person g10guang; 19.04.2019
comment
CGO_ENABLED=0 должно решить проблему. Это условие тоже важно, GOARCH=amd64 - person Naren Yellavula; 29.12.2019
comment
Было бы здорово, если бы этот ответ также включал возможные проблемы, которые могут вызвать два решения. Гарантируется ли безопасность любого из них? Или они могут вызвать другие проблемы во время выполнения, когда программа пытается выполнить какой-то связанный код? Я создаю чужой проект как компонент своего кода, поэтому у меня нет достаточного понимания связанного кода, который мог бы полагаться на CGO. - person Stephen Smith; 11.09.2020

У меня была такая же проблема с бинарным файлом go, и я заставил его работать после добавления этого в мой файл докера:

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

person zakaria amine    schedule 14.06.2018
comment
помогите мне запустить CGO в образе Alpine - person xsor; 27.09.2018
comment
Сэкономил много времени. +1 - person Manwal; 31.05.2019
comment
К сожалению, у меня это не работает и дает error relocating ...: fprintf chk: symbol not found - person TheDiveO; 01.06.2019
comment
вам нужно построить и запустить в той же среде. например, если вы создадите двоичный файл на своем локальном компьютере и запустите его в докере, это может вызвать проблему. - person zakaria amine; 01.06.2019
comment
Я запускаю чужой двоичный файл go в док-контейнере Alpine и не хочу перекомпилировать его самостоятельно. Я бы предпочел использовать общедоступный двоичный файл. Это решение устраняет проблему и позволяет запускать бинарный файл go в контейнере Docker Alpine. - person Brandon; 26.07.2019

Компилятор Go с вашей сборочной машины, вероятно, связывает ваш двоичный файл с библиотеками в другом месте, чем в Alpine. В моем случае он был скомпилирован с зависимостями в /lib64, но Alpine не использует эту папку.

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

Я работаю над статьей об этой проблеме. Вы можете найти проект с этим решением здесь http://kefblog.com/2017-07-04/Golang-ang-docker .

person Norbert Szydlik    schedule 04.07.2017

Что мне помогло, так это включение статической ссылки в параметрах компоновщика:

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

Параметр -linkmode указывает Go использовать внешний компоновщик, параметр -extldflags устанавливает параметры для передачи компоновщику, а флаг -w отключает отладочную информацию DWARF для улучшения размера двоичного файла.

См. go tool link и статически скомпилированные программы Go, всегда, даже с cgo, с использованием musl Больше подробностей

person msiemens    schedule 01.10.2019
comment
Однако это работает, возможно, стоит отметить, что это также выдает предупреждение. =› warning: Using 'getaddrinfo' in statically linked applications requires at runtime the shared libraries from the glibc version used for linking Думаю, в случае с альпийским базовым изображением все в порядке. - person The Fool; 28.06.2021

При выполнении бинарного файла go внутри контейнера докеров Debian столкнулся с этой проблемой: /bin/bash: line 10: /my/go/binary: No such file or directory

Бинарный файл был собран с помощью docker-in-docker (dind) из контейнера alpine с помощью команды: GOOS=linux GOARCH=amd64 go build

Исправлено это с помощью следующей среды при сборке бинарника: CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build

person varunrocks6    schedule 28.10.2020

У меня было приложение, которое требовало CGO_ENABLED=1.

Исправление для запуска скомпилированного двоичного файла go в контейнере debian-slim состояло в том, чтобы собрать двоичный файл с использованием RUN GOOS=linux GOARCH=amd64 CGO_ENABLED=1 go build -o goapp

И выполните следующие команды в Debian Slim

RUN apt-get update && apt-get install -y musl-dev
RUN ln -s /usr/lib/x86_64-linux-musl/libc.so /lib/libc.musl-x86_64.so.1

После этого я смог запустить goapp

СОВЕТ: ldd goapp показал, что в контейнере отсутствует libc.musl-x86_64.

person sirius    schedule 12.02.2021