Скомпільований двійковий файл не працює в контейнері альпійського докера на хості Ubuntu


82

Враховуючи двійковий файл, скомпільований за допомогою 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 

4
Чи допомагає додавання CGO_ENABLED = 0?
Мартін Галлахер,

Магія, це робить. Не могли б Ви дати детальнішу відповідь, і я прийму.
Олег Скляр

Не могли б ви спробувати go build -tags netgo -a -v stdCGO_ENABLED = 1? Я думаю, що це можуть бути проблеми з мережевим пакетом, що спричиняють динамічне зв'язування.
Мартін Галлахер,

Як ви запропонували, це допомогло CGO_ENABLED=1 go build -tags netgo -a -v. Якщо я правильно розумію, на відміну від CGO_ENABLED = 0, це також збереже функціональність TLS в мережевому пакеті, яка в іншому випадку була б втрачена, чи не так? Чи можу я якось побачити, що статично пов’язано з двійковою системою, а що залишено для динамічного зв’язування?
Олег Скляр

2
За замовчуванням CGO може бути використаний для мережевого пакета - використання вищезазначеного тегу або CGO_ENABLED = 0 змушує використовувати реалізацію Go std для пошуку - що ви можете зробити, це зробити: ldd output.binу кожному варіанті збірки, щоб побачити, чи вони справді статично скомпільовані або якщо відбувається динамічне зв’язування.
Мартін Галлахер,

Відповіді:


94

За замовчуванням, якщо використовується netпакет, збірка, ймовірно, створить двійковий файл з деяким динамічним зв'язуванням, наприклад, до libc. Ви можете перевірити динамічне та статичне посилання, переглянувши результатldd output.bin

Є два рішення, з якими я зіткнувся:

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

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

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

Вищевикладене передбачає, що єдиною залежністю CGO є стандартний netпакет бібліотеки .


4
CGO_ENABLED=0також вирішив мою проблему: Спроба запустити альпійську програму go на неальпійському докері. Помилка в моєму випадку щойно сказалаdocker: Error response from daemon: Container command not found or does not exist..
Влад А. Іонеску

Для кожного, хто використовує Bazel, вищезазначене можна досягти за допомогою --features=static --features=pureпрапорів.
Бен Елгар,

Дякую за цю відповідь, знадобилося багато Google, щоб знайти.
Кайл Гіббонс,

Я думаю, що іншим необов’язковим рішенням є копіювання вихідного коду в image та exec go build in image.
g10guang

CGO_ENABLED = 0 має вирішити проблему. Ця умова також важлива, GOARCH = amd64
Нарен Єллавула,

65

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

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


1
допоможіть мені запустити CGO на моєму альпійському зображенні
xsor

Заощадили багато часу. +1
Манвал,

1
На жаль, це не працює для мене, і це даєerror relocating ...: fprintf chk: symbol not found
TheDiveO

1
вам потрібно будувати і запускати в одному середовищі. наприклад, якщо ви побудуєте двійковий файл на своїй локальній машині і запустите його всередині докера, це може спричинити проблему
zakaria amine

1
Я запускаю чужий двійковий файл go в контейнері док-станції Alpine, і я не хочу сам перекомпілювати його. Я волів би використовувати загальнодоступний двійковий файл. Це рішення виправляє проблему і дозволяє мені запускати бінарний файл go в контейнері
Брендон

9

Компілятор 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 .


4

Що для мене було фокусом, це ввімкнення статичного зв’язування в параметрах компонувальника:

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

-linkmodeОпція повідомляє Go використовувати зовнішній линкер, то -extldflagsпараметри опції встановлює для передачі линкера і -wпрапор забороняє DWARF налагоджувальної інформації для поліпшення довічного розміру.

Перегляньте go tool linkта статистично скомпільовані програми Go, завжди, навіть із cgo, використовуючи musl для отримання детальної інформації


0

Під час запуску бінарного файлу go всередині контейнера docker debian зіткнувся з цією проблемою: /bin/bash: line 10: /my/go/binary: No such file or directory

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

Виправлено за допомогою наступного env під час побудови двійкового файлу: CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.